• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

deepdiff/H13-Oct-2021-5,8174,571

deepdiff.egg-info/H03-May-2022-519384

docs/H13-Oct-2021-4,0012,968

tests/H13-Oct-2021-7,9296,820

LICENSEH A D28-Apr-20211.1 KiB2418

MANIFEST.inH A D01-Feb-2021317 1615

PKG-INFOH A D13-Oct-202120.4 KiB519384

README.mdH A D13-Oct-202115.6 KiB494360

conftest.pyH A D29-Apr-20212.2 KiB8355

pytest.iniH A D01-Feb-202158 32

run_tests.shH A D01-Feb-202148 21

setup.cfgH A D13-Oct-2021433 2819

setup.pyH A D13-Oct-20212.1 KiB6855

README.md

1# DeepDiff v 5.6.0
2
3![Downloads](https://img.shields.io/pypi/dm/deepdiff.svg?style=flat)
4![Python Versions](https://img.shields.io/pypi/pyversions/deepdiff.svg?style=flat)
5![License](https://img.shields.io/pypi/l/deepdiff.svg?version=latest)
6[![Build Status](https://github.com/seperman/deepdiff/workflows/Unit%20Tests/badge.svg)](https://github.com/seperman/deepdiff/actions)
7[![codecov](https://codecov.io/gh/seperman/deepdiff/branch/master/graph/badge.svg?token=KkHZ3siA3m)](https://codecov.io/gh/seperman/deepdiff)
8
9## DeepDiff Overview
10
11- DeepDiff: Deep Difference of dictionaries, iterables, strings and other objects. It will recursively look for all the changes.
12- DeepSearch: Search for objects within other objects.
13- DeepHash: Hash any object based on their content.
14
15Tested on Python 3.6+ and PyPy3.
16
17**NOTE: Python 2 is not supported any more. DeepDiff v3.3.0 was the last version to support Python 2**
18
19**NOTE: The last version of DeepDiff to work on Python 3.5 was DeepDiff 5-0-2**
20
21- [Documentation](https://zepworks.com/deepdiff/5.6.0/)
22
23## What is new?
24
25DeepDiff 5-6-0 allows you to pass custom operators.
26
27```python
28>>> from deepdiff import DeepDiff
29>>> from deepdiff.operator import BaseOperator
30>>> class CustomClass:
31...     def __init__(self, d: dict, l: list):
32...         self.dict = d
33...         self.dict['list'] = l
34...
35>>>
36>>> custom1 = CustomClass(d=dict(a=1, b=2), l=[1, 2, 3])
37>>> custom2 = CustomClass(d=dict(c=3, d=4), l=[1, 2, 3, 2])
38>>> custom3 = CustomClass(d=dict(a=1, b=2), l=[1, 2, 3, 4])
39>>>
40>>>
41>>> class ListMatchOperator(BaseOperator):
42...     def give_up_diffing(self, level, diff_instance):
43...         if set(level.t1.dict['list']) == set(level.t2.dict['list']):
44...             return True
45...
46>>>
47>>> DeepDiff(custom1, custom2, custom_operators=[
48...     ListMatchOperator(types=[CustomClass])
49... ])
50{}
51>>>
52>>>
53>>> DeepDiff(custom2, custom3, custom_operators=[
54...     ListMatchOperator(types=[CustomClass])
55... ])
56{'dictionary_item_added': [root.dict['a'], root.dict['b']], 'dictionary_item_removed': [root.dict['c'], root.dict['d']], 'values_changed': {"root.dict['list'][3]": {'new_value': 4, 'old_value': 2}}}
57>>>
58
59```
60
61**New in 5-6-0: Dynamic ignore order function**
62
63Ignoring order when certain word in the path
64
65```python
66>>> from deepdiff import DeepDiff
67>>> t1 = {'a': [1, 2], 'b': [3, 4]}
68>>> t2 = {'a': [2, 1], 'b': [4, 3]}
69>>> DeepDiff(t1, t2, ignore_order=True)
70{}
71>>> def ignore_order_func(level):
72...     return 'a' in level.path()
73...
74>>> DeepDiff(t1, t2, ignore_order=True, ignore_order_func=ignore_order_func)
75{'values_changed': {"root['b'][0]": {'new_value': 4, 'old_value': 3}, "root['b'][1]": {'new_value': 3, 'old_value': 4}}}
76
77```
78
79
80## Installation
81
82### Install from PyPi:
83
84`pip install deepdiff`
85
86If you want to use DeepDiff from commandline:
87
88`pip install "deepdiff[cli]"`
89
90### Importing
91
92```python
93>>> from deepdiff import DeepDiff  # For Deep Difference of 2 objects
94>>> from deepdiff import grep, DeepSearch  # For finding if item exists in an object
95>>> from deepdiff import DeepHash  # For hashing objects based on their contents
96```
97
98Note: if you want to use DeepDiff via commandline, make sure to run `pip install "deepdiff[cli]"`. Then you can access the commands via:
99
100- DeepDiff
101    - `$ deep diff --help`
102- Delta
103    - `$ deep patch --help`
104- grep
105    - `$ deep grep --help`
106- extract
107    - `$ deep extract --help`
108
109# Deep Diff
110
111DeepDiff gets the difference of 2 objects.
112
113> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.6.0/diff.html)
114> - The full documentation of all modules can be found on <https://zepworks.com/deepdiff/5.6.0/>
115> - Tutorials and posts about DeepDiff can be found on <https://zepworks.com/tags/deepdiff/>
116
117## A few Examples
118
119> Note: This is just a brief overview of what DeepDiff can do. Please visit <https://zepworks.com/deepdiff/5.6.0/> for full documentation.
120
121### List difference ignoring order or duplicates
122
123```python
124>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
125>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
126>>> ddiff = DeepDiff(t1, t2, ignore_order=True)
127>>> print (ddiff)
128{}
129```
130
131### Report repetitions
132
133This flag ONLY works when ignoring order is enabled.
134
135```python
136t1 = [1, 3, 1, 4]
137t2 = [4, 4, 1]
138ddiff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
139print(ddiff)
140```
141
142which will print you:
143
144```python
145{'iterable_item_removed': {'root[1]': 3},
146  'repetition_change': {'root[0]': {'old_repeat': 2,
147                                    'old_indexes': [0, 2],
148                                    'new_indexes': [2],
149                                    'value': 1,
150                                    'new_repeat': 1},
151                        'root[3]': {'old_repeat': 1,
152                                    'old_indexes': [3],
153                                    'new_indexes': [0, 1],
154                                    'value': 4,
155                                    'new_repeat': 2}}}
156```
157
158### Exclude certain types from comparison:
159
160```python
161>>> l1 = logging.getLogger("test")
162>>> l2 = logging.getLogger("test2")
163>>> t1 = {"log": l1, 2: 1337}
164>>> t2 = {"log": l2, 2: 1337}
165>>> print(DeepDiff(t1, t2, exclude_types={logging.Logger}))
166{}
167```
168
169### Exclude part of your object tree from comparison
170
171```python
172>>> t1 = {"for life": "vegan", "ingredients": ["no meat", "no eggs", "no dairy"]}
173>>> t2 = {"for life": "vegan", "ingredients": ["veggies", "tofu", "soy sauce"]}
174>>> print (DeepDiff(t1, t2, exclude_paths={"root['ingredients']"}))
175{}
176```
177
178### Exclude Regex Paths
179
180
181You can also exclude using regular expressions by using `exclude_regex_paths` and pass a set or list of path regexes to exclude. The items in the list could be raw regex strings or compiled regex objects.
182
183```python
184>>> t1 = [{'a': 1, 'b': 2}, {'c': 4, 'b': 5}]
185>>> t2 = [{'a': 1, 'b': 3}, {'c': 4, 'b': 5}]
186>>> print(DeepDiff(t1, t2, exclude_regex_paths={r"root\[\d+\]\['b'\]"}))
187{}
188>>> exclude_path = re.compile(r"root\[\d+\]\['b'\]")
189>>> print(DeepDiff(t1, t2, exclude_regex_paths=[exclude_path]))
190{}
191```
192
193### Significant Digits
194
195Digits **after** the decimal point. Internally it uses "{:.Xf}".format(Your Number) to compare numbers where X=significant_digits
196
197```python
198>>> t1 = Decimal('1.52')
199>>> t2 = Decimal('1.57')
200>>> DeepDiff(t1, t2, significant_digits=0)
201{}
202>>> DeepDiff(t1, t2, significant_digits=1)
203{'values_changed': {'root': {'old_value': Decimal('1.52'), 'new_value': Decimal('1.57')}}}
204```
205
206### Ignore Type Number - List that contains float and integer:
207
208```py
209>>> from deepdiff import DeepDiff
210>>> from pprint import pprint
211>>> t1 = [1, 2, 3]
212>>> t2 = [1.0, 2.0, 3.0]
213>>> ddiff = DeepDiff(t1, t2)
214>>> pprint(ddiff, indent=2)
215{ 'type_changes': { 'root[0]': { 'new_type': <class 'float'>,
216                         'new_value': 1.0,
217                         'old_type': <class 'int'>,
218                         'old_value': 1},
219            'root[1]': { 'new_type': <class 'float'>,
220                         'new_value': 2.0,
221                         'old_type': <class 'int'>,
222                         'old_value': 2},
223            'root[2]': { 'new_type': <class 'float'>,
224                         'new_value': 3.0,
225                         'old_type': <class 'int'>,
226                         'old_value': 3}}}
227>>> ddiff = DeepDiff(t1, t2, ignore_type_in_groups=[(int, float)])
228{}
229```
230
231## Views
232
233Starting with DeepDiff v 3, there are two different views into your diffed data: text view (original) and tree view (new).
234
235### Text View
236
237Text view is the original and currently the default view of DeepDiff.
238
239It is called text view because the results contain texts that represent the path to the data:
240
241Example of using the text view.
242
243```python
244>>> from deepdiff import DeepDiff
245>>> t1 = {1:1, 3:3, 4:4}
246>>> t2 = {1:1, 3:3, 5:5, 6:6}
247>>> ddiff = DeepDiff(t1, t2)
248>>> print(ddiff)
249{'dictionary_item_added': {'root[5]', 'root[6]'}, 'dictionary_item_removed': {'root[4]'}}
250```
251
252So for example `ddiff['dictionary_item_removed']` is a set if strings thus this is called the text view.
253
254    The following examples are using the *default text view.*
255    The Tree View is introduced in DeepDiff v3
256    and provides traversing capabilities through your diffed data and more!
257    Read more about the Tree View at the [tree view section](#tree-view) of this page.
258
259
260### Tree View
261
262Starting the version v3 You can choose the view into the deepdiff results.
263The tree view provides you with tree objects that you can traverse through to find the parents of the objects that are diffed and the actual objects that are being diffed.
264
265
266#### Value of an item has changed (Tree View)
267
268```python
269>>> from deepdiff import DeepDiff
270>>> from pprint import pprint
271>>> t1 = {1:1, 2:2, 3:3}
272>>> t2 = {1:1, 2:4, 3:3}
273>>> ddiff_verbose0 = DeepDiff(t1, t2, verbose_level=0, view='tree')
274>>> ddiff_verbose0
275{'values_changed': {<root[2]>}}
276>>>
277>>> ddiff_verbose1 = DeepDiff(t1, t2, verbose_level=1, view='tree')
278>>> ddiff_verbose1
279{'values_changed': {<root[2] t1:2, t2:4>}}
280>>> set_of_values_changed = ddiff_verbose1['values_changed']
281>>> # since set_of_values_changed includes only one item in a set
282>>> # in order to get that one item we can:
283>>> (changed,) = set_of_values_changed
284>>> changed  # Another way to get this is to do: changed=list(set_of_values_changed)[0]
285<root[2] t1:2, t2:4>
286>>> changed.t1
2872
288>>> changed.t2
2894
290>>> # You can traverse through the tree, get to the parents!
291>>> changed.up
292<root t1:{1: 1, 2: 2,...}, t2:{1: 1, 2: 4,...}>
293```
294
295### Serialization
296
297In order to convert the DeepDiff object into a normal Python dictionary, use the to_dict() method.
298Note that to_dict will use the text view even if you did the diff in tree view.
299
300Example:
301
302```python
303>>> t1 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": [1, 2, 3]}}
304>>> t2 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": "world\n\n\nEnd"}}
305>>> ddiff = DeepDiff(t1, t2, view='tree')
306>>> ddiff.to_dict()
307{'type_changes': {"root[4]['b']": {'old_type': <class 'list'>, 'new_type': <class 'str'>, 'old_value': [1, 2, 3], 'new_value': 'world\n\n\nEnd'}}}
308```
309
310In order to do safe json serialization, use the to_json() method.
311
312Example:
313
314```python
315>>> t1 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": [1, 2, 3]}}
316>>> t2 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": "world\n\n\nEnd"}}
317>>> ddiff = DeepDiff(t1, t2, view='tree')
318>>> ddiff.to_json()
319'{"type_changes": {"root[4][\'b\']": {"old_type": "list", "new_type": "str", "old_value": [1, 2, 3], "new_value": "world\\n\\n\\nEnd"}}}'
320```
321
322
323> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.6.0/diff.html)
324> - The full documentation can be found on <https://zepworks.com/deepdiff/5.6.0/>
325
326
327# Deep Search
328
329DeepDiff comes with a utility to find the path to the item you are looking for.
330It is called DeepSearch and it has a similar interface to DeepDiff.
331
332Let's say you have a huge nested object and want to see if any item with the word `somewhere` exists in it.
333Just grep through your objects as you would in shell!
334
335```py
336from deepdiff import grep
337obj = {"long": "somewhere", "string": 2, 0: 0, "somewhere": "around"}
338ds = obj | grep("somewhere")
339print(ds)
340```
341
342Which will print:
343
344```py
345{'matched_paths': {"root['somewhere']"},
346 'matched_values': {"root['long']"}}
347```
348
349And you can pass all the same kwargs as DeepSearch to grep too:
350
351```py
352>>> obj | grep(item, verbose_level=2)
353{'matched_paths': {"root['somewhere']": 'around'}, 'matched_values': {"root['long']": 'somewhere'}}
354```
355
356> - Please take a look at the [DeepSearch docs](https://zepworks.com/deepdiff/5.6.0/dsearch.html)
357> - The full documentation can be found on <https://zepworks.com/deepdiff/5.6.0/>
358
359# Deep Hash
360(New in v4-0-0)
361
362DeepHash is designed to give you hash of ANY python object based on its contents even if the object is not considered hashable!
363DeepHash is supposed to be deterministic in order to make sure 2 objects that contain the same data, produce the same hash.
364
365> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.6.0/deephash.html)
366> - The full documentation can be found on <https://zepworks.com/deepdiff/5.6.0/>
367
368Let's say you have a dictionary object.
369
370```py
371>>> from deepdiff import DeepHash
372>>>
373>>> obj = {1: 2, 'a': 'b'}
374```
375
376If you try to hash it:
377
378```py
379>>> hash(obj)
380Traceback (most recent call last):
381  File "<stdin>", line 1, in <module>
382TypeError: unhashable type: 'dict'
383```
384
385But with DeepHash:
386
387```py
388>>> from deepdiff import DeepHash
389>>> obj = {1: 2, 'a': 'b'}
390>>> DeepHash(obj)
391{4355639248: 2468916477072481777512283587789292749, 4355639280: -35787773492556653776377555218122431491, 4358636128: -88390647972316138151822486391929534118, 4358009664: 8833996863197925870419376694314494743, 4357467952: 34150898645750099477987229399128149852}
392```
393
394So what is exactly the hash of obj in this case?
395DeepHash is calculating the hash of the obj and any other object that obj contains.
396The output of DeepHash is a dictionary of object IDs to their hashes.
397In order to get the hash of obj itself, you need to use the object (or the id of object) to get its hash:
398
399```py
400>>> hashes = DeepHash(obj)
401>>> hashes[obj]
40234150898645750099477987229399128149852
403```
404
405Which you can write as:
406
407```py
408>>> hashes = DeepHash(obj)[obj]
409```
410
411At first it might seem weird why DeepHash(obj)[obj] but remember that DeepHash(obj) is a dictionary of hashes of all other objects that obj contains too.
412
413
414> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.6.0/deephash.html)
415> - The full documentation can be found on <https://zepworks.com/deepdiff/5.6.0/>
416
417
418# Using DeepDiff in unit tests
419
420`result` is the output of the function that is being tests.
421`expected` is the expected output of the function.
422
423```python
424self.assertEqual(DeepDiff(expected, result), {})
425```
426
427or if you are using Pytest:
428
429
430```python
431assert not DeepDiff(expected, result)
432```
433
434In other words, assert that there is no diff between the expected and the result.
435
436# Difference with Json Patch
437
438Unlike [Json Patch](https://tools.ietf.org/html/rfc6902) which is designed only for Json objects, DeepDiff is designed specifically for almost all Python types. In addition to that, DeepDiff checks for type changes and attribute value changes that Json Patch does not cover since there are no such things in Json. Last but not least, DeepDiff gives you the exact path of the item(s) that were changed in Python syntax.
439
440Example in Json Patch for replacing:
441
442`{ "op": "replace", "path": "/a/b/c", "value": 42 }`
443
444Example in DeepDiff for the same operation:
445
446```python
447>>> item1 = {'a':{'b':{'c':'foo'}}}
448>>> item2 = {'a':{'b':{'c':42}}}
449>>> DeepDiff(item1, item2)
450{'type_changes': {"root['a']['b']['c']": {'old_type': <type 'str'>, 'new_value': 42, 'old_value': 'foo', 'new_type': <type 'int'>}}}
451```
452
453
454# Documentation
455
456<https://zepworks.com/deepdiff/current/>
457
458
459# Pycon 2016
460
461I was honored to give a talk about the basics of how DeepDiff does what it does at Pycon 2016. Please check out the video and let me know what you think:
462
463[Diff It To Dig It Video](https://www.youtube.com/watch?v=J5r99eJIxF4)
464And here is more info: <http://zepworks.com/blog/diff-it-to-digg-it/>
465
466# ChangeLog
467
468Please take a look at the [CHANGELOG](CHANGELOG.md) file.
469
470# Releases
471
472We use bump2version to bump and tag releases.
473
474```bash
475git checkout master && git pull
476bumpversion {patch|minor|major}
477git push && git push --tags
478```
479
480# Contribute
481
4821. Please make your PR against the dev branch
4832. Please make sure that your PR has tests. Since DeepDiff is used in many sensitive data driven projects, we strive to maintain around 100% test coverage on the code.
484
485Please run `pytest --cov=deepdiff --runslow` to see the coverage report. Note that the `--runslow` flag will run some slow tests too. In most cases you only want to run the fast tests which so you won't add the `--runslow` flag.
486
487Or to see a more user friendly version, please run: `pytest --cov=deepdiff --cov-report term-missing --runslow`.
488
489Thank you!
490
491# Authors
492
493Please take a look at the [AUTHORS](AUTHORS.md) file.
494