1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - ItemSupport library 4 5 @copyright: 2013 by Paul Boddie <paul@boddie.org.uk> 6 @license: GNU GPL (v2 or later), see COPYING.txt for details. 7 """ 8 9 from MoinMoin.util import lock 10 import os 11 12 # Content storage support. 13 14 class ItemDirectoryStore: 15 16 "A directory-based item store." 17 18 def __init__(self, path, lock_dir): 19 20 "Initialise an item store for the given 'path' and 'lock_dir'." 21 22 self.path = path 23 self.next_path = os.path.join(self.path, "next") 24 self.lock_dir = lock_dir 25 self.writelock = lock.WriteLock(lock_dir) 26 self.readlock = lock.ReadLock(lock_dir) 27 28 def get_next(self): 29 30 "Return the next item number." 31 32 next = self.read_next() 33 if next is None: 34 next = self.deduce_next() 35 self.write_next(next) 36 return next 37 38 def deduce_next(self): 39 40 "Deduce the next item number from the existing item files." 41 42 return max([int(filename) for filename in os.listdir(self.path) if filename.isdigit()] or [-1]) + 1 43 44 def read_next(self): 45 46 "Read the next item number from a special file." 47 48 if not os.path.exists(self.next_path): 49 return None 50 51 f = open(self.next_path) 52 try: 53 try: 54 return int(f.read()) 55 except ValueError: 56 return None 57 finally: 58 f.close() 59 60 def write_next(self, next): 61 62 "Write the 'next' item number to a special file." 63 64 f = open(self.next_path, "w") 65 try: 66 f.write(str(next)) 67 finally: 68 f.close() 69 70 def write_item(self, item, next): 71 72 "Write the given 'item' to a file with the given 'next' item number." 73 74 f = open(self.get_item_path(next), "w") 75 try: 76 f.write(item) 77 finally: 78 f.close() 79 80 def read_item(self, number): 81 82 "Read the item with the given item 'number'." 83 84 f = open(self.get_item_path(number)) 85 try: 86 return f.read() 87 finally: 88 f.close() 89 90 def get_item_path(self, number): 91 92 "Get the path for the given item 'number'." 93 94 path = os.path.abspath(os.path.join(self.path, str(number))) 95 basepath = os.path.join(self.path, "") 96 97 if os.path.commonprefix([path, basepath]) != basepath: 98 raise OSError, path 99 100 return path 101 102 # High-level methods. 103 104 def append(self, item): 105 106 "Append the given 'item' to the store." 107 108 self.writelock.acquire() 109 try: 110 next = self.get_next() 111 self.write_item(item, next) 112 self.write_next(next + 1) 113 finally: 114 self.writelock.release() 115 116 def __len__(self): 117 118 """ 119 Return the number of the next item (which should also be the number of 120 items). 121 """ 122 123 self.writelock.acquire() 124 try: 125 return self.get_next() 126 finally: 127 self.writelock.release() 128 129 def __iter__(self): 130 131 "Return an iterator over the items in the store." 132 133 return ItemIterator(self) 134 135 def __getitem__(self, number): 136 137 "Return the item with the given 'number'." 138 139 self.readlock.acquire() 140 try: 141 try: 142 return self.read_item(number) 143 except (IOError, OSError): 144 raise IndexError, number 145 finally: 146 self.readlock.release() 147 148 class ItemIterator: 149 150 "An iterator over items in a store." 151 152 def __init__(self, store): 153 self.store = store 154 self._next = -1 155 156 def next(self): 157 final = len(self.store) 158 159 while self._next < final: 160 self._next += 1 161 try: 162 return self.store[self._next] 163 except IndexError: 164 pass 165 166 raise StopIteration 167 168 # vim: tabstop=4 expandtab shiftwidth=4