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