Changeset 46579 for Python/nuxeo.treedelta
- Timestamp:
- 06/19/06 01:44:47 (2 years ago)
- Files:
-
- Python/nuxeo.treedelta/trunk/src/nuxeo/treedelta/README.txt (modified) (4 diffs)
- Python/nuxeo.treedelta/trunk/src/nuxeo/treedelta/__init__.py (modified) (1 diff)
- Python/nuxeo.treedelta/trunk/src/nuxeo/treedelta/tests/SIMPLE.txt (modified) (1 diff)
- Python/nuxeo.treedelta/trunk/src/nuxeo/treedelta/treedelta.py (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
Python/nuxeo.treedelta/trunk/src/nuxeo/treedelta/README.txt
r46578 r46579 41 41 >>> from nuxeo.treedelta import ADD, REMOVE, MODIFY 42 42 >>> from nuxeo.treedelta import TreeDelta 43 >>> from nuxeo.treedelta import Ordering44 43 45 44 >>> tree = TreeDelta([]) … … 83 82 >>> TreeDelta([(ADD, 'A'), (REMOVE, 'A'), (ADD, 'A')]) 84 83 TreeDelta([(ADD, 'A')]) 84 85 >>> TreeDelta([(ADD, 'AB'), (REMOVE, 'AB')]) 86 TreeDelta([(REMOVE, 'AB')]) 85 87 86 88 >>> TreeDelta([(ADD, 'A'), (ADD, 'AB'), (REMOVE, 'AB')]) … … 204 206 made in a row on the same node. Then, their `info` mappings are merged:: 205 207 206 >>> tree = TreeDelta([(MODIFY, 'A', {'foo': 1}), 207 ... (MODIFY, 'A', {'bar': 2})]) 208 >>> list(tree.get()) == [(MODIFY, 'A', {'foo': 1, 'bar': 2})] 209 True 208 >>> TreeDelta([(MODIFY, 'A', {'foo': 1}), 209 ... (MODIFY, 'A', {'bar': 2})]) 210 TreeDelta([(MODIFY, 'A', {'foo': 1, 'bar': 2})]) 210 211 211 212 For some specific application use cases, it's possible to override the … … 254 255 a reordeing operation has been done. 255 256 256 Let's see the first case of partial orderings first:257 Full ordering, if available, is returned when the tree is queried:: 257 258 258 259 >>> tree = TreeDelta([(ADD, 'A'), (ADD, 'AB'), (ADD, 'AC')]) 259 >>> tree 260 ... # doctest: +NORMALIZE_WHITESPACE 261 TreeDelta([(ADD, 'A', {Ordering: ['B', 'C']}), 262 (ADD, 'AB'), 263 (ADD, 'AC')]) 260 >>> tree.add(MODIFY, 'A', order=['C', 'B']) 261 >>> tree 262 TreeDelta([(ADD, 'A', {}, ['C', 'B']), (ADD, 'AB'), (ADD, 'AC')]) 263 264 Various checks are made to ensure ordering is always sane:: 265 266 >>> tree = TreeDelta() 267 >>> tree.add(MODIFY, 'A', order=['B']) 268 >>> tree.add(REMOVE, 'ABC') 269 >>> tree.add(ADD, 'AB') 270 Traceback (most recent call last): 271 ... 272 TreeError: ADD 'AB' already in parent order 273 274 >>> tree = TreeDelta() 275 >>> tree.add(MODIFY, 'A', order=['B']) 276 >>> tree.add(MODIFY, 'A', order=['C']) 277 Traceback (most recent call last): 278 ... 279 TreeError: Bad reordering: ['B'] to ['C'] 280 281 >>> tree = TreeDelta() 282 >>> tree.add(ADD, 'AB') 283 >>> tree.add(MODIFY, 'A', order=['C']) 284 Traceback (most recent call last): 285 ... 286 TreeError: Order doesn't mention ['B'] 287 288 >>> tree = TreeDelta([(MODIFY, 'AB')]) 289 >>> tree.add(MODIFY, 'A', order=['C']) 290 Traceback (most recent call last): 291 ... 292 TreeError: Order doesn't mention ['B'] 293 294 >>> tree = TreeDelta() 295 >>> tree.add(MODIFY, 'A', order=['B']) 296 >>> tree.add(ADD, 'AB') 297 Traceback (most recent call last): 298 ... 299 TreeError: ADD 'AB' already in parent order 300 301 >>> tree = TreeDelta() 302 >>> tree.add(MODIFY, 'A', order=['B']) 303 >>> tree.add(REMOVE, 'AC') 304 Traceback (most recent call last): 305 ... 306 TreeError: REMOVE 'AC' not in parent order 307 Python/nuxeo.treedelta/trunk/src/nuxeo/treedelta/__init__.py
r46578 r46579 23 23 from nuxeo.treedelta.simpletreedelta import SimpleTreeDelta 24 24 from nuxeo.treedelta.treedelta import TreeError 25 from nuxeo.treedelta.treedelta import ADD, REMOVE, MODIFY, ORDER 26 from nuxeo.treedelta.treedelta import Ordering 25 from nuxeo.treedelta.treedelta import ADD, REMOVE, MODIFY Python/nuxeo.treedelta/trunk/src/nuxeo/treedelta/tests/SIMPLE.txt
r46577 r46579 208 208 made in a row on the same node. Then, their `info` mappings are merged:: 209 209 210 >>> tree = SimpleTreeDelta([(MODIFY, 'A', {'foo': 1}), 211 ... (MODIFY, 'A', {'bar': 2})]) 212 >>> list(tree.get()) == [(MODIFY, 'A', {'foo': 1, 'bar': 2})] 213 True 210 >>> SimpleTreeDelta([(MODIFY, 'A', {'foo': 1}), 211 ... (MODIFY, 'A', {'bar': 2})]) 212 SimpleTreeDelta([(MODIFY, 'A', {'foo': 1, 'bar': 2})]) 214 213 215 214 For some specific application use cases, it's possible to override the Python/nuxeo.treedelta/trunk/src/nuxeo/treedelta/treedelta.py
r46578 r46579 40 40 REMOVE = 2 41 41 MODIFY = 3 42 ORDER = 443 42 # Internal to the tree representation 44 43 _SEEN = 5 … … 48 47 REMOVE: 'REMOVE', 49 48 MODIFY: 'MODIFY', 50 ORDER: 'ORDER',51 49 _SEEN: '_SEEN', 52 50 }.get 53 54 55 class _Token(object):56 def __init__(self, name):57 self.name = name58 def __repr__(self):59 return self.name60 Ordering = _Token('Ordering')61 PartialOrdering = _Token('PartialOrdering')62 51 63 52 … … 73 62 self.order_total = False 74 63 self.children = {} 64 75 65 def __repr__(self): 76 66 args = [printop(self.op)] … … 81 71 return 'Node(%s)' % ', '.join(args) 82 72 83 73 class Item(object): 74 """Flattened version of a Node. 75 """ 76 def __init__(self, node, path): 77 self.op = node.op 78 self.path = path 79 self.info = node.info.copy() 80 if node.order_total: 81 self.order = list(node.order) 82 else: 83 self.order = None 84 85 def __repr__(self): 86 args = [printop(self.op), repr(self.path)] 87 if self.order is not None: 88 args.append(repr(self.info)) 89 args.append(repr(self.order)) 90 elif self.info: 91 args.append(repr(self.info)) 92 return '(%s)' % ', '.join(args) 84 93 85 94 class TreeDelta(object): … … 87 96 88 97 An ADD is only allowed if nothing was there before. 89 ORDER constraints are checked.90 98 """ 91 99 def __init__(self, ops=None, mergeModifyInfo=None): … … 102 110 103 111 def __repr__(self): 104 ops = sorted(self.get(), key=operator.itemgetter(1)) 105 res = [] 106 for op, path, info in ops: 107 if not info: 108 res.append('(%s, %r)' % (printop(op), path)) 109 else: 110 res.append('(%s, %r, %r)' % (printop(op), path, info)) 111 return self.__class__.__name__ + '([' + ', '.join(res) + '])' 112 ops = sorted(self, key=lambda item: item.path) 113 return '%s([%s])' % (self.__class__.__name__, 114 ', '.join([repr(item) for item in ops])) 112 115 113 116 def __nonzero__(self): … … 126 129 else: 127 130 jpath = path 128 yield (child.op, jpath, child.info)129 for stuffin self._recurse(path, child):130 yield stuff131 yield Item(child, jpath) 132 for item in self._recurse(path, child): 133 yield item 131 134 132 135 def __iter__(self): … … 149 152 return res 150 153 151 def add(self, op, path, info=None ):154 def add(self, op, path, info=None, order=None): 152 155 """Do an operation on the tree. 153 156 154 See `SimpleTreeDelta.op`. 157 ``op`` can be one of ``ADD``, ``REMOVE`` or ``MODIFY``. 158 159 ``path`` is a sequence representing a node. 160 161 ``info`` may be passed, to give additional information about a 162 ``MODIFY`` operation. 163 164 ``order`` may be passed to a ``MODIFY`` operation, to specify 165 the new order of the children of this node as a list of names. 155 166 """ 156 if not path: 157 return TreeError("Empty path forbidden") 167 assert path, "Empty path forbidden" 168 assert order is None or op == MODIFY 169 158 170 if info is None: 159 171 info = {} 160 if not self._root.children and isinstance(path, basestring): 172 173 parent = self._root 174 in_add = False 175 176 if not parent.children and isinstance(path, basestring): 161 177 self._path_is_string = True 162 163 parent, in_add = self._walkExcepLast(op, path)164 self._lastStep(op, path, info, parent, in_add)165 166 def _walkExcepLast(self, op, path):167 """Walk until the last component of the path.168 169 Returns (node, in_add).170 """171 node = self._root172 in_add = False173 178 174 179 # Walk the path to our node. 175 180 for i, name in enumerate(path[:-1]): 176 parent = node177 node = parent.children.get(name)181 children = parent.children 182 node = children.get(name) 178 183 if node is not None: 179 184 if node.op == REMOVE: … … 192 197 # Add an intermediate node 193 198 node = Node(_SEEN) 194 parent.children[name] = node 195 196 return node, in_add 197 198 def _lastStep(self, op, path, info, parent, in_add): 199 children[name] = node 200 parent = node 201 199 202 # Last step 200 203 name = path[-1] … … 206 209 raise TreeError("ADD %r after %s %r" 207 210 % (path, printop(node.op), path)) 211 if name in parent.order: 212 raise TreeError("ADD %r already in parent order" % (path,)) 208 213 children[name] = Node(ADD, info=info) 214 parent.order.append(name) 209 215 elif op == REMOVE: 210 216 if node.op == REMOVE: 211 217 raise TreeError("REMOVE %r after REMOVE %r" % (path, path)) 218 if parent.order_total: 219 if name not in parent.order: 220 # never reached because of other sanity checks? 221 raise TreeError("REMOVE %r not in parent order" % 222 (path,)) 223 parent.order.remove(name) 224 else: 225 if name in parent.order: 226 parent.order.remove(name) 212 227 if in_add: 213 228 assert node.op == ADD # tree invariant … … 215 230 else: 216 231 children[name] = Node(REMOVE, info=info) 217 elif op == ORDER:218 XXX219 232 # op == MODIFY: 220 elif node.op == _SEEN:221 # MODIFY after _SEEN, replace op222 node.op = op223 node.info = info224 233 elif node.op == REMOVE: 225 234 raise TreeError("MODIFY %r after REMOVE %r" % (path, path)) 226 235 else: 227 # MODIFY after ADD/MODIFY, merge infos 228 new_info = self.mergeModifyInfo(node.info, info) 229 node.info = new_info 236 # MODIFY after _SEEN/ADD/MODIFY 237 if order is not None: 238 if node.order_total: 239 # assert same sets 240 if set(node.order) != set(order): 241 raise TreeError("Bad reordering: %s to %s" % 242 (node.order, order)) 243 else: 244 # assert subset 245 missing = set(node.order) - set(order) 246 if missing: 247 raise TreeError("Order doesn't mention %s" % 248 (sorted(missing),)) 249 node.order_total = True 250 node.order = order 251 # assert: check that children are present in 'order' 252 mentioned = [name for name, child in node.children.items() 253 if child.op != REMOVE] 254 missing = set(mentioned) - set(order) 255 if missing: 256 raise TreeError("Order doesn't mention %s" % 257 (sorted(missing),)) 258 if node.op == _SEEN: 259 # MODIFY after _SEEN, replace 260 node.op = op 261 node.info = info 262 else: 263 # MODIFY after ADD/MODIFY, merge infos 264 new_info = self.mergeModifyInfo(node.info, info) 265 node.info = new_info 230 266 else: 231 267 if in_add and op != ADD: 232 268 raise TreeError("%s %r on nonexistent node" % 233 269 (printop(op), path)) 234 children[name] = Node(op, info=info) 235 236 270 # Order checks 271 if op == ADD: 272 if name in parent.order: 273 raise TreeError("ADD %r already in parent order" % (path,)) 274 parent.order.append(name) 275 elif op == REMOVE: 276 if parent.order_total: 277 if name not in parent.order: 278 raise TreeError("REMOVE %r not in parent order" % 279 (path,)) 280 parent.order.remove(name) 281 else: 282 if name in parent.order: 283 parent.order.remove(name) 284 node = Node(op, info=info) 285 children[name] = node 286 if order is not None: 287 node.order = order 288 node.order_total = True
