+ {
+ "name": "John",
+ "age": 30 -> 31,
+ "scores": [
+ 1,
+ 2,
+ 3 -> 4
+ ],
+ "address": {
+ "city": "New York" -> "Boston",
+ "zip": "10001"
+ },
+ "new": "value"
+ }
+
+
+Colored Compact View
+--------------------
+
+For a more concise output, especially with deeply nested objects where many parts are unchanged,
+the `ColoredView` with the compact option can be used. This view is similar but collapses
+unchanged nested dictionaries to `{...}` and unchanged lists/tuples to `[...]`. To use the compact
+option do:
+
+.. code-block:: python
+
+ from deepdiff import DeepDiff
+ from deepdiff.helper import COLORED_COMPACT_VIEW
+
+ t1 = {"name": "John", "age": 30, "scores": [1, 2, 3], "address": {"city": "New York", "zip": "10001"}}
+ t2 = {"name": "John", "age": 31, "scores": [1, 2, 4], "address": {"city": "New York", "zip": "10001"}, "new": "value"}
+
+ diff = DeepDiff(t1, t2, view=COLORED_COMPACT_VIEW)
+ print(diff)
+
+Or from command line:
+
+.. code-block:: bash
+
+ deep diff --view colored_compact t1.json t2.json
+
+
+The output will look something like this:
+
+.. raw:: html
+
+
+ {
+ "name": "John",
+ "age": 30 -> 31,
+ "scores": [
+ 1,
+ 2,
+ 3 -> 4
+ ],
+ "address": {...},
+ "new": "value"
+ }
+
diff --git a/deepdiff/docstrings/commandline.rst b/deepdiff/docstrings/commandline.rst
new file mode 100644
index 00000000..e7853dd6
--- /dev/null
+++ b/deepdiff/docstrings/commandline.rst
@@ -0,0 +1,320 @@
+:doc:`/index`
+
+Command Line
+============
+
+`New in DeepDiff 5.2.0`
+
+DeepDiff provides commandline interface to a subset of functionality that it provides through its Python API.
+
+The commands are:
+
+- :ref:`deep_diff_command`
+- :ref:`deep_grep_command`
+- :ref:`deep_extract_command`
+- :ref:`deep_patch_command`
+
+
+.. _deep_diff_command:
+
+deep diff command
+-----------------
+
+Run
+
+.. code:: bash
+
+ $ deep diff
+
+to get the options:
+
+.. code-block:: bash
+
+ $ deep diff --help
+ Usage: deep diff [OPTIONS] T1 T2
+
+ Deep Diff Commandline
+
+ Deep Difference of content in files.
+ It can read csv, tsv, json, yaml, and toml files.
+
+ T1 and T2 are the path to the files to be compared with each other.
+
+ Options:
+ --cutoff-distance-for-pairs FLOAT
+ [default: 0.3]
+ --cutoff-intersection-for-pairs FLOAT
+ [default: 0.7]
+ --cache-size INTEGER [default: 0]
+ --cache-tuning-sample-size INTEGER
+ [default: 0]
+ --cache-purge-level INTEGER RANGE
+ [default: 1]
+ --create-patch [default: False]
+ --exclude-paths TEXT
+ --exclude-regex-paths TEXT
+ --math-epsilon DECIMAL
+ --get-deep-distance [default: False]
+ --group-by TEXT
+ --ignore-order [default: False]
+ --ignore-string-type-changes [default: False]
+ --ignore-numeric-type-changes [default: False]
+ --ignore-type-subclasses [default: False]
+ --ignore-string-case [default: False]
+ --ignore-nan-inequality [default: False]
+ --include-private-variables [default: False]
+ --log-frequency-in-sec INTEGER [default: 0]
+ --max-passes INTEGER [default: 10000000]
+ --max_diffs INTEGER
+ --number-format-notation [f|e] [default: f]
+ --progress-logger [info|error] [default: info]
+ --report-repetition [default: False]
+ --significant-digits INTEGER
+ --truncate-datetime [second|minute|hour|day]
+ --verbose-level INTEGER RANGE [default: 1]
+ --view [-|colored|colored_compact]
+ [default: -]
+ Format for displaying differences.
+ --help Show this message and exit.
+
+
+Example usage:
+
+Let's imagine we have t1.csv and t2.csv:
+
+.. csv-table:: t1.csv
+ :file: ../tests/fixtures/t1.csv
+ :header-rows: 1
+
+
+.. csv-table:: t2.csv
+ :file: ../tests/fixtures/t2.csv
+ :header-rows: 1
+
+We can run:
+
+.. code-block:: bash
+
+ $ deep diff t1.csv t2.csv --ignore-order
+ {'values_changed': {"root[2]['zip']": {'new_value': 90002, 'old_value': 90001}}}
+
+As you can see here the path to the item that is being changed is `root[2]['zip']` which is ok but
+what if we assume last names are unique and group by last_name?
+
+.. code-block:: bash
+
+ $ deep diff t1.csv t2.csv --ignore-order --group-by last_name
+ { 'values_changed': { "root['Molotov']['zip']": { 'new_value': 90002,
+ 'old_value': 90001}}}
+
+The path is perhaps more readable now: `root['Molotov']['zip']`. It is more clear that the zip code of Molotov has changed.
+
+.. Note::
+ The parameters in the deep diff commandline are a subset of those in :ref:`deepdiff_label` 's Python API.
+
+To output in a specific format, for example the colored compact view (see :doc:`colored_view` for output details):
+
+.. code-block:: bash
+
+ $ deep diff t1.json t2.json --view colored_compact
+
+
+.. _deep_grep_command:
+
+deep grep command
+-----------------
+
+Run
+
+.. code:: bash
+
+ $ deep grep
+
+to get the options:
+
+.. code-block:: bash
+
+ $ deep grep --help
+ Usage: deep grep [OPTIONS] ITEM PATH
+
+ Deep Grep Commandline
+
+ Grep through the contents of a file and find the path to the item.
+ It can read csv, tsv, json, yaml, and toml files.
+
+ Options:
+ -i, --ignore-case [default: False]
+ --exact-match [default: False]
+ --exclude-paths TEXT
+ --exclude-regex-paths TEXT
+ --verbose-level INTEGER RANGE [default: 1]
+ --help Show this message and exit.
+
+
+.. csv-table:: t1.csv
+ :file: ../tests/fixtures/t1.csv
+ :header-rows: 1
+
+.. code-block:: bash
+
+ $ deep grep --ignore-case james t1.csv
+ {'matched_values': ["root[2]['first_name']"]}
+
+
+.. _deep_extract_command:
+
+deep extract command
+--------------------
+
+Run
+
+.. code:: bash
+
+ $ deep extract
+
+to get the options:
+
+.. code-block:: bash
+
+ $ deep extract --help
+ Usage: deep extract [OPTIONS] PATH_INSIDE PATH
+
+ Deep Extract Commandline
+
+ Extract an item from a file based on the path that is passed. It can read
+ csv, tsv, json, yaml, and toml files.
+
+ Options:
+ --help Show this message and exit.
+
+.. csv-table:: t1.csv
+ :file: ../tests/fixtures/t1.csv
+ :header-rows: 1
+
+.. code-block:: bash
+
+ $ deep extract "root[2]['first_name']" t1.csv
+ 'James'
+
+
+.. _deep_patch_command:
+
+deep patch command
+------------------
+
+Run
+
+.. code:: bash
+
+ $ deep patch --help
+
+to get the options:
+
+.. code-block:: text
+
+ $ deep patch --help
+ Usage: deep patch [OPTIONS] PATH DELTA_PATH
+
+ Deep Patch Commandline
+
+ Patches a file based on the information in a delta file. The delta file
+ can be created by the deep diff command and passing the --create-patch
+ argument.
+
+ Deep Patch is similar to Linux's patch command. The difference is that it
+ is made for patching data. It can read csv, tsv, json, yaml, and toml
+ files.
+
+ Options:
+ -b, --backup [default: False]
+ --raise-errors [default: False]
+ --help Show this message and exit.
+
+Imagine if we have the following files:
+
+
+.. csv-table:: t1.csv
+ :file: ../tests/fixtures/t1.csv
+ :header-rows: 1
+
+.. csv-table:: t2.csv
+ :file: ../tests/fixtures/t2.csv
+ :header-rows: 1
+
+
+First we need to create a "delta" file which represents the difference between the 2 files.
+
+.. code-block:: bash
+
+ $ deep diff t1.csv t2.csv --ignore-order
+ {'values_changed': {"root[2]['zip']": {'new_value': 90002, 'old_value': 90001}}}
+
+We create the delta by using the deep diff command and passing the `--create-patch` argument.
+However since we are using `--ignore-order`, `deep diff` will ask us to also use `--report-repetition`:
+
+.. code-block:: bash
+
+ deep diff t1.csv t2.csv --ignore-order --report-repetition --create-patch
+ =}values_changed}root[2]['zip']} new_valueJ_sss.%
+
+Note that the delta is not human readable. It is meant for us to pass it into a file:
+
+.. code-block:: bash
+
+ deep diff t1.csv t2.csv --ignore-order --report-repetition --create-patch > patch1.pickle
+
+Now this delta file is ready to be applied by the `deep patch` command to any json, csv, toml or yaml file!
+It is expecting the structure of the file to be similar to the one in the csv file though.
+
+Let's look at this yaml file:
+
+`another.yaml`
+
+.. code-block:: yaml
+
+ ---
+ -
+ first_name: Joe
+ last_name: Nobody
+ zip: 90011
+ -
+ first_name: Jack
+ last_name: Doit
+ zip: 22222
+ -
+ first_name: Sara
+ last_name: Stanley
+ zip: 11111
+
+All that our delta knows is that `root[2]['zip']` has changed to `90002`.
+
+Let's apply the delta:
+
+.. code-block:: bash
+
+ deep patch --backup another.yaml patch1.pickle --raise-errors
+
+And looking at the `another.yaml` file, the zip code is indeed updated!
+
+.. code-block:: yaml
+
+ - first_name: Joe
+ last_name: Nobody
+ zip: 90011
+ - first_name: Jack
+ last_name: Doit
+ zip: 22222
+ - first_name: Sara
+ last_name: Stanley
+ zip: 90002
+
+As you can see the formatting of the yaml file is changed.
+This is due to the fact that DeepDiff loads the file into a Python dictionary, modifies it and then writes it back to disk.
+During this operation, the file loses its original formatting.
+
+.. note::
+ The deep patch command only provides a subset of what DeepDiff's :ref:`delta_label`'s Python API provides.
+ The deep patch command is minimalistic and is designed to have a similar interface to Linux's patch command
+ rather than DeepDiff's :ref:`delta_label`.
+
+Back to :doc:`/index`
diff --git a/deepdiff/docstrings/custom.rst b/deepdiff/docstrings/custom.rst
new file mode 100644
index 00000000..e2ff1d96
--- /dev/null
+++ b/deepdiff/docstrings/custom.rst
@@ -0,0 +1,440 @@
+:doc:`/index`
+
+Customized Diff
+===============
+
+.. _iterable_compare_func_label:
+
+Iterable Compare Func
+---------------------
+
+New in DeepDiff 5.5.0
+
+There are times that we want to guide DeepDiff as to what items to compare with other items. In such cases we can pass a `iterable_compare_func` that takes a function pointer to compare two items. The function takes three parameters (x, y, level) and should return `True` if it is a match, `False` if it is not a match or raise `CannotCompare` if it is unable to compare the two.
+
+
+For example take the following objects:
+
+
+Now let's define a compare_func that takes 3 parameters: x, y and level.
+
+ >>> from deepdiff import DeepDiff
+ >>> from deepdiff.helper import CannotCompare
+ >>>
+ >>> t1 = [
+ ... {
+ ... 'id': 1,
+ ... 'value': [1]
+ ... },
+ ... {
+ ... 'id': 2,
+ ... 'value': [7, 8, 1]
+ ... },
+ ... {
+ ... 'id': 3,
+ ... 'value': [7, 8],
+ ... },
+ ... ]
+ >>>
+ >>> t2 = [
+ ... {
+ ... 'id': 2,
+ ... 'value': [7, 8]
+ ... },
+ ... {
+ ... 'id': 3,
+ ... 'value': [7, 8, 1],
+ ... },
+ ... {
+ ... 'id': 1,
+ ... 'value': [1]
+ ... },
+ ... ]
+ >>>
+ >>> DeepDiff(t1, t2)
+ {'values_changed': {"root[0]['id']": {'new_value': 2, 'old_value': 1}, "root[0]['value'][0]": {'new_value': 7, 'old_value': 1}, "root[1]['id']": {'new_value': 3, 'old_value': 2}, "root[2]['id']": {'new_value': 1, 'old_value': 3}, "root[2]['value'][0]": {'new_value': 1, 'old_value': 7}}, 'iterable_item_added': {"root[0]['value'][1]": 8}, 'iterable_item_removed': {"root[2]['value'][1]": 8}}
+
+As you can see the results are different. Now items with the same ids are compared with each other.
+
+ >>> def compare_func(x, y, level=None):
+ ... try:
+ ... return x['id'] == y['id']
+ ... except Exception:
+ ... raise CannotCompare() from None
+ ...
+ >>> DeepDiff(t1, t2, iterable_compare_func=compare_func)
+ {'iterable_item_added': {"root[2]['value'][2]": 1}, 'iterable_item_removed': {"root[1]['value'][2]": 1}}
+
+If we set the verbose_level=2, we can see more details.
+
+ >>> DeepDiff(t1, t2, iterable_compare_func=compare_func, verbose_level=2)
+ {'iterable_item_added': {"root[2]['value'][2]": 1}, 'iterable_item_removed': {"root[1]['value'][2]": 1}, 'iterable_item_moved': {'root[0]': {'new_path': 'root[2]', 'value': {'id': 1, 'value': [1]}}, 'root[1]': {'new_path': 'root[0]', 'value': {'id': 2, 'value': [7, 8]}}, 'root[2]': {'new_path': 'root[1]', 'value': {'id': 3, 'value': [7, 8, 1]}}}}
+
+
+We can also use the level parameter. Levels are explained in the :ref:`tree_view_label`.
+
+For example you could use the level object to further determine if the 2 objects should be matches or not.
+
+
+ >>> t1 = {
+ ... 'path1': [],
+ ... 'path2': [
+ ... {
+ ... 'id': 1,
+ ... 'value': [1]
+ ... },
+ ... {
+ ... 'id': 2,
+ ... 'value': [7, 8, 1]
+ ... },
+ ... ]
+ ... }
+ >>>
+ >>> t2 = {
+ ... 'path1': [{'pizza'}],
+ ... 'path2': [
+ ... {
+ ... 'id': 2,
+ ... 'value': [7, 8, 1]
+ ... },
+ ... {
+ ... 'id': 1,
+ ... 'value': [1, 2]
+ ... },
+ ... ]
+ ... }
+ >>>
+ >>>
+ >>> def compare_func2(x, y, level):
+ ... if (not isinstance(x, dict) or not isinstance(y, dict)):
+ ... raise CannotCompare
+ ... if(level.path() == "root['path2']"):
+ ... if (x["id"] == y["id"]):
+ ... return True
+ ... return False
+ ...
+ >>>
+ >>> DeepDiff(t1, t2, iterable_compare_func=compare_func2)
+ {'iterable_item_added': {"root['path1'][0]": {'pizza'}, "root['path2'][0]['value'][1]": 2}}
+
+
+.. note::
+
+ The level parameter of the iterable_compare_func is only used when ignore_order=False which is the default value for ignore_order.
+
+
+.. _custom_operators_label:
+
+Custom Operators
+----------------
+
+Whether two objects are different or not largely depends on the context. For example, apples and bananas are the same
+if you are considering whether they are fruits or not.
+
+In that case, you can pass a *custom_operators* for the job.
+
+Custom operators give you a lot of power. In the following examples, we explore various use cases such as:
+
+- Making DeepDiff report the L2 Distance of items
+- Only include specific paths in diffing
+- Making DeepDiff stop diffing once we find the first diff.
+
+You can use one of the predefined custom operators that come with DeepDiff. Or you can define one yourself.
+
+
+Built-In Custom Operators
+
+.. _prefix_or_suffix_operator_label:
+
+PrefixOrSuffixOperator
+......................
+
+
+This operator will skip strings that are suffix or prefix of each other.
+
+For example when this operator is used, the two strings of "joe" and "joe's car" will not be reported as different.
+
+ >>> from deepdiff import DeepDiff
+ >>> from deepdiff.operator import PrefixOrSuffixOperator
+ >>> t1 = {
+ ... "key1": ["foo", "bar's food", "jack", "joe"]
+ ... }
+ >>> t2 = {
+ ... "key1": ["foo", "bar", "jill", "joe'car"]
+ ... }
+ >>>
+ >>> DeepDiff(t1, t2)
+ {'values_changed': {"root['key1'][1]": {'new_value': 'bar', 'old_value': "bar's food"}, "root['key1'][2]": {'new_value': 'jill', 'old_value': 'jack'}, "root['key1'][3]": {'new_value': "joe'car", 'old_value': 'joe'}}}
+ >>> DeepDiff(t1, t2, custom_operators=[
+ ... PrefixOrSuffixOperator()
+ ... ])
+ >>>
+ {'values_changed': {"root['key1'][2]": {'new_value': 'jill', 'old_value': 'jack'}}}
+
+
+
+
+Define A Custom Operator
+------------------------
+
+
+To define a custom operator, you just need to inherit *BaseOperator* or *BaseOperatorPlus*.
+
+ - *BaseOperatorPlus* is our new base operator that can be subclassed and provides the structure to build any custom operator.
+ - *BaseOperator* is our older base class for creating custom operators. It was designed mainly for simple string based regex comparison.
+
+
+Base Operator Plus
+..................
+
+*BaseOperatorPlus* is our new base operator that can be subclassed and provides the structure to build any custom operator.
+
+.. code-block:: python
+
+ class BaseOperatorPlus(metaclass=ABCMeta):
+
+ @abstractmethod
+ def match(self, level) -> bool:
+ """
+ Given a level which includes t1 and t2 in the tree view, is this operator a good match to compare t1 and t2?
+ If yes, we will run the give_up_diffing to compare t1 and t2 for this level.
+ """
+ pass
+
+ @abstractmethod
+ def give_up_diffing(self, level, diff_instance: "DeepDiff") -> bool:
+ """
+ Given a level which includes t1 and t2 in the tree view, and the "distance" between l1 and l2.
+ do we consider t1 and t2 to be equal or not. The distance is a number between zero to one and is calculated by DeepDiff to measure how similar objects are.
+ """
+
+ @abstractmethod
+ def normalize_value_for_hashing(self, parent: Any, obj: Any) -> Any:
+ """
+ You can use this function to normalize values for ignore_order=True
+
+ For example, you may want to turn all the words to be lowercase. Then you return obj.lower()
+ """
+ pass
+
+
+**Example 1: We don't care about the exact GUID values. As long as pairs of strings match GUID regex, we want them to be considered as equals**
+
+ >>> import re
+ ... from typing import Any
+ ... from deepdiff import DeepDiff
+ ... from deepdiff.operator import BaseOperatorPlus
+ ...
+ ...
+ ... d1 = {
+ ... "Name": "SUB_OBJECT_FILES",
+ ... "Values": {
+ ... "Value": [
+ ... "{f254498b-b752-4f35-bef5-6f1844b61eb7}",
+ ... "{7fb2a550-1849-45c0-b273-9aa5e4eb9f2b}",
+ ... "{a9cbecc0-21dc-49ce-8b2c-d36352dae139}"
+ ... ]
+ ... }
+ ... }
+ ...
+ ... d2 = {
+ ... "Name": "SUB_OBJECT_FILES",
+ ... "Values": {
+ ... "Value": [
+ ... "{e5d18917-1a2c-4abe-b601-8ec002629953}",
+ ... "{ea71ba1f-1339-4fae-bc28-a9ce9b8a8c67}",
+ ... "{66bb6192-9cd2-4074-8be1-f2ac52877c70}",
+ ... ]
+ ... }
+ ... }
+ ...
+ ...
+ ... class RemoveGUIDsOperator(BaseOperatorPlus):
+ ... _pattern = r"[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"
+ ... _substitute = "guid"
+ ...
+ ... def match(self, level) -> bool:
+ ... return isinstance(level.t1, str) and isinstance(level.t2, str)
+ ...
+ ... @classmethod
+ ... def _remove_pattern(cls, t: str):
+ ... return re.sub(cls._pattern, cls._substitute, t)
+ ...
+ ... def give_up_diffing(self, level, diff_instance):
+ ... t1 = self._remove_pattern(level.t1)
+ ... t2 = self._remove_pattern(level.t2)
+ ... return t1 == t2
+ ...
+ ... def normalize_value_for_hashing(self, parent: Any, obj: Any) -> Any:
+ ... """
+ ... Used for ignore_order=True
+ ... """
+ ... if isinstance(obj, str):
+ ... return self._remove_pattern(obj)
+ ... return obj
+ ...
+ ...
+ ... operator = RemoveGUIDsOperator()
+ ...
+ >>> diff1 = DeepDiff(d1, d2, custom_operators=[operator], log_stacktrace=True)
+ ... diff1
+ {}
+ >>> diff2 = DeepDiff(d1, d2, ignore_order=True, custom_operators=[operator], log_stacktrace=True)
+ ... diff2
+ {}
+
+
+
+Base Operator
+.............
+
+*BaseOperator* is our older base class for creating custom operators. It was designed mainly for simple string based regex comparison.
+
+
+.. code-block:: python
+
+ class BaseOperator:
+
+ def __init__(self, regex_paths:Optional[List[str]]=None, types:Optional[List[type]]=None):
+ if regex_paths:
+ self.regex_paths = convert_item_or_items_into_compiled_regexes_else_none(regex_paths)
+ else:
+ self.regex_paths = None
+ self.types = types
+
+ def match(self, level) -> bool:
+ if self.regex_paths:
+ for pattern in self.regex_paths:
+ matched = re.search(pattern, level.path()) is not None
+ if matched:
+ return True
+ if self.types:
+ for type_ in self.types:
+ if isinstance(level.t1, type_) and isinstance(level.t2, type_):
+ return True
+ return False
+
+ def give_up_diffing(self, level, diff_instance) -> bool:
+ raise NotImplementedError('Please implement the diff function.')
+
+
+
+**Example 2: An operator that mapping L2:distance as diff criteria and reports the distance**
+
+ >>> import math
+ >>>
+ >>> from typing import List
+ >>> from deepdiff import DeepDiff
+ >>> from deepdiff.operator import BaseOperator
+ >>>
+ >>>
+ >>> class L2DistanceDifferWithPreventDefault(BaseOperator):
+ ... def __init__(self, regex_paths: List[str], distance_threshold: float):
+ ... super().__init__(regex_paths)
+ ... self.distance_threshold = distance_threshold
+ ... def _l2_distance(self, c1, c2):
+ ... return math.sqrt(
+ ... (c1["x"] - c2["x"]) ** 2 + (c1["y"] - c2["y"]) ** 2
+ ... )
+ ... def give_up_diffing(self, level, diff_instance):
+ ... l2_distance = self._l2_distance(level.t1, level.t2)
+ ... if l2_distance > self.distance_threshold:
+ ... diff_instance.custom_report_result('distance_too_far', level, {
+ ... "l2_distance": l2_distance
+ ... })
+ ... return True
+ ...
+ >>>
+ >>> t1 = {
+ ... "coordinates": [
+ ... {"x": 5, "y": 5},
+ ... {"x": 8, "y": 8}
+ ... ]
+ ... }
+ >>>
+ >>> t2 = {
+ ... "coordinates": [
+ ... {"x": 6, "y": 6},
+ ... {"x": 88, "y": 88}
+ ... ]
+ ... }
+ >>> DeepDiff(t1, t2, custom_operators=[L2DistanceDifferWithPreventDefault(
+ ... ["^root\\['coordinates'\\]\\[\\d+\\]$"],
+ ... 1
+ ... )])
+ {'distance_too_far': {"root['coordinates'][0]": {'l2_distance': 1.4142135623730951}, "root['coordinates'][1]": {'l2_distance': 113.13708498984761}}}
+
+
+**Example 3: If the objects are subclasses of a certain type, only compare them if their list attributes are not equal sets**
+
+ >>> class CustomClass:
+ ... def __init__(self, d: dict, l: list):
+ ... self.dict = d
+ ... self.dict['list'] = l
+ ...
+ >>>
+ >>> custom1 = CustomClass(d=dict(a=1, b=2), l=[1, 2, 3])
+ >>> custom2 = CustomClass(d=dict(c=3, d=4), l=[1, 2, 3, 2])
+ >>> custom3 = CustomClass(d=dict(a=1, b=2), l=[1, 2, 3, 4])
+ >>>
+ >>>
+ >>> class ListMatchOperator(BaseOperator):
+ ... def give_up_diffing(self, level, diff_instance):
+ ... if set(level.t1.dict['list']) == set(level.t2.dict['list']):
+ ... return True
+ ...
+ >>>
+ >>> DeepDiff(custom1, custom2, custom_operators=[
+ ... ListMatchOperator(types=[CustomClass])
+ ... ])
+ {}
+ >>>
+ >>>
+ >>> DeepDiff(custom2, custom3, custom_operators=[
+ ... ListMatchOperator(types=[CustomClass])
+ ... ])
+ {'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}}}
+ >>>
+
+**Example 4: Only diff certain paths**
+
+ >>> from deepdiff import DeepDiff
+ >>> class MyOperator:
+ ... def __init__(self, include_paths):
+ ... self.include_paths = include_paths
+ ... def match(self, level) -> bool:
+ ... return True
+ ... def give_up_diffing(self, level, diff_instance) -> bool:
+ ... return level.path() not in self.include_paths
+ ...
+ >>>
+ >>> t1 = {'a': [10, 11], 'b': [20, 21], 'c': [30, 31]}
+ >>> t2 = {'a': [10, 22], 'b': [20, 33], 'c': [30, 44]}
+ >>>
+ >>> DeepDiff(t1, t2, custom_operators=[
+ ... MyOperator(include_paths="root['a'][1]")
+ ... ])
+ {'values_changed': {"root['a'][1]": {'new_value': 22, 'old_value': 11}}}
+
+**Example 5: Give up further diffing once the first diff is found**
+
+Sometimes all you care about is that there is a difference between 2 objects and not all the details of what exactly is different.
+In that case you may want to stop diffing as soon as the first diff is found.
+
+ >>> from deepdiff import DeepDiff
+ >>> class MyOperator:
+ ... def match(self, level) -> bool:
+ ... return True
+ ... def give_up_diffing(self, level, diff_instance) -> bool:
+ ... return any(diff_instance.tree.values())
+ ...
+ >>> t1 = [[1, 2], [3, 4], [5, 6]]
+ >>> t2 = [[1, 3], [3, 5], [5, 7]]
+ >>>
+ >>> DeepDiff(t1, t2, custom_operators=[
+ ... MyOperator()
+ ... ])
+ {'values_changed': {'root[0][1]': {'new_value': 3, 'old_value': 2}}}
+
+
+Back to :doc:`/index`
diff --git a/deepdiff/docstrings/deep_distance.rst b/deepdiff/docstrings/deep_distance.rst
new file mode 100644
index 00000000..09179b7b
--- /dev/null
+++ b/deepdiff/docstrings/deep_distance.rst
@@ -0,0 +1,119 @@
+:doc:`/index`
+
+.. _deep_distance_label:
+
+Deep Distance
+=============
+
+
+Deep Distance is the distance between 2 objects. It is a floating point number between 0 and 1. Deep Distance in concept is inspired by `Levenshtein Edit Distance
-
-
- {% if theme_logo_name|lower == 'true' %}
-
{{ project }}
- {% endif %}
-
-
{{ theme_description }}
-{% endif %} - -{% if theme_github_user and theme_github_repo %} -{% if theme_github_button|lower == 'true' %} -- -
-{% endif %} -{% endif %} - -{% if theme_travis_button|lower != 'false' %} -{% if theme_travis_button|lower == 'true' %} - {% set path = theme_github_user + '/' + theme_github_repo %} -{% else %} - {% set path = theme_travis_button %} -{% endif %} -
-
-
-
-
-
-
-
-