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 mtime(self): 29 30 "Return the last modified time of the item store directory." 31 32 return os.path.getmtime(self.path) 33 34 def get_next(self): 35 36 "Return the next item number." 37 38 next = self.read_next() 39 if next is None: 40 next = self.deduce_next() 41 self.write_next(next) 42 return next 43 44 def deduce_next(self): 45 46 "Deduce the next item number from the existing item files." 47 48 return max([int(filename) for filename in os.listdir(self.path) if filename.isdigit()] or [-1]) + 1 49 50 def read_next(self): 51 52 "Read the next item number from a special file." 53 54 if not os.path.exists(self.next_path): 55 return None 56 57 f = open(self.next_path) 58 try: 59 try: 60 return int(f.read()) 61 except ValueError: 62 return None 63 finally: 64 f.close() 65 66 def write_next(self, next): 67 68 "Write the 'next' item number to a special file." 69 70 f = open(self.next_path, "w") 71 try: 72 f.write(str(next)) 73 finally: 74 f.close() 75 76 def write_item(self, item, next): 77 78 "Write the given 'item' to a file with the given 'next' item number." 79 80 f = open(self.get_item_path(next), "w") 81 try: 82 f.write(item) 83 finally: 84 f.close() 85 86 def read_item(self, number): 87 88 "Read the item with the given item 'number'." 89 90 f = open(self.get_item_path(number)) 91 try: 92 return f.read() 93 finally: 94 f.close() 95 96 def get_item_path(self, number): 97 98 "Get the path for the given item 'number'." 99 100 path = os.path.abspath(os.path.join(self.path, str(number))) 101 basepath = os.path.join(self.path, "") 102 103 if os.path.commonprefix([path, basepath]) != basepath: 104 raise OSError, path 105 106 return path 107 108 # High-level methods. 109 110 def append(self, item): 111 112 "Append the given 'item' to the store." 113 114 self.writelock.acquire() 115 try: 116 next = self.get_next() 117 self.write_item(item, next) 118 self.write_next(next + 1) 119 finally: 120 self.writelock.release() 121 122 def __len__(self): 123 124 """ 125 Return the number of the next item (which should also be the number of 126 items). 127 """ 128 129 self.writelock.acquire() 130 try: 131 return self.get_next() 132 finally: 133 self.writelock.release() 134 135 def __iter__(self): 136 137 "Return an iterator over the items in the store." 138 139 return ItemIterator(self) 140 141 def __getitem__(self, number): 142 143 "Return the item with the given 'number'." 144 145 self.readlock.acquire() 146 try: 147 try: 148 return self.read_item(number) 149 except (IOError, OSError): 150 raise IndexError, number 151 finally: 152 self.readlock.release() 153 154 class ItemIterator: 155 156 "An iterator over items in a store." 157 158 def __init__(self, store, direction=1): 159 self.store = store 160 self.direction = direction 161 self.reset() 162 163 def reset(self): 164 if self.direction == 1: 165 self._next = 0 166 self.final = len(self.store) 167 else: 168 self._next = len(self.store) - 1 169 self.final = 0 170 171 def more(self): 172 if self.direction == 1: 173 return self._next < self.final 174 else: 175 return self._next >= self.final 176 177 def get_next(self): 178 next = self._next 179 self._next += self.direction 180 return next 181 182 def next(self): 183 while self.more(): 184 try: 185 return self.store[self.get_next()] 186 except IndexError: 187 pass 188 189 raise StopIteration 190 191 def reverse(self): 192 self.direction = -self.direction 193 self.reset() 194 195 def reversed(self): 196 self.reverse() 197 return self 198 199 def __iter__(self): 200 return self 201 202 # vim: tabstop=4 expandtab shiftwidth=4