1.1 --- a/ItemSupport.py Sun Mar 30 22:38:05 2014 +0200
1.2 +++ b/ItemSupport.py Tue May 06 22:54:10 2014 +0200
1.3 @@ -6,6 +6,7 @@
1.4 @license: GNU GPL (v2 or later), see COPYING.txt for details.
1.5 """
1.6
1.7 +from MoinMoin import config
1.8 from MoinMoin.Page import Page
1.9 from MoinMoin.PageEditor import PageEditor
1.10 from MoinMoin.security import Permissions
1.11 @@ -53,12 +54,6 @@
1.12 self.writelock = lock.WriteLock(lock_dir)
1.13 self.readlock = lock.ReadLock(lock_dir)
1.14
1.15 - def deduce_next(self):
1.16 -
1.17 - "Deduce the next item number from the existing item files."
1.18 -
1.19 - return max(self.get_keys() or [-1]) + 1
1.20 -
1.21 # High-level methods.
1.22
1.23 def __len__(self):
1.24 @@ -124,41 +119,15 @@
1.25 finally:
1.26 self.writelock.release()
1.27
1.28 -class DirectoryItemStore(GeneralItemStore):
1.29 -
1.30 - "A directory-based item store."
1.31 -
1.32 - def __init__(self, path, lock_dir):
1.33 -
1.34 - "Initialise an item store for the given 'path' and 'lock_dir'."
1.35 +class SequentialAccess(GeneralItemStore):
1.36
1.37 - self.path = path
1.38 - self.next_path = os.path.join(self.path, "next")
1.39 - self.lock_dir = lock_dir
1.40 - self.writelock = lock.WriteLock(lock_dir)
1.41 - self.readlock = lock.ReadLock(lock_dir)
1.42 -
1.43 - def mtime(self):
1.44 -
1.45 - "Return the last modified time of the item store directory."
1.46 + "Support sequential access to items."
1.47
1.48 - return os.path.getmtime(self.path)
1.49 -
1.50 - def get_next(self):
1.51 -
1.52 - "Return the next item number."
1.53 + def deduce_next(self):
1.54
1.55 - next = self.read_next()
1.56 - if next is None:
1.57 - next = self.deduce_next()
1.58 - self.write_next(next)
1.59 - return next
1.60 + "Deduce the next item number from the existing item files."
1.61
1.62 - def get_keys(self):
1.63 -
1.64 - "Return the item keys."
1.65 -
1.66 - return [int(filename) for filename in os.listdir(self.path) if filename.isdigit()]
1.67 + return max(self.get_keys() or [-1]) + 1
1.68
1.69 def read_next(self):
1.70
1.71 @@ -186,6 +155,32 @@
1.72 finally:
1.73 f.close()
1.74
1.75 +class DirectoryStore(GeneralItemStore):
1.76 +
1.77 + "A directory-based item store."
1.78 +
1.79 + def __init__(self, path, lock_dir):
1.80 +
1.81 + "Initialise an item store for the given 'path' and 'lock_dir'."
1.82 +
1.83 + self.path = path
1.84 + self.next_path = os.path.join(self.path, "next")
1.85 + self.lock_dir = lock_dir
1.86 + self.writelock = lock.WriteLock(lock_dir)
1.87 + self.readlock = lock.ReadLock(lock_dir)
1.88 +
1.89 + def mtime(self):
1.90 +
1.91 + "Return the last modified time of the item store directory."
1.92 +
1.93 + return os.path.getmtime(self.path)
1.94 +
1.95 + def get_keys(self):
1.96 +
1.97 + "Return the item keys."
1.98 +
1.99 + return [int(filename) for filename in os.listdir(self.path) if filename.isdigit()]
1.100 +
1.101 def write_item(self, item, next):
1.102
1.103 "Write the given 'item' to a file with the given 'next' item number."
1.104 @@ -196,27 +191,32 @@
1.105 finally:
1.106 f.close()
1.107
1.108 - def read_item(self, number):
1.109 + def read_item(self, identifier):
1.110
1.111 - "Read the item with the given item 'number'."
1.112 + "Read the item with the given item 'identifier'."
1.113
1.114 - f = open(self.get_item_path(number), "rb")
1.115 + f = open(self.get_item_path(identifier), "rb")
1.116 try:
1.117 return f.read()
1.118 finally:
1.119 f.close()
1.120
1.121 - def remove_item(self, number):
1.122 + def remove_item(self, identifier):
1.123 +
1.124 + "Remove the item with the given item 'identifier'."
1.125
1.126 - "Remove the item with the given item 'number'."
1.127 + os.remove(self.get_item_path(identifier))
1.128
1.129 - os.remove(self.get_item_path(number))
1.130 + def get_item_path(self, identifier):
1.131
1.132 - def get_item_path(self, number):
1.133 + "Get the path for the given item 'identifier'."
1.134
1.135 - "Get the path for the given item 'number'."
1.136 + if isinstance(identifier, unicode):
1.137 + filename = identifier.encode(config.charset)
1.138 + else:
1.139 + filename = identifier
1.140
1.141 - path = os.path.abspath(os.path.join(self.path, str(number)))
1.142 + path = os.path.abspath(os.path.join(self.path, filename))
1.143 basepath = os.path.join(self.path, "")
1.144
1.145 if os.path.commonprefix([path, basepath]) != basepath:
1.146 @@ -224,6 +224,20 @@
1.147
1.148 return path
1.149
1.150 +class DirectoryItemStore(DirectoryStore, SequentialAccess):
1.151 +
1.152 + "A directory-based item store with numeric keys."
1.153 +
1.154 + def get_next(self):
1.155 +
1.156 + "Return the next item number."
1.157 +
1.158 + next = self.read_next()
1.159 + if next is None:
1.160 + next = self.deduce_next()
1.161 + self.write_next(next)
1.162 + return next
1.163 +
1.164 # High-level methods.
1.165
1.166 def append(self, item):
1.167 @@ -238,7 +252,21 @@
1.168 finally:
1.169 self.writelock.release()
1.170
1.171 -class SubpageItemStore(GeneralItemStore):
1.172 +class DirectoryNamedItemStore(DirectoryStore):
1.173 +
1.174 + "A directory-based item store with explicit keys."
1.175 +
1.176 + def __setitem__(self, name, item):
1.177 +
1.178 + "Using the given 'name', set the given 'item' in the store."
1.179 +
1.180 + self.writelock.acquire()
1.181 + try:
1.182 + self.write_item(item, name)
1.183 + finally:
1.184 + self.writelock.release()
1.185 +
1.186 +class SubpageItemStore(SequentialAccess):
1.187
1.188 "A subpage-based item store."
1.189
1.190 @@ -303,7 +331,7 @@
1.191
1.192 def write_item(self, item, next):
1.193
1.194 - "Write the given 'item' to a file with the given 'next' item number."
1.195 + "Write the given 'item' to a page with the given 'next' item number."
1.196
1.197 request = self.page.request
1.198 pagename = self.get_item_path(next)
1.199 @@ -361,9 +389,10 @@
1.200
1.201 "An iterator over items in a store."
1.202
1.203 - def __init__(self, store, direction=1):
1.204 + def __init__(self, store, direction=1, keys=None):
1.205 self.store = store
1.206 self.direction = direction
1.207 + self.keys = keys
1.208 self.reset()
1.209
1.210 def reset(self):
1.211 @@ -381,7 +410,10 @@
1.212 return self._next >= self.final
1.213
1.214 def get_next(self):
1.215 - next = self._next
1.216 + if self.keys:
1.217 + next = self.keys[self._next]
1.218 + else:
1.219 + next = self._next
1.220 self._next += self.direction
1.221 return next
1.222
1.223 @@ -416,6 +448,17 @@
1.224 lock_dir_path = tuple(lock_dir.split("/"))
1.225 return DirectoryItemStore(page.getPagePath(*item_dir_path), page.getPagePath(*lock_dir_path))
1.226
1.227 +def getDirectoryNamedItemStoreForPage(page, item_dir, lock_dir):
1.228 +
1.229 + """
1.230 + A convenience function returning a directory-based store for the given
1.231 + 'page', using the given 'item_dir' and 'lock_dir'.
1.232 + """
1.233 +
1.234 + item_dir_path = tuple(item_dir.split("/"))
1.235 + lock_dir_path = tuple(lock_dir.split("/"))
1.236 + return DirectoryNamedItemStore(page.getPagePath(*item_dir_path), page.getPagePath(*lock_dir_path))
1.237 +
1.238 def getSubpageItemStoreForPage(page, lock_dir):
1.239
1.240 """
1.241 @@ -482,15 +525,6 @@
1.242
1.243 return self.store.keys()
1.244
1.245 - def append(self, item):
1.246 -
1.247 - "Append the given 'item' to the store."
1.248 -
1.249 - if not self.can_write():
1.250 - return
1.251 -
1.252 - self.store.append(item)
1.253 -
1.254 def __len__(self):
1.255
1.256 "Return the number of items in the store."
1.257 @@ -524,15 +558,48 @@
1.258 def next(self):
1.259 return self.store.next()
1.260
1.261 +class SequentialStoreBase:
1.262 +
1.263 + "Sequential access methods for item stores."
1.264 +
1.265 + def append(self, item):
1.266 +
1.267 + "Append the given 'item' to the store."
1.268 +
1.269 + if not self.can_write():
1.270 + return
1.271 +
1.272 + self.store.append(item)
1.273 +
1.274 +class NamedStoreBase:
1.275 +
1.276 + "Name-based access methods for item stores."
1.277 +
1.278 + def __setitem__(self, name, item):
1.279 +
1.280 + "Using the given 'name', set the given 'item' in the store."
1.281 +
1.282 + if not self.can_write():
1.283 + return
1.284 +
1.285 + self.store[name] = item
1.286 +
1.287 # Convenience store classes.
1.288
1.289 -class ItemStore(ItemStoreBase):
1.290 +class ItemStore(ItemStoreBase, SequentialStoreBase):
1.291
1.292 "Store items in a directory via a page."
1.293
1.294 def __init__(self, page, item_dir="items", lock_dir=None):
1.295 ItemStoreBase.__init__(self, page, getDirectoryItemStoreForPage(page, item_dir, lock_dir or ("%s-locks" % item_dir)))
1.296
1.297 +class NamedItemStore(ItemStoreBase, NamedStoreBase):
1.298 +
1.299 + "Store items in a directory via a page."
1.300 +
1.301 + def __init__(self, page, item_dir="items", lock_dir=None):
1.302 + ItemStoreBase.__init__(self, page, getDirectoryNamedItemStoreForPage(page, item_dir, lock_dir or ("%s-locks" % item_dir)))
1.303 +
1.304 class ItemSubpageStore(ItemStoreBase):
1.305
1.306 "Store items in subpages of a page."