# HG changeset patch # User Paul Boddie # Date 1383693857 -3600 # Node ID 4ae7dc2a3a92bef70116bea3fa52e97d07c3591f # Parent e3819a445b17d2ef700258b827aab9956e90c86f Reorganised the item store code, adding initial support for subpage items. diff -r e3819a445b17 -r 4ae7dc2a3a92 ItemSupport.py --- a/ItemSupport.py Wed Oct 30 15:38:24 2013 +0100 +++ b/ItemSupport.py Wed Nov 06 00:24:17 2013 +0100 @@ -6,11 +6,95 @@ @license: GNU GPL (v2 or later), see COPYING.txt for details. """ +from MoinMoin.Page import RootPage from MoinMoin.util import lock import os # Content storage support. +class ItemStoreBase: + + "Common item store functionality." + + def __init__(self, lock_dir): + + "Initialise an item store with the given 'lock_dir' guarding access." + + self.lock_dir = lock_dir + 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): + + """ + Return the number of items. + """ + + return len(self.keys()) + + def __iter__(self): + + "Return an iterator over the items in the store." + + return ItemIterator(self) + + def keys(self): + + "Return a list of keys for items in the store." + + self.readlock.acquire() + try: + return self.get_keys() + finally: + self.readlock.release() + + def __getitem__(self, number): + + "Return the item with the given 'number'." + + self.readlock.acquire() + try: + try: + return self.read_item(number) + except (IOError, OSError): + raise IndexError, number + finally: + self.readlock.release() + + def __delitem__(self, number): + + "Remove the item with the given 'number' from the store." + + self.writelock.acquire() + try: + try: + self.remove_item(number) + except (IOError, OSError): + raise IndexError, number + finally: + self.writelock.release() + + def next(self): + + """ + Return the number of the next item (which should also be the number of + items if none have been deleted). + """ + + self.writelock.acquire() + try: + return self.get_next() + finally: + self.writelock.release() + class ItemDirectoryStore: "A directory-based item store." @@ -47,12 +131,6 @@ return [int(filename) for filename in os.listdir(self.path) if filename.isdigit()] - def deduce_next(self): - - "Deduce the next item number from the existing item files." - - return max(self.get_keys() or [-1]) + 1 - def read_next(self): "Read the next item number from a special file." @@ -131,69 +209,6 @@ finally: self.writelock.release() - def __len__(self): - - """ - Return the number of items. - """ - - return len(self.keys()) - - def __iter__(self): - - "Return an iterator over the items in the store." - - return ItemIterator(self) - - def keys(self): - - "Return a list of keys for items in the store." - - self.readlock.acquire() - try: - return self.get_keys() - finally: - self.readlock.release() - - def __getitem__(self, number): - - "Return the item with the given 'number'." - - self.readlock.acquire() - try: - try: - return self.read_item(number) - except (IOError, OSError): - raise IndexError, number - finally: - self.readlock.release() - - def __delitem__(self, number): - - "Remove the item with the given 'number' from the store." - - self.writelock.acquire() - try: - try: - self.remove_item(number) - except (IOError, OSError): - raise IndexError, number - finally: - self.writelock.release() - - def next(self): - - """ - Return the number of the next item (which should also be the number of - items if none have been deleted). - """ - - self.writelock.acquire() - try: - return self.get_next() - finally: - self.writelock.release() - class ItemIterator: "An iterator over items in a store." diff -r e3819a445b17 -r 4ae7dc2a3a92 MoinSupport.py --- a/MoinSupport.py Wed Oct 30 15:38:24 2013 +0100 +++ b/MoinSupport.py Wed Nov 06 00:24:17 2013 +0100 @@ -13,9 +13,10 @@ """ from DateSupport import * -from ItemSupport import ItemDirectoryStore +from ItemSupport import ItemDirectoryStore, ItemStoreBase from MoinMoin.parser import text_moin_wiki from MoinMoin.Page import Page +from MoinMoin.PageEditor import PageEditor from MoinMoin.util import lock from MoinMoin import config, search, wikiutil from shlex import shlex @@ -1092,18 +1093,18 @@ # Content storage support. -class ItemStore(ItemDirectoryStore): +class ItemStore: "A page-specific item store." - def __init__(self, page, item_dir="items", lock_dir="item_locks"): + def __init__(self, page, item_dir="items", lock_dir="item_locks", store=None): "Initialise an item store for the given 'page'." item_dir_path = tuple(item_dir.split("/")) lock_dir_path = tuple(lock_dir.split("/")) - ItemDirectoryStore.__init__(self, page.getPagePath(*item_dir_path), page.getPagePath(*lock_dir_path)) self.page = page + self.store = store or ItemDirectoryStore(page.getPagePath(*item_dir_path), page.getPagePath(*lock_dir_path)) def can_write(self): @@ -1135,8 +1136,22 @@ user = self.page.request.user return user and user.may.delete(self.page.page_name) + # Store-specific methods. + + def mtime(self): + return self.store.mtime() + # High-level methods. + def keys(self): + + "Return a list of keys for items in the store." + + if not self.can_read(): + return 0 + + return self.store.keys() + def append(self, item): "Append the given 'item' to the store." @@ -1144,7 +1159,7 @@ if not self.can_write(): return - ItemDirectoryStore.append(self, item) + self.store.append(item) def __len__(self): @@ -1153,7 +1168,7 @@ if not self.can_read(): return 0 - return ItemDirectoryStore.__len__(self) + return len(self.store) def __getitem__(self, number): @@ -1162,7 +1177,7 @@ if not self.can_read(): raise IndexError, number - return ItemDirectoryStore.__getitem__(self, number) + return self.store.__getitem__(number) def __delitem__(self, number): @@ -1171,6 +1186,105 @@ if not self.can_delete(): return - return ItemDirectoryStore.__delitem__(self, number) + return self.store.__delitem__(number) + + def __iter__(self): + return self.store.__iter__() + + def next(self): + return self.store.next() + +class ItemSubpageStore(ItemStoreBase): + + "A subpage-based item store." + + def __init__(self, page, lock_dir="item_locks"): + + "Initialise an item store for subpages under the given 'page'." + + lock_dir_path = tuple(lock_dir.split("/")) + ItemStoreBase.__init__(self, page.getPagePath(*lock_dir_path)) + self.page = page + + def mtime(self): + + "Return the last modified time of the item store." + + keys = self.get_keys() + if not keys: + page = self.page + else: + page = Page(self.page.request, self.get_item_path(max(keys))) + + return wikiutil.version2timestamp( + getMetadata(page)["last-modified"] + ) + + def get_next(self): + + "Return the next item number." + + return self.deduce_next() + + def get_keys(self): + + "Return the item keys." + + is_subpage = re.compile(u"^%s/" % re.escape(self.page.page_name), re.UNICODE).match + + # Collect the strict subpages of the parent page. + + leafnames = [] + parentname = self.page.page_name + + for pagename in RootPage(self.page.request).getPageList(filter=is_subpage): + parts = pagename[len(parentname)+1:].split("/") + + # Only collect numbered pages immediately below the parent. + + if len(parts) == 1 and parts[0].isdigit(): + leafnames.append(int(parts[0])) + + return leafnames + + def write_item(self, item, next): + + "Write the given 'item' to a file with the given 'next' item number." + + page = PageEditor(self.page.request, self.get_item_path(next)) + page.saveText(item, 0) + + def read_item(self, number): + + "Read the item with the given item 'number'." + + page = Page(self.page.request, self.get_item_path(number)) + return page.get_raw_body() + + def remove_item(self, number): + + "Remove the item with the given item 'number'." + + page = PageEditor(self.page.request, self.get_item_path(number)) + page.deletePage() + + def get_item_path(self, number): + + "Get the path for the given item 'number'." + + return "%s/%s" % (self.page.page_name, number) + + # High-level methods. + + def append(self, item): + + "Append the given 'item' to the store." + + self.writelock.acquire() + try: + next = self.get_next() + self.write_item(item, next) + finally: + self.writelock.release() # vim: tabstop=4 expandtab shiftwidth=4