@@ -106,7 +106,7 @@ def multidict(ordered_pairs):
106
106
_jsonloads = functools .partial (json .loads , object_pairs_hook = multidict )
107
107
108
108
109
- def apply_patch (doc , patch , in_place = False ):
109
+ def apply_patch (doc , patch , in_place = False , pointer_cls = JsonPointer ):
110
110
"""Apply list of patches to specified json document.
111
111
112
112
:param doc: Document object.
@@ -119,6 +119,9 @@ def apply_patch(doc, patch, in_place=False):
119
119
By default patch will be applied to document copy.
120
120
:type in_place: bool
121
121
122
+ :param pointer_cls: JSON pointer class to use.
123
+ :type pointer_cls: Type[JsonPointer]
124
+
122
125
:return: Patched document object.
123
126
:rtype: dict
124
127
@@ -137,13 +140,13 @@ def apply_patch(doc, patch, in_place=False):
137
140
"""
138
141
139
142
if isinstance (patch , basestring ):
140
- patch = JsonPatch .from_string (patch )
143
+ patch = JsonPatch .from_string (patch , pointer_cls = pointer_cls )
141
144
else :
142
- patch = JsonPatch (patch )
145
+ patch = JsonPatch (patch , pointer_cls = pointer_cls )
143
146
return patch .apply (doc , in_place )
144
147
145
148
146
- def make_patch (src , dst ):
149
+ def make_patch (src , dst , pointer_cls = JsonPointer ):
147
150
"""Generates patch by comparing two document objects. Actually is
148
151
a proxy to :meth:`JsonPatch.from_diff` method.
149
152
@@ -153,6 +156,9 @@ def make_patch(src, dst):
153
156
:param dst: Data source document object.
154
157
:type dst: dict
155
158
159
+ :param pointer_cls: JSON pointer class to use.
160
+ :type pointer_cls: Type[JsonPointer]
161
+
156
162
>>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]}
157
163
>>> dst = {'baz': 'qux', 'numbers': [1, 4, 7]}
158
164
>>> patch = make_patch(src, dst)
@@ -161,7 +167,7 @@ def make_patch(src, dst):
161
167
True
162
168
"""
163
169
164
- return JsonPatch .from_diff (src , dst )
170
+ return JsonPatch .from_diff (src , dst , pointer_cls = pointer_cls )
165
171
166
172
167
173
class JsonPatch (object ):
@@ -213,8 +219,9 @@ class JsonPatch(object):
213
219
... patch.apply(old) #doctest: +ELLIPSIS
214
220
{...}
215
221
"""
216
- def __init__ (self , patch ):
222
+ def __init__ (self , patch , pointer_cls = JsonPointer ):
217
223
self .patch = patch
224
+ self .pointer_cls = pointer_cls
218
225
219
226
self .operations = {
220
227
'remove' : RemoveOperation ,
@@ -256,23 +263,30 @@ def __ne__(self, other):
256
263
return not (self == other )
257
264
258
265
@classmethod
259
- def from_string (cls , patch_str , loads = None ):
266
+ def from_string (cls , patch_str , loads = None , pointer_cls = JsonPointer ):
260
267
"""Creates JsonPatch instance from string source.
261
268
262
269
:param patch_str: JSON patch as raw string.
263
270
:type patch_str: str
271
+
264
272
:param loads: A function of one argument that loads a serialized
265
273
JSON string.
266
274
:type loads: function
267
275
276
+ :param pointer_cls: JSON pointer class to use.
277
+ :type pointer_cls: Type[JsonPointer]
278
+
268
279
:return: :class:`JsonPatch` instance.
269
280
"""
270
281
json_loader = loads or cls .json_loader
271
282
patch = json_loader (patch_str )
272
- return cls (patch )
283
+ return cls (patch , pointer_cls = pointer_cls )
273
284
274
285
@classmethod
275
- def from_diff (cls , src , dst , optimization = True , dumps = None ):
286
+ def from_diff (
287
+ cls , src , dst , optimization = True , dumps = None ,
288
+ pointer_cls = JsonPointer ,
289
+ ):
276
290
"""Creates JsonPatch instance based on comparison of two document
277
291
objects. Json patch would be created for `src` argument against `dst`
278
292
one.
@@ -287,6 +301,9 @@ def from_diff(cls, src, dst, optimization=True, dumps=None):
287
301
JSON string.
288
302
:type dumps: function
289
303
304
+ :param pointer_cls: JSON pointer class to use.
305
+ :type pointer_cls: Type[JsonPointer]
306
+
290
307
:return: :class:`JsonPatch` instance.
291
308
292
309
>>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]}
@@ -297,10 +314,10 @@ def from_diff(cls, src, dst, optimization=True, dumps=None):
297
314
True
298
315
"""
299
316
json_dumper = dumps or cls .json_dumper
300
- builder = DiffBuilder (json_dumper )
317
+ builder = DiffBuilder (json_dumper , pointer_cls = pointer_cls )
301
318
builder ._compare_values ('' , None , src , dst )
302
319
ops = list (builder .execute ())
303
- return cls (ops )
320
+ return cls (ops , pointer_cls = pointer_cls )
304
321
305
322
def to_string (self , dumps = None ):
306
323
"""Returns patch set as JSON string."""
@@ -345,24 +362,25 @@ def _get_operation(self, operation):
345
362
raise InvalidJsonPatch ("Unknown operation {0!r}" .format (op ))
346
363
347
364
cls = self .operations [op ]
348
- return cls (operation )
365
+ return cls (operation , pointer_cls = self . pointer_cls )
349
366
350
367
351
368
class PatchOperation (object ):
352
369
"""A single operation inside a JSON Patch."""
353
370
354
- def __init__ (self , operation ):
371
+ def __init__ (self , operation , pointer_cls = JsonPointer ):
372
+ self .pointer_cls = pointer_cls
355
373
356
374
if not operation .__contains__ ('path' ):
357
375
raise InvalidJsonPatch ("Operation must have a 'path' member" )
358
376
359
- if isinstance (operation ['path' ], JsonPointer ):
377
+ if isinstance (operation ['path' ], self . pointer_cls ):
360
378
self .location = operation ['path' ].path
361
379
self .pointer = operation ['path' ]
362
380
else :
363
381
self .location = operation ['path' ]
364
382
try :
365
- self .pointer = JsonPointer (self .location )
383
+ self .pointer = self . pointer_cls (self .location )
366
384
except TypeError as ex :
367
385
raise InvalidJsonPatch ("Invalid 'path'" )
368
386
@@ -530,10 +548,10 @@ class MoveOperation(PatchOperation):
530
548
531
549
def apply (self , obj ):
532
550
try :
533
- if isinstance (self .operation ['from' ], JsonPointer ):
551
+ if isinstance (self .operation ['from' ], self . pointer_cls ):
534
552
from_ptr = self .operation ['from' ]
535
553
else :
536
- from_ptr = JsonPointer (self .operation ['from' ])
554
+ from_ptr = self . pointer_cls (self .operation ['from' ])
537
555
except KeyError as ex :
538
556
raise InvalidJsonPatch (
539
557
"The operation does not contain a 'from' member" )
@@ -555,32 +573,32 @@ def apply(self, obj):
555
573
obj = RemoveOperation ({
556
574
'op' : 'remove' ,
557
575
'path' : self .operation ['from' ]
558
- }).apply (obj )
576
+ }, pointer_cls = self . pointer_cls ).apply (obj )
559
577
560
578
obj = AddOperation ({
561
579
'op' : 'add' ,
562
580
'path' : self .location ,
563
581
'value' : value
564
- }).apply (obj )
582
+ }, pointer_cls = self . pointer_cls ).apply (obj )
565
583
566
584
return obj
567
585
568
586
@property
569
587
def from_path (self ):
570
- from_ptr = JsonPointer (self .operation ['from' ])
588
+ from_ptr = self . pointer_cls (self .operation ['from' ])
571
589
return '/' .join (from_ptr .parts [:- 1 ])
572
590
573
591
@property
574
592
def from_key (self ):
575
- from_ptr = JsonPointer (self .operation ['from' ])
593
+ from_ptr = self . pointer_cls (self .operation ['from' ])
576
594
try :
577
595
return int (from_ptr .parts [- 1 ])
578
596
except TypeError :
579
597
return from_ptr .parts [- 1 ]
580
598
581
599
@from_key .setter
582
600
def from_key (self , value ):
583
- from_ptr = JsonPointer (self .operation ['from' ])
601
+ from_ptr = self . pointer_cls (self .operation ['from' ])
584
602
from_ptr .parts [- 1 ] = str (value )
585
603
self .operation ['from' ] = from_ptr .path
586
604
@@ -643,7 +661,7 @@ class CopyOperation(PatchOperation):
643
661
644
662
def apply (self , obj ):
645
663
try :
646
- from_ptr = JsonPointer (self .operation ['from' ])
664
+ from_ptr = self . pointer_cls (self .operation ['from' ])
647
665
except KeyError as ex :
648
666
raise InvalidJsonPatch (
649
667
"The operation does not contain a 'from' member" )
@@ -658,15 +676,16 @@ def apply(self, obj):
658
676
'op' : 'add' ,
659
677
'path' : self .location ,
660
678
'value' : value
661
- }).apply (obj )
679
+ }, pointer_cls = self . pointer_cls ).apply (obj )
662
680
663
681
return obj
664
682
665
683
666
684
class DiffBuilder (object ):
667
685
668
- def __init__ (self , dumps = json .dumps ):
686
+ def __init__ (self , dumps = json .dumps , pointer_cls = JsonPointer ):
669
687
self .dumps = dumps
688
+ self .pointer_cls = pointer_cls
670
689
self .index_storage = [{}, {}]
671
690
self .index_storage2 = [[], []]
672
691
self .__root = root = []
@@ -735,7 +754,7 @@ def execute(self):
735
754
'op' : 'replace' ,
736
755
'path' : op_second .location ,
737
756
'value' : op_second .operation ['value' ],
738
- }).operation
757
+ }, pointer_cls = self . pointer_cls ).operation
739
758
curr = curr [1 ][1 ]
740
759
continue
741
760
@@ -756,22 +775,22 @@ def _item_added(self, path, key, item):
756
775
'op' : 'move' ,
757
776
'from' : op .location ,
758
777
'path' : _path_join (path , key ),
759
- })
778
+ }, pointer_cls = self . pointer_cls )
760
779
self .insert (new_op )
761
780
else :
762
781
new_op = AddOperation ({
763
782
'op' : 'add' ,
764
783
'path' : _path_join (path , key ),
765
784
'value' : item ,
766
- })
785
+ }, pointer_cls = self . pointer_cls )
767
786
new_index = self .insert (new_op )
768
787
self .store_index (item , new_index , _ST_ADD )
769
788
770
789
def _item_removed (self , path , key , item ):
771
790
new_op = RemoveOperation ({
772
791
'op' : 'remove' ,
773
792
'path' : _path_join (path , key ),
774
- })
793
+ }, pointer_cls = self . pointer_cls )
775
794
index = self .take_index (item , _ST_ADD )
776
795
new_index = self .insert (new_op )
777
796
if index is not None :
@@ -786,7 +805,7 @@ def _item_removed(self, path, key, item):
786
805
'op' : 'move' ,
787
806
'from' : new_op .location ,
788
807
'path' : op .location ,
789
- })
808
+ }, pointer_cls = self . pointer_cls )
790
809
new_index [2 ] = new_op
791
810
792
811
else :
@@ -800,7 +819,7 @@ def _item_replaced(self, path, key, item):
800
819
'op' : 'replace' ,
801
820
'path' : _path_join (path , key ),
802
821
'value' : item ,
803
- }))
822
+ }, pointer_cls = self . pointer_cls ))
804
823
805
824
def _compare_dicts (self , path , src , dst ):
806
825
src_keys = set (src .keys ())
0 commit comments