@@ -199,28 +199,69 @@ class DeltaResult(TextResult):
199
199
200
200
ADD_QUOTES_TO_STRINGS = False
201
201
202
- def __init__ (self , tree_results = None , verbose_level = 1 ):
203
- self .verbose_level = verbose_level
202
+ def __init__ (self , tree_results = None , ignore_order = None ):
203
+ self .ignore_order = ignore_order
204
204
205
205
self .update ({
206
206
"type_changes" : {},
207
207
"dictionary_item_added" : {},
208
- "dictionary_item_removed" : self . __set_or_dict () ,
208
+ "dictionary_item_removed" : {} ,
209
209
"values_changed" : {},
210
210
"iterable_item_added" : {},
211
- "iterable_item_removed" : self . __set_or_dict () ,
211
+ "iterable_item_removed" : {} ,
212
212
"attribute_added" : {},
213
- "attribute_removed" : self . __set_or_dict () ,
213
+ "attribute_removed" : {} ,
214
214
"set_item_removed" : {},
215
215
"set_item_added" : {},
216
- "repetition_change" : {}
216
+ "ignore_order_fixed_indexes" : {},
217
+ "ignore_order_remove_indexes" : {},
217
218
})
218
219
219
220
if tree_results :
220
221
self ._from_tree_results (tree_results )
221
222
222
- def __set_or_dict (self ):
223
- return {} if self .verbose_level >= 2 else PrettyOrderedSet ()
223
+ def _from_tree_results (self , tree ):
224
+ """
225
+ Populate this object by parsing an existing reference-style result dictionary.
226
+ :param tree: A TreeResult
227
+ :return:
228
+ """
229
+ self ._from_tree_type_changes (tree )
230
+ self ._from_tree_default (tree , 'dictionary_item_added' )
231
+ self ._from_tree_default (tree , 'dictionary_item_removed' )
232
+ self ._from_tree_value_changed (tree )
233
+ if self .ignore_order :
234
+ self ._from_tree_iterable_item_added (
235
+ tree , 'iterable_item_added' , delta_report_key = 'ignore_order_fixed_indexes' )
236
+ self ._from_tree_iterable_item_added (
237
+ tree , 'iterable_item_removed' , delta_report_key = 'ignore_order_remove_indexes' )
238
+ else :
239
+ self ._from_tree_default (tree , 'iterable_item_added' )
240
+ self ._from_tree_default (tree , 'iterable_item_removed' )
241
+ self ._from_tree_default (tree , 'attribute_added' )
242
+ self ._from_tree_default (tree , 'attribute_removed' )
243
+ self ._from_tree_set_item_removed (tree )
244
+ self ._from_tree_set_item_added (tree )
245
+ self ._from_tree_repetition_change (tree )
246
+
247
+ def _from_tree_iterable_item_added (self , tree , report_type , delta_report_key ):
248
+ if report_type in tree :
249
+ for change in tree [report_type ]: # report each change
250
+ # determine change direction (added or removed)
251
+ # Report t2 (the new one) whenever possible.
252
+ # In cases where t2 doesn't exist (i.e. stuff removed), report t1.
253
+ if change .t2 is not notpresent :
254
+ item = change .t2
255
+ else :
256
+ item = change .t1
257
+
258
+ # do the reporting
259
+ path , param , _ = change .path (force = FORCE_DEFAULT , get_parent_too = True )
260
+ try :
261
+ ignore_order_fixed_indexes = self [delta_report_key ][path ]
262
+ except KeyError :
263
+ ignore_order_fixed_indexes = self [delta_report_key ][path ] = {}
264
+ ignore_order_fixed_indexes [param ] = item
224
265
225
266
def _from_tree_type_changes (self , tree ):
226
267
if 'type_changes' in tree :
@@ -247,7 +288,7 @@ def _from_tree_type_changes(self, tree):
247
288
})
248
289
self ['type_changes' ][change .path (
249
290
force = FORCE_DEFAULT )] = remap_dict
250
- if self . verbose_level and include_values :
291
+ if include_values :
251
292
remap_dict .update (old_value = change .t1 , new_value = change .t2 )
252
293
253
294
def _from_tree_value_changed (self , tree ):
@@ -259,8 +300,26 @@ def _from_tree_value_changed(self, tree):
259
300
if 'diff' in change .additional :
260
301
the_changed .update ({'diff' : change .additional ['diff' ]})
261
302
262
- def _from_tree_unprocessed (self , tree ):
263
- pass
303
+ def _from_tree_repetition_change (self , tree ):
304
+ if 'repetition_change' in tree :
305
+ for change in tree ['repetition_change' ]:
306
+ path , _ , _ = change .path (get_parent_too = True )
307
+ repetition = RemapDict (change .additional ['repetition' ])
308
+ value = change .t1
309
+ try :
310
+ ignore_order_fixed_indexes = self ['ignore_order_fixed_indexes' ][path ]
311
+ except KeyError :
312
+ ignore_order_fixed_indexes = self ['ignore_order_fixed_indexes' ][path ] = {}
313
+ for index in repetition ['new_indexes' ]:
314
+ ignore_order_fixed_indexes [index ] = value
315
+ # self['repetition_change'][path][]
316
+ # old_indexes = set(repetition['old_indexes'])
317
+ # new_indexes = set(repetition['new_indexes'])
318
+ # value['old_indexes'] = old_indexes - new_indexes
319
+ # value['new_indexes'] = new_indexes - old_indexes
320
+ # self['repetition_change'][path] = RemapDict(change.additional[
321
+ # 'repetition'])
322
+ # self['repetition_change'][path]['value'] = change.t1
264
323
265
324
266
325
class DiffLevel :
@@ -474,7 +533,11 @@ def all_down(self):
474
533
level = level .down
475
534
return level
476
535
477
- def path (self , root = "root" , force = None ):
536
+ @staticmethod
537
+ def _format_result (root , result ):
538
+ return None if result is None else "{}{}" .format (root , result )
539
+
540
+ def path (self , root = "root" , force = None , get_parent_too = False ):
478
541
"""
479
542
A python syntax string describing how to descend to this level, assuming the top level object is called root.
480
543
Returns None if the path is not representable as a string.
@@ -496,11 +559,16 @@ def path(self, root="root", force=None):
496
559
This will pretend all iterables are subscriptable, for example.
497
560
"""
498
561
# TODO: We could optimize this by building on top of self.up's path if it is cached there
499
- if force in self ._path :
500
- result = self ._path [force ]
501
- return None if result is None else "{}{}" .format (root , result )
562
+ cache_key = "{}{}" .format (force , get_parent_too )
563
+ if cache_key in self ._path :
564
+ result = self ._path [cache_key ]
565
+ if get_parent_too :
566
+ # parent, param, result
567
+ return (self ._format_result (root , result [0 ]), result [1 ], self ._format_result (root , result [2 ]))
568
+ else :
569
+ return self ._format_result (root , result )
502
570
503
- result = ""
571
+ result = parent = param = ""
504
572
level = self .all_up # start at the root
505
573
506
574
# traverse all levels of this relationship
@@ -515,6 +583,8 @@ def path(self, root="root", force=None):
515
583
# Build path for this level
516
584
item = next_rel .get_param_repr (force )
517
585
if item :
586
+ parent = result
587
+ param = next_rel .param
518
588
result += item
519
589
else :
520
590
# it seems this path is not representable as a string
@@ -525,7 +595,10 @@ def path(self, root="root", force=None):
525
595
level = level .down
526
596
527
597
self ._path [force ] = result
528
- result = None if result is None else "{}{}" .format (root , result )
598
+ result = self ._format_result (root , result )
599
+ if get_parent_too :
600
+ parent = self ._format_result (root , parent )
601
+ return (parent , param , result )
529
602
return result
530
603
531
604
def create_deeper (self ,
0 commit comments