├── pip_requirements.txt ├── MANIFEST.in ├── inmemorystorage ├── __init__.py └── storage.py ├── README.rst ├── setup.py ├── LICENSE └── tests.py /pip_requirements.txt: -------------------------------------------------------------------------------- 1 | django 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE tests.py 2 | -------------------------------------------------------------------------------- /inmemorystorage/__init__.py: -------------------------------------------------------------------------------- 1 | from .storage import InMemoryStorage 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | django-inmemorystorage 3 | ====================== 4 | 5 | A non-persistent in-memory data storage backend for Django. 6 | 7 | Compatible with Django's `storage API `_. 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from distutils.core import setup 3 | 4 | def read(fname): 5 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 6 | 7 | README = read('README.rst') 8 | 9 | setup( 10 | name = "django-inmemorystorage", 11 | version = "0.1.1", 12 | url = 'http://github.com/codysoyland/django-inmemorystorage', 13 | license = 'BSD', 14 | description = "A non-persistent in-memory data storage backend for Django.", 15 | long_description = README, 16 | author = 'Cody Soyland', 17 | author_email = 'cody@soyland.com', 18 | packages = [ 19 | 'inmemorystorage', 20 | ], 21 | classifiers = [ 22 | 'Development Status :: 4 - Beta', 23 | 'Framework :: Django', 24 | 'Intended Audience :: Developers', 25 | 'License :: OSI Approved :: BSD License', 26 | 'Operating System :: OS Independent', 27 | 'Programming Language :: Python', 28 | 'Topic :: Internet :: WWW/HTTP', 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Cody Soyland 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of django-inmemorystorage nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL CODY SOYLAND BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /inmemorystorage/storage.py: -------------------------------------------------------------------------------- 1 | from django.core.files.storage import Storage 2 | from django.core.files.base import ContentFile 3 | 4 | class PathDoesNotExist(Exception): 5 | pass 6 | 7 | class InMemoryNode(object): 8 | """ 9 | Base class for files and directories. 10 | """ 11 | parent = None 12 | 13 | def add_child(self, name, child): 14 | child.parent = self 15 | self.children[name] = child 16 | 17 | class InMemoryFile(InMemoryNode): 18 | """ 19 | Stores contents of file and stores reference to parent. 20 | """ 21 | def __init__(self, contents='', parent=None): 22 | self.contents = contents 23 | self.parent = parent 24 | 25 | class InMemoryDir(InMemoryNode): 26 | """ 27 | Stores dictionary of child directories/files and reference to parent. 28 | """ 29 | def __init__(self, dirs=None, files=None, parent=None): 30 | self.children = {} 31 | self.parent = parent 32 | 33 | def resolve(self, path, create=False): 34 | path_bits = path.strip('/').split('/', 1) 35 | current = path_bits[0] 36 | rest = path_bits[1] if len(path_bits) > 1 else None 37 | if not rest: 38 | if current == '': 39 | return self 40 | if current in self.children.keys(): 41 | return self.children[current] 42 | if not create: 43 | raise PathDoesNotExist() 44 | node = InMemoryFile() 45 | self.add_child(current, node) 46 | return node 47 | if current in self.children.keys(): 48 | return self.children[current].resolve(rest, create=create) 49 | if not create: 50 | raise PathDoesNotExist() 51 | node = InMemoryDir() 52 | self.add_child(current, node) 53 | return self.children[current].resolve(rest, create) 54 | 55 | def ls(self, path=''): 56 | return self.resolve(path).children.keys() 57 | 58 | def listdir(self, dir): 59 | nodes = tuple(self.resolve(dir).children.iteritems()) 60 | dirs = [k for (k, v) in nodes if isinstance(v, InMemoryDir)] 61 | files = [k for (k, v) in nodes if isinstance(v, InMemoryFile)] 62 | return [dirs, files] 63 | 64 | def delete(self, path): 65 | node = self.resolve(path) 66 | for name, child in node.parent.children.iteritems(): 67 | if child is node: 68 | del node.parent.children[name] 69 | break 70 | 71 | def exists(self, name): 72 | try: 73 | self.resolve(name) 74 | except PathDoesNotExist: 75 | return False 76 | else: 77 | return True 78 | 79 | def size(self, name): 80 | return len(self.resolve(name).contents) 81 | 82 | def open(self, path): 83 | return ContentFile(self.resolve(path, create=True).contents) 84 | 85 | def save(self, path, content): 86 | file = self.resolve(path, create=True) 87 | file.contents = content 88 | return path 89 | 90 | class InMemoryStorage(Storage): 91 | """ 92 | Django storage class for in-memory filesystem. 93 | """ 94 | def __init__(self, filesystem=None): 95 | self.filesystem = filesystem or InMemoryDir() 96 | 97 | def listdir(self, dir): 98 | return self.filesystem.listdir(dir) 99 | 100 | def delete(self, path): 101 | return self.filesystem.delete(path) 102 | 103 | def exists(self, name): 104 | return self.filesystem.exists(name) 105 | 106 | def size(self, name): 107 | return self.filesystem.size(name) 108 | 109 | def _open(self, name, mode=None): 110 | return self.filesystem.open(name) 111 | 112 | def _save(self, name, content): 113 | return self.filesystem.save(name, content.read()) 114 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from inmemorystorage import InMemoryStorage 3 | from inmemorystorage.storage import InMemoryDir, InMemoryFile 4 | from django.core.files.base import ContentFile 5 | 6 | class MemoryStorageTests(unittest.TestCase): 7 | def setUp(self): 8 | self.storage = InMemoryStorage() 9 | self.filesystem = self.storage.filesystem 10 | 11 | def test_listdir(self): 12 | self.assertEqual(self.storage.listdir(''), [[], []]) 13 | 14 | self.filesystem.add_child('dir0', InMemoryDir()) 15 | self.filesystem.add_child('file0', InMemoryFile()) 16 | 17 | self.assertEqual(self.storage.listdir(''), [['dir0'], ['file0']]) 18 | self.assertEqual(self.storage.listdir('dir0'), [[], []]) 19 | 20 | self.filesystem.resolve('dir0').add_child('subdir', InMemoryDir()) 21 | self.assertEqual(self.storage.listdir('dir0'), [['subdir'], []]) 22 | 23 | def test_delete(self): 24 | self.filesystem.add_child('dir0', InMemoryDir()) 25 | self.filesystem.resolve('dir0').add_child('nested_file', InMemoryFile()) 26 | self.filesystem.add_child('file0', InMemoryFile()) 27 | self.assertEqual(self.filesystem.resolve('dir0').ls(), ['nested_file']) 28 | 29 | self.storage.delete('dir0/nested_file') 30 | self.assertEqual(self.filesystem.resolve('dir0').ls(), []) 31 | self.assertEqual(set(self.filesystem.ls()), set(['dir0', 'file0'])) 32 | 33 | self.storage.delete('dir0') 34 | self.assertEqual(set(self.filesystem.ls()), set(['file0'])) 35 | 36 | def test_exists(self): 37 | self.filesystem.add_child('file0', InMemoryFile()) 38 | self.assertTrue(self.storage.exists('file0')) 39 | self.assertFalse(self.storage.exists('file1')) 40 | self.storage.delete('file0') 41 | self.assertFalse(self.storage.exists('file0')) 42 | 43 | def test_size(self): 44 | self.filesystem.add_child('file0', InMemoryFile('test')) 45 | self.assertEqual(self.storage.size('file0'), 4) 46 | 47 | def test_save(self): 48 | self.storage.save('file', ContentFile('test')) 49 | self.assertEqual(self.storage.size('file'), 4) 50 | self.storage.save('subdir/file', ContentFile('test')) 51 | self.assertEqual(self.storage.size('subdir/file'), 4) 52 | 53 | def test_all(self): 54 | self.assertEqual(self.storage.listdir('/'), [[], []]) 55 | self.assertEqual(self.storage.save('dir/subdir/file', ContentFile('testing')), 'dir/subdir/file') 56 | self.assertEqual(self.storage.listdir('/'), [['dir'], []]) 57 | self.assertEqual(self.storage.listdir('dir/'), [['subdir'], []]) 58 | self.assertEqual(self.storage.save('dir/subdir/file2', ContentFile('testing2')), 'dir/subdir/file2') 59 | self.assertEqual(self.storage.save('file', ContentFile('testing3')), 'file') 60 | self.assertEqual(self.storage.listdir('/'), [['dir'], ['file']]) 61 | self.assertEqual(self.storage.listdir('dir/'), [['subdir'], []]) 62 | self.assertEqual(self.storage.open('dir/subdir/file').read(), 'testing') 63 | self.assertEqual(self.storage.size('dir/subdir/file'), 7) 64 | self.assertEqual(self.storage.size('dir/subdir/file2'), 8) 65 | self.assertEqual(self.storage.size('file'), 8) 66 | self.assertEqual(self.storage.delete('file'), None) 67 | self.assertEqual(self.storage.listdir('/'), [['dir'], []]) 68 | self.assertEqual(self.storage.delete('dir/subdir/file'), None) 69 | self.assertEqual(self.storage.listdir('dir/subdir'), [[], ['file2']]) 70 | self.assertEqual(self.storage.exists('dir/subdir/file2'), True) 71 | self.assertEqual(self.storage.delete('dir/subdir/file2'), None) 72 | self.assertEqual(self.storage.exists('dir/subdir/file2'), False) 73 | self.assertEqual(self.storage.listdir('dir/subdir'), [[], []]) 74 | 75 | if __name__ == '__main__': 76 | unittest.main() 77 | --------------------------------------------------------------------------------