# HG changeset patch # User Paul Boddie # Date 1399409650 -7200 # Node ID 9baf3570197b280a97b61376d228f869e4dc791b # Parent 9ee4ce1abcffc6cd320cf9749b6f273b488bca86 Introduced explicitly named item storage in addition to sequential item storage. diff -r 9ee4ce1abcff -r 9baf3570197b ItemSupport.py --- a/ItemSupport.py Sun Mar 30 22:38:05 2014 +0200 +++ b/ItemSupport.py Tue May 06 22:54:10 2014 +0200 @@ -6,6 +6,7 @@ @license: GNU GPL (v2 or later), see COPYING.txt for details. """ +from MoinMoin import config from MoinMoin.Page import Page from MoinMoin.PageEditor import PageEditor from MoinMoin.security import Permissions @@ -53,12 +54,6 @@ self.writelock = lock.WriteLock(lock_dir) self.readlock = lock.ReadLock(lock_dir) - def deduce_next(self): - - "Deduce the next item number from the existing item files." - - return max(self.get_keys() or [-1]) + 1 - # High-level methods. def __len__(self): @@ -124,41 +119,15 @@ finally: self.writelock.release() -class DirectoryItemStore(GeneralItemStore): - - "A directory-based item store." - - def __init__(self, path, lock_dir): - - "Initialise an item store for the given 'path' and 'lock_dir'." +class SequentialAccess(GeneralItemStore): - self.path = path - self.next_path = os.path.join(self.path, "next") - self.lock_dir = lock_dir - self.writelock = lock.WriteLock(lock_dir) - self.readlock = lock.ReadLock(lock_dir) - - def mtime(self): - - "Return the last modified time of the item store directory." + "Support sequential access to items." - return os.path.getmtime(self.path) - - def get_next(self): - - "Return the next item number." + def deduce_next(self): - next = self.read_next() - if next is None: - next = self.deduce_next() - self.write_next(next) - return next + "Deduce the next item number from the existing item files." - def get_keys(self): - - "Return the item keys." - - return [int(filename) for filename in os.listdir(self.path) if filename.isdigit()] + return max(self.get_keys() or [-1]) + 1 def read_next(self): @@ -186,6 +155,32 @@ finally: f.close() +class DirectoryStore(GeneralItemStore): + + "A directory-based item store." + + def __init__(self, path, lock_dir): + + "Initialise an item store for the given 'path' and 'lock_dir'." + + self.path = path + self.next_path = os.path.join(self.path, "next") + self.lock_dir = lock_dir + self.writelock = lock.WriteLock(lock_dir) + self.readlock = lock.ReadLock(lock_dir) + + def mtime(self): + + "Return the last modified time of the item store directory." + + return os.path.getmtime(self.path) + + def get_keys(self): + + "Return the item keys." + + return [int(filename) for filename in os.listdir(self.path) if filename.isdigit()] + def write_item(self, item, next): "Write the given 'item' to a file with the given 'next' item number." @@ -196,27 +191,32 @@ finally: f.close() - def read_item(self, number): + def read_item(self, identifier): - "Read the item with the given item 'number'." + "Read the item with the given item 'identifier'." - f = open(self.get_item_path(number), "rb") + f = open(self.get_item_path(identifier), "rb") try: return f.read() finally: f.close() - def remove_item(self, number): + def remove_item(self, identifier): + + "Remove the item with the given item 'identifier'." - "Remove the item with the given item 'number'." + os.remove(self.get_item_path(identifier)) - os.remove(self.get_item_path(number)) + def get_item_path(self, identifier): - def get_item_path(self, number): + "Get the path for the given item 'identifier'." - "Get the path for the given item 'number'." + if isinstance(identifier, unicode): + filename = identifier.encode(config.charset) + else: + filename = identifier - path = os.path.abspath(os.path.join(self.path, str(number))) + path = os.path.abspath(os.path.join(self.path, filename)) basepath = os.path.join(self.path, "") if os.path.commonprefix([path, basepath]) != basepath: @@ -224,6 +224,20 @@ return path +class DirectoryItemStore(DirectoryStore, SequentialAccess): + + "A directory-based item store with numeric keys." + + def get_next(self): + + "Return the next item number." + + next = self.read_next() + if next is None: + next = self.deduce_next() + self.write_next(next) + return next + # High-level methods. def append(self, item): @@ -238,7 +252,21 @@ finally: self.writelock.release() -class SubpageItemStore(GeneralItemStore): +class DirectoryNamedItemStore(DirectoryStore): + + "A directory-based item store with explicit keys." + + def __setitem__(self, name, item): + + "Using the given 'name', set the given 'item' in the store." + + self.writelock.acquire() + try: + self.write_item(item, name) + finally: + self.writelock.release() + +class SubpageItemStore(SequentialAccess): "A subpage-based item store." @@ -303,7 +331,7 @@ def write_item(self, item, next): - "Write the given 'item' to a file with the given 'next' item number." + "Write the given 'item' to a page with the given 'next' item number." request = self.page.request pagename = self.get_item_path(next) @@ -361,9 +389,10 @@ "An iterator over items in a store." - def __init__(self, store, direction=1): + def __init__(self, store, direction=1, keys=None): self.store = store self.direction = direction + self.keys = keys self.reset() def reset(self): @@ -381,7 +410,10 @@ return self._next >= self.final def get_next(self): - next = self._next + if self.keys: + next = self.keys[self._next] + else: + next = self._next self._next += self.direction return next @@ -416,6 +448,17 @@ lock_dir_path = tuple(lock_dir.split("/")) return DirectoryItemStore(page.getPagePath(*item_dir_path), page.getPagePath(*lock_dir_path)) +def getDirectoryNamedItemStoreForPage(page, item_dir, lock_dir): + + """ + A convenience function returning a directory-based store for the given + 'page', using the given 'item_dir' and 'lock_dir'. + """ + + item_dir_path = tuple(item_dir.split("/")) + lock_dir_path = tuple(lock_dir.split("/")) + return DirectoryNamedItemStore(page.getPagePath(*item_dir_path), page.getPagePath(*lock_dir_path)) + def getSubpageItemStoreForPage(page, lock_dir): """ @@ -482,15 +525,6 @@ return self.store.keys() - def append(self, item): - - "Append the given 'item' to the store." - - if not self.can_write(): - return - - self.store.append(item) - def __len__(self): "Return the number of items in the store." @@ -524,15 +558,48 @@ def next(self): return self.store.next() +class SequentialStoreBase: + + "Sequential access methods for item stores." + + def append(self, item): + + "Append the given 'item' to the store." + + if not self.can_write(): + return + + self.store.append(item) + +class NamedStoreBase: + + "Name-based access methods for item stores." + + def __setitem__(self, name, item): + + "Using the given 'name', set the given 'item' in the store." + + if not self.can_write(): + return + + self.store[name] = item + # Convenience store classes. -class ItemStore(ItemStoreBase): +class ItemStore(ItemStoreBase, SequentialStoreBase): "Store items in a directory via a page." def __init__(self, page, item_dir="items", lock_dir=None): ItemStoreBase.__init__(self, page, getDirectoryItemStoreForPage(page, item_dir, lock_dir or ("%s-locks" % item_dir))) +class NamedItemStore(ItemStoreBase, NamedStoreBase): + + "Store items in a directory via a page." + + def __init__(self, page, item_dir="items", lock_dir=None): + ItemStoreBase.__init__(self, page, getDirectoryNamedItemStoreForPage(page, item_dir, lock_dir or ("%s-locks" % item_dir))) + class ItemSubpageStore(ItemStoreBase): "Store items in subpages of a page."