1.. _delta_label:
2
3Delta
4=====
5
6DeepDiff Delta is a directed delta that when applied to t1 can yield t2 where delta is the difference between t1 and t2.
7Delta objects are like git commits but for structured data.
8You can convert the diff results into Delta objects, store the deltas, and later apply to other objects.
9
10.. note::
11    If you plan to generate Delta objects from the DeepDiff result, and ignore_order=True, you need to also set the report_repetition=True.
12
13**Parameters**
14
15diff : Delta dictionary, Delta dump payload or a DeepDiff object, default=None.
16    :ref:`delta_diff_label` is the content to be loaded.
17
18delta_path : String, default=None.
19    :ref:`delta_path_label` is the local path to the delta dump file to be loaded
20
21delta_file : File Object, default=None.
22    :ref:`delta_file_label` is the file object containing the delta data.
23
24.. note::
25    You need to pass only one of the diff, delta_path, or delta_file parameters.
26
27deserializer : Deserializer function, default=pickle_load
28    :ref:`delta_deserializer_label` is the function to deserialize the delta content. The default is the pickle_load function that comes with DeepDiff.
29
30serializer : Serializer function, default=pickle_dump
31    :ref:`delta_serializer_label` is the function to serialize the delta content into a format that can be stored. The default is the pickle_dump function that comes with DeepDiff.
32
33log_errors : Boolean, default=True
34    Whether to log the errors or not when applying the delta object.
35
36raise_errors : Boolean, default=False
37    :ref:`raise_errors_label`
38    Whether to raise errors or not when applying a delta object.
39
40mutate : Boolean, default=False.
41    :ref:`delta_mutate_label` defines whether to mutate the original object when adding the delta to it or not.
42    Note that this parameter is not always successful in mutating. For example if your original object
43    is an immutable type such as a frozenset or a tuple, mutation will not succeed.
44    Hence it is recommended to keep this parameter as the default value of False unless you are sure
45    that you do not have immutable objects. There is a small overhead of doing deepcopy on the original
46    object when mutate=False. If performance is a concern and modifying the original object is not a big deal,
47    set the mutate=True but always reassign the output back to the original object.
48
49safe_to_import : Set, default=None.
50    :ref:`delta_safe_to_import_label` is a set of modules that needs to be explicitly white listed to be loaded
51    Example: {'mymodule.MyClass', 'decimal.Decimal'}
52    Note that this set will be added to the basic set of modules that are already white listed.
53    The set of what is already white listed can be found in deepdiff.serialization.SAFE_TO_IMPORT
54
55verify_symmetry : Boolean, default=False
56    :ref:`delta_verify_symmetry_label` is used to verify that the original value of items are the same as when the delta was created. Note that in order for this option to work, the delta object will need to store more data and thus the size of the object will increase. Let's say that the diff object says root[0] changed value from X to Y. If you create the delta with the default value of verify_symmetry=False, then what delta will store is root[0] = Y. And if this delta was applied to an object that has any root[0] value, it will still set the root[0] to Y. However if verify_symmetry=True, then the delta object will store also that the original value of root[0] was X and if you try to apply the delta to an object that has root[0] of any value other than X, it will notify you.
57
58**Returns**
59
60    A delta object that can be added to t1 to recreate t2.
61
62
63.. _delta_diff_label:
64
65Diff to load in Delta
66---------------------
67
68diff : Delta dictionary, Delta dump payload or a DeepDiff object, default=None.
69    diff is the content to be loaded.
70
71>>> from deepdiff import DeepDiff, Delta
72>>> from pprint import pprint
73>>>
74>>> t1 = [1, 2, 3]
75>>> t2 = ['a', 2, 3, 4]
76>>> diff = DeepDiff(t1, t2)
77>>> diff
78{'type_changes': {'root[0]': {'old_type': <class 'int'>, 'new_type': <class 'str'>, 'old_value': 1, 'new_value': 'a'}}, 'iterable_item_added': {'root[3]': 4}}
79>>> delta = Delta(diff)
80>>> delta
81<Delta: {'type_changes': {'root[0]': {'old_type': <class 'int'>, 'new_type': <class 'str'>, 'new_value': ...}>
82
83Applying the delta object to t1 will yield t2:
84
85>>> t1 + delta
86['a', 2, 3, 4]
87>>> t1 + delta == t2
88True
89
90Now let's dump the delta object so we can store it.
91
92>>> dump = delta.dumps()
93>>>
94>>> dump
95b'\x80\x04\x95\x8d\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x0ctype_changes\x94}\x94\x8c\x07root[0]\x94}\x94(\x8c\x08old_type\x94\x8c\x08builtins\x94\x8c\x03int\x94\x93\x94\x8c\x08new_type\x94h\x06\x8c\x03str\x94\x93\x94\x8c\tnew_value\x94\x8c\x01a\x94us\x8c\x13iterable_item_added\x94}\x94\x8c\x07root[3]\x94K\x04su.'
96
97The dumps() function gives us the serialized content of the delta in the form of bytes. We could store it however we want. Or we could use the dump(file_object) to write the dump to the file_object instead. But before we try the dump(file_object) method, let's create a new Delta object and reapply it to t1 and see if we still get t2:
98
99>>> delta2 = Delta(dump)
100>>> t1 + delta2 == t2
101True
102>>>
103
104.. _delta_path_label:
105
106Delta Path parameter
107--------------------
108
109Ok now we can try the dumps(file_object). It does what you expect:
110
111>>> with open('/tmp/delta1', 'wb') as dump_file:
112...     delta.dump(dump_file)
113...
114
115And we use the delta_path parameter to load the delta
116
117>>> delta3 = Delta(delta_path='/tmp/delta1')
118
119It still gives us the same result when applied.
120
121>>> t1 + delta3 == t2
122True
123
124
125.. _delta_file_label:
126
127Delta File parameter
128--------------------
129
130You can also pass a file object containing the delta dump:
131
132>>> with open('/tmp/delta1', 'rb') as dump_file:
133...     delta4 = Delta(delta_file=dump_file)
134...
135>>> t1 + delta4 == t2
136True
137
138
139.. _delta_deserializer_label:
140
141Delta Deserializer
142------------------
143
144DeepDiff by default uses a restricted Python pickle function to deserialize the Delta dumps. Read more about :ref:`delta_dump_safety_label`.
145
146The user of Delta can decide to switch the serializer and deserializer to their custom ones. The serializer and deserializer parameters can be used exactly for that reason. The best way to come up with your own serializer and deserialier is to take a look at the `pickle_dump and pickle_load functions in the serializer module <https://github.com/seperman/deepdiff/serialization.py>`_
147
148.. _delta_json_deserializer_label:
149
150Json Deserializer for Delta
151```````````````````````````
152
153If all you deal with are Json serializable objects, you can use json for serialization.
154
155>>> from deepdiff import DeepDiff, Delta
156>>> import json
157>>> t1 = {"a": 1}
158>>> t2 = {"a": 2}
159>>>
160>>> diff = DeepDiff(t1, t2)
161>>> delta = Delta(diff, serializer=json.dumps)
162>>> dump = delta.dumps()
163>>> dump
164'{"values_changed": {"root[\'a\']": {"new_value": 2}}}'
165>>> delta_reloaded = Delta(dump, deserializer=json.loads)
166>>> t2 == delta_reloaded + t1
167True
168
169
170.. note::
171
172    Json is very limited and easily you can get to deltas that are not json serializable. You will probably want to extend the Python's Json serializer to support your needs.
173
174    >>> t1 = {"a": 1}
175    >>> t2 = {"a": None}
176    >>> diff = DeepDiff(t1, t2)
177    >>> diff
178    {'type_changes': {"root['a']": {'old_type': <class 'int'>, 'new_type': <class 'NoneType'>, 'old_value': 1, 'new_value': None}}}
179    >>> Delta(diff, serializer=json.dumps)
180    <Delta: {'type_changes': {"root['a']": {'old_type': <class 'int'>, 'new_type': <class 'NoneType'>, 'new_v...}>
181    >>> delta = Delta(diff, serializer=json.dumps)
182    >>> dump = delta.dumps()
183    Traceback (most recent call last):
184      File "lib/python3.8/json/encoder.py", line 179, in default
185        raise TypeError(f'Object of type {o.__class__.__name__} '
186    TypeError: Object of type type is not JSON serializable
187
188.. _delta_serializer_label:
189
190Delta Serializer
191----------------
192
193DeepDiff uses pickle to serialize delta objects by default. Please take a look at the :ref:`delta_deserializer_label` for more information.
194
195.. _delta_dump_safety_label:
196
197Delta Dump Safety
198-----------------
199
200Delta by default uses Python's pickle to serialize and deserialize. While the unrestricted use of pickle is not safe as noted in the `pickle's documentation <https://docs.python.org/3/library/pickle.html>`_ , DeepDiff's Delta is written with extra care to `restrict the globals <https://docs.python.org/3/library/pickle.html#restricting-globals>`_ and hence mitigate this security risk.
201
202In fact only a few Python object types are allowed by default. The user of DeepDiff can pass additional types using the :ref:`delta_safe_to_import_label` to allow further object types that need to be allowed.
203
204
205.. _delta_mutate_label:
206
207Delta Mutate parameter
208----------------------
209
210mutate : Boolean, default=False.
211    delta_mutate defines whether to mutate the original object when adding the delta to it or not.
212    Note that this parameter is not always successful in mutating. For example if your original object
213    is an immutable type such as a frozenset or a tuple, mutation will not succeed.
214    Hence it is recommended to keep this parameter as the default value of False unless you are sure
215    that you do not have immutable objects. There is a small overhead of doing deepcopy on the original
216    object when mutate=False. If performance is a concern and modifying the original object is not a big deal,
217    set the mutate=True but always reassign the output back to the original object.
218
219For example:
220
221>>> t1 = [1, 2, [3, 5, 6]]
222>>> t2 = [2, 3, [3, 6, 8]]
223
224>>> diff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
225>>> diff
226{'values_changed': {'root[0]': {'new_value': 3, 'old_value': 1}, 'root[2][1]': {'new_value': 8, 'old_value': 5}}}
227>>> delta = Delta(diff)
228>>> delta
229<Delta: {'values_changed': {'root[0]': {'new_value': 3}, 'root[2][1]': {'new_value': 8}}}>
230
231Note that we can apply delta to objects different than the original objects they were made from:
232
233>>> t3 = ["a", 2, [3, "b", "c"]]
234>>> t3 + delta
235[3, 2, [3, 8, 'c']]
236
237If we check t3, it is still the same as the original value of t3:
238
239>>> t3
240['a', 2, [3, 'b', 'c']]
241
242Now let's make the delta with mutate=True
243
244>>> delta2 = Delta(diff, mutate=True)
245>>> t3 + delta2
246[3, 2, [3, 8, 'c']]
247>>> t3
248[3, 2, [3, 8, 'c']]
249
250Applying the delta to t3 mutated the t3 itself in this case!
251
252
253.. _delta_and_numpy_label:
254
255Delta and Numpy
256---------------
257
258>>> from deepdiff import DeepDiff, Delta
259>>> import numpy as np
260>>> t1 = np.array([1, 2, 3, 5])
261>>> t2 = np.array([2, 2, 7, 5])
262>>> diff = DeepDiff(t1, t2)
263>>> diff
264{'values_changed': {'root[0]': {'new_value': 2, 'old_value': 1}, 'root[2]': {'new_value': 7, 'old_value': 3}}}
265>>> delta = Delta(diff)
266
267.. note::
268    When applying delta to Numpy arrays, make sure to put the delta object first and the numpy array second. This is because Numpy array overrides the + operator and thus DeepDiff's Delta won't be able to be applied.
269
270    >>> t1 + delta
271    Traceback (most recent call last):
272      File "<stdin>", line 1, in <module>
273        raise DeltaNumpyOperatorOverrideError(DELTA_NUMPY_OPERATOR_OVERRIDE_MSG)
274    deepdiff.delta.DeltaNumpyOperatorOverrideError: A numpy ndarray is most likely being added to a delta. Due to Numpy override the + operator, you can only do: delta + ndarray and NOT ndarray + delta
275
276Let's put the delta first then:
277
278>>> delta + t1
279array([2, 2, 7, 5])
280>>> delta + t2 == t2
281array([ True,  True,  True,  True])
282
283
284.. note::
285    You can apply a delta that was created from normal Python objects to Numpy arrays. But it is not recommended.
286
287.. _raise_errors_label:
288
289Delta Raise Errors parameter
290----------------------------
291
292raise_errors : Boolean, default=False
293    Whether to raise errors or not when applying a delta object.
294
295>>> from deepdiff import DeepDiff, Delta
296>>> t1 = [1, 2, [3, 5, 6]]
297>>> t2 = [2, 3, [3, 6, 8]]
298>>> diff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
299>>> delta = Delta(diff, raise_errors=False)
300
301Now let's apply the delta to a very different object:
302
303>>> t3 = [1, 2, 3, 5]
304>>> t4 = t3 + delta
305Unable to get the item at root[2][1]
306
307We get the above log message that it was unable to get the item at root[2][1]. We get the message since by default log_errors=True
308
309Let's see what t4 is now:
310
311>>> t4
312[3, 2, 3, 5]
313
314So the delta was partially applied on t3.
315
316Now let's set the raise_errors=True
317
318>>> delta2 = Delta(diff, raise_errors=True)
319>>>
320>>> t3 + delta2
321Unable to get the item at root[2][1]
322Traceback (most recent call last):
323current_old_value = obj[elem]
324TypeError: 'int' object is not subscriptable
325During handling of the above exception, another exception occurred:
326deepdiff.delta.DeltaError: Unable to get the item at root[2][1]
327
328
329.. _delta_safe_to_import_label:
330
331Delta Safe To Import parameter
332------------------------------
333
334safe_to_import : Set, default=None.
335    safe_to_import is a set of modules that needs to be explicitly white listed to be loaded
336    Example: {'mymodule.MyClass', 'decimal.Decimal'}
337    Note that this set will be added to the basic set of modules that are already white listed.
338
339
340As noted in :ref:`delta_dump_safety_label` and :ref:`delta_deserializer_label`, DeepDiff's Delta takes safety very seriously and thus limits the globals that can be deserialized when importing. However on occasions that you need a specific type (class) that needs to be used in delta objects, you need to pass it to the Delta via safe_to_import parameter.
341
342The set of what is already white listed can be found in deepdiff.serialization.SAFE_TO_IMPORT
343At the time of writing this document, this list consists of:
344
345>>> from deepdiff.serialization import SAFE_TO_IMPORT
346>>> from pprint import pprint
347>>> pprint(SAFE_TO_IMPORT)
348{'builtins.None',
349 'builtins.bin',
350 'builtins.bool',
351 'builtins.bytes',
352 'builtins.complex',
353 'builtins.dict',
354 'builtins.float',
355 'builtins.frozenset',
356 'builtins.int',
357 'builtins.list',
358 'builtins.range',
359 'builtins.set',
360 'builtins.slice',
361 'builtins.str',
362 'builtins.tuple',
363 'collections.namedtuple',
364 'datetime.datetime',
365 'datetime.time',
366 'datetime.timedelta',
367 'decimal.Decimal',
368 'deepdiff.helper.OrderedDictPlus',
369 'ordered_set.OrderedSet',
370 're.Pattern'}
371
372If you want to pass any other argument to safe_to_import, you will need to put the full path to the type as it appears in the sys.modules
373
374For example let's say you have a package call mypackage and has a module called mymodule. If you check the sys.modules, the address to this module must be mypackage.mymodule. In order for Delta to be able to serialize this object via pickle, first of all it has to be `picklable <https://docs.python.org/3/library/pickle.html#object.__reduce__>`_.
375
376>>> diff = DeepDiff(t1, t2)
377>>> delta = Delta(diff)
378>>> dump = delta.dumps()
379
380The dump at this point is serialized via Pickle and can be written to disc if needed.
381
382Later when you want to load this dump, by default Delta will block you from importing anything that is NOT in deepdiff.serialization.SAFE_TO_IMPORT . In fact it will show you this error message when trying to load this dump:
383
384    deepdiff.serialization.ForbiddenModule: Module 'builtins.type' is forbidden. You need to explicitly pass it by passing a safe_to_import parameter
385
386In order to let Delta know that this specific module is safe to import, you will need to pass it to Delta during loading of this dump:
387
388>>> delta = Delta(dump, safe_to_import={'mypackage.mymodule'})
389
390.. note ::
391
392    If you pass a custom deserializer to Delta, DeepDiff will pass safe_to_import parameter to the custom deserializer if that deserializer takes safe_to_import as a parameter in its definition.
393    For example if you just use json.loads as deserializer, the safe_to_import items won't be passed to it since json.loads does not have such a parameter.
394
395
396.. _delta_verify_symmetry_label:
397
398Delta Verify Symmetry parameter
399-------------------------------
400
401verify_symmetry : Boolean, default=False
402    verify_symmetry is used to verify that the original value of items are the same as when the delta was created. Note that in order for this option to work, the delta object will need to store more data and thus the size of the object will increase. Let's say that the diff object says root[0] changed value from X to Y. If you create the delta with the default value of verify_symmetry=False, then what delta will store is root[0] = Y. And if this delta was applied to an object that has any root[0] value, it will still set the root[0] to Y. However if verify_symmetry=True, then the delta object will store also that the original value of root[0] was X and if you try to apply the delta to an object that has root[0] of any value other than X, it will notify you.
403
404
405
406>>> from deepdiff import DeepDiff, Delta
407>>> t1 = [1]
408>>> t2 = [2]
409>>> t3 = [3]
410>>>
411>>> diff = DeepDiff(t1, t2)
412>>>
413>>> delta2 = Delta(diff, raise_errors=False, verify_symmetry=True)
414>>> t4 = delta2 + t3
415Expected the old value for root[0] to be 1 but it is 3. Error found on: while checking the symmetry of the delta. You have applied the delta to an object that has different values than the original object the delta was made from
416>>> t4
417[2]
418
419And if you had set raise_errors=True, then it would have raised the error in addition to logging it.
420