From 89143f1655b359eb5789ffe5a3b7f8c64a3f86ac Mon Sep 17 00:00:00 2001 From: Glenn Matthews Date: Wed, 11 Nov 2020 09:59:11 -0500 Subject: [PATCH 1/3] Change _dst/_src keys to -/+ keys. Fixes #34 --- CHANGELOG.md | 1 + README.md | 6 +++--- diffsync/__init__.py | 4 ++-- diffsync/diff.py | 32 ++++++++++++++++---------------- tests/unit/test_diff.py | 2 +- tests/unit/test_diff_element.py | 19 ++++++++++++------- 6 files changed, 35 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfbfe8db..2c31be3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- #34 - in diff dicts, change keys `src`/`dst`/`_src`/`_dst` to `-` and `+` - #37 - add `sync_complete` callback, triggered on `sync_from` completion with changes. ## v1.0.0 - 2020-10-23 diff --git a/README.md b/README.md index 59117b89..e0ab9fc8 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ B = DiffSyncSystemB() A.load() B.load() -# it will show the difference between both systems +# Show the difference between both systems, that is, what would change if we applied changes from System B to System A diff_a_b = A.diff_from(B) print(diff.str()) -# it will update System A to align with the current status of system B +# Update System A to align with the current status of system B A.sync_from(B) -# it will update System B to align with the current status of system A +# Update System B to align with the current status of system A A.sync_to(B) ``` diff --git a/diffsync/__init__.py b/diffsync/__init__.py index 41ab2afc..aec892b1 100644 --- a/diffsync/__init__.py +++ b/diffsync/__init__.py @@ -554,13 +554,13 @@ def _sync_from_diff_element( log.debug("Attempting object creation") if obj: raise ObjectNotCreated(f"Failed to create {object_class.get_type()} {element.keys} - it exists!") - obj = object_class.create(diffsync=self, ids=element.keys, attrs=diffs["src"]) + obj = object_class.create(diffsync=self, ids=element.keys, attrs=diffs["+"]) log.info("Created successfully", status="success") elif element.action == "update": log.debug("Attempting object update") if not obj: raise ObjectNotUpdated(f"Failed to update {object_class.get_type()} {element.keys} - not found!") - obj = obj.update(attrs=diffs["src"]) + obj = obj.update(attrs=diffs["+"]) log.info("Updated successfully", status="success") elif element.action == "delete": log.debug("Attempting object deletion") diff --git a/diffsync/diff.py b/diffsync/diff.py index 0282bfd8..b4ed418f 100644 --- a/diffsync/diff.py +++ b/diffsync/diff.py @@ -240,27 +240,27 @@ def get_attrs_diffs(self) -> Mapping[Text, Mapping[Text, Any]]: """Get the dict of actual attribute diffs between source_attrs and dest_attrs. Returns: - dict: of the form `{src: {key1: , key2: ...}, dst: {key1: , key2: ...}}`, - where the `src` or `dst` dicts may be empty. + dict: of the form `{"-": {key1: , key2: ...}, "+": {key1: , key2: ...}}`, + where the `"-"` or `"+"` dicts may be empty. """ if self.source_attrs is not None and self.dest_attrs is not None: return { - "src": { - key: self.source_attrs[key] + "-": { + key: self.dest_attrs[key] for key in self.get_attrs_keys() if self.source_attrs[key] != self.dest_attrs[key] }, - "dst": { - key: self.dest_attrs[key] + "+": { + key: self.source_attrs[key] for key in self.get_attrs_keys() if self.source_attrs[key] != self.dest_attrs[key] }, } if self.source_attrs is None and self.dest_attrs is not None: - return {"src": {}, "dst": {key: self.dest_attrs[key] for key in self.get_attrs_keys()}} + return {"-": {key: self.dest_attrs[key] for key in self.get_attrs_keys()}, "+": {}} if self.source_attrs is not None and self.dest_attrs is None: - return {"src": {key: self.source_attrs[key] for key in self.get_attrs_keys()}, "dst": {}} - return {"src": {}, "dst": {}} + return {"-": {}, "+": {key: self.source_attrs[key] for key in self.get_attrs_keys()}} + return {"-": {}, "+": {}} def add_child(self, element: "DiffElement"): """Attach a child object of type DiffElement. @@ -304,11 +304,11 @@ def str(self, indent: int = 0): if self.source_attrs is not None and self.dest_attrs is not None: # Only print attrs that have meaning in both source and dest attrs_diffs = self.get_attrs_diffs() - for attr in attrs_diffs["src"]: + for attr in attrs_diffs["+"]: result += ( f"\n{margin} {attr}" - f" {self.source_name}({attrs_diffs['src'][attr]})" - f" {self.dest_name}({attrs_diffs['dst'][attr]})" + f" {self.source_name}({attrs_diffs['+'][attr]})" + f" {self.dest_name}({attrs_diffs['-'][attr]})" ) elif self.dest_attrs is not None: result += f" MISSING in {self.source_name}" @@ -325,10 +325,10 @@ def dict(self) -> Mapping[Text, Mapping[Text, Any]]: """Build a dictionary representation of this DiffElement and its children.""" attrs_diffs = self.get_attrs_diffs() result = {} - if attrs_diffs.get("src"): - result["_src"] = attrs_diffs["src"] - if attrs_diffs.get("dst"): - result["_dst"] = attrs_diffs["dst"] + if attrs_diffs.get("-"): + result["-"] = attrs_diffs["-"] + if attrs_diffs.get("+"): + result["+"] = attrs_diffs["+"] if self.child_diff.has_diffs(): result.update(self.child_diff.dict()) return result diff --git a/tests/unit/test_diff.py b/tests/unit/test_diff.py index 521a9ec5..c11d02d3 100644 --- a/tests/unit/test_diff.py +++ b/tests/unit/test_diff.py @@ -106,7 +106,7 @@ def test_diff_dict_with_diffs(): diff.add(intf_element) assert diff.dict() == { - "interface": {"eth0": {"_dst": {"description": "your interface"}, "_src": {"description": "my interface"}}}, + "interface": {"eth0": {"-": {"description": "your interface"}, "+": {"description": "my interface"}}}, } diff --git a/tests/unit/test_diff_element.py b/tests/unit/test_diff_element.py index a073a415..2b5ea957 100644 --- a/tests/unit/test_diff_element.py +++ b/tests/unit/test_diff_element.py @@ -93,9 +93,9 @@ def test_diff_element_str_with_diffs(): def test_diff_element_dict_with_diffs(): element = DiffElement("interface", "eth0", {"device_name": "device1", "name": "eth0"}) element.add_attrs(source={"interface_type": "ethernet", "description": "my interface"}) - assert element.dict() == {"_src": {"description": "my interface", "interface_type": "ethernet"}} + assert element.dict() == {"+": {"description": "my interface", "interface_type": "ethernet"}} element.add_attrs(dest={"description": "your interface"}) - assert element.dict() == {"_dst": {"description": "your interface"}, "_src": {"description": "my interface"}} + assert element.dict() == {"-": {"description": "your interface"}, "+": {"description": "my interface"}} def test_diff_element_children(): @@ -119,17 +119,19 @@ def test_diff_element_children(): def test_diff_element_str_with_child_diffs(): - child_element = DiffElement("interface", "eth0", {"device_name": "device1", "name": "eth0"}) parent_element = DiffElement("device", "device1", {"name": "device1"}) - parent_element.add_child(child_element) + parent_element.add_attrs(source={"role": "switch"}, dest={"role": "router"}) + child_element = DiffElement("interface", "eth0", {"device_name": "device1", "name": "eth0"}) source_attrs = {"interface_type": "ethernet", "description": "my interface"} dest_attrs = {"description": "your interface"} child_element.add_attrs(source=source_attrs, dest=dest_attrs) + parent_element.add_child(child_element) assert ( parent_element.str() == """\ device: device1 + role source(switch) dest(router) interface interface: eth0 description source(my interface) dest(your interface)\ @@ -138,13 +140,16 @@ def test_diff_element_str_with_child_diffs(): def test_diff_element_dict_with_child_diffs(): - child_element = DiffElement("interface", "eth0", {"device_name": "device1", "name": "eth0"}) parent_element = DiffElement("device", "device1", {"name": "device1"}) - parent_element.add_child(child_element) + parent_element.add_attrs(source={"role": "switch"}, dest={"role": "router"}) + child_element = DiffElement("interface", "eth0", {"device_name": "device1", "name": "eth0"}) source_attrs = {"interface_type": "ethernet", "description": "my interface"} dest_attrs = {"description": "your interface"} child_element.add_attrs(source=source_attrs, dest=dest_attrs) + parent_element.add_child(child_element) assert parent_element.dict() == { - "interface": {"eth0": {"_dst": {"description": "your interface"}, "_src": {"description": "my interface"}}}, + "-": {"role": "router"}, + "+": {"role": "switch"}, + "interface": {"eth0": {"-": {"description": "your interface"}, "+": {"description": "my interface"}}}, } From 6c52fad3700c22eb08101693307800285e5213b0 Mon Sep 17 00:00:00 2001 From: Glenn Matthews Date: Wed, 11 Nov 2020 11:02:35 -0500 Subject: [PATCH 2/3] Fix yamllint warnings --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f68c1959..5d8a935c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ python: env: global: -# yamllint disable-line rule:line-length + # yamllint disable-line rule:line-length - secure: "oSEtMKAmz3dlzrFnRLp3D/KgdSFy7XmVv6c3aSP7LXi8L2ljRrqFOB6BU3RQ6hNbSF3/bvpM9I4QKfZmOyId23Pr5UoMzaN8eL+xL8ZLkjZp0ngVZcCeXRvGwcmg4WJvVOnq3T/NoC/zwtsZbUt19yIJVVCEQqc84im90g6cLNMUulxQOvh6M/qgW4AFhAfi7lUFybl/RiWZYhvFchWifYTj7IfvZSDtin4UStJj7UApguqW4SseTZ/bmt18GSkOn9WO0sOaUSkehkT3NEMy97TLY73KgYb3LNrP47C2NPYQyyzJdb0szJ9CcVKtFjBBl5bqN5MGW/fqtqbh84Jq2GhTHNiYBcu6u/CJ+fscWYJkEWo0nNeED/ef8Vwv1M/q68IVeWsNO3+Se41WvOhMRsM8u1ek6+sHyyTNcVpGIUw4phHBsfCNiIlydWr8VpjZv9N3E4KqKRyjtpOoZElY11ZJa5rEL4D0s3JgSug958dYg/vsh+QVivNb9bbC/o9vBFqZGhWzGmNW2F3ezODZ9JcBlf1TEIZf8QPAHEO2SF5XCVRcDyByefqW28pOzwgk9Acl1/zIh5fiH/9ZAemlxjr17t4DQQbeQ/wbF6Gsmn0cYYoxjWMSrLqMD7TRQOTAYcxWAOKN/hCK/K6DS96r2CW5pU506zKMvezrskDmmX0=" before_script: - "pip install invoke" @@ -22,7 +22,7 @@ deploy: provider: "script" script: "poetry config pypi-token.pypi $PYPI_TOKEN && poetry publish --build" skip_cleanup: true - on: + "on": tags: true branch: "master" python: "3.7" From 522091f134c706320dfd8075750189d81f7bf47c Mon Sep 17 00:00:00 2001 From: Glenn Matthews Date: Thu, 12 Nov 2020 16:56:42 -0500 Subject: [PATCH 3/3] Add dict output to example1 --- examples/example1/main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/example1/main.py b/examples/example1/main.py index 87c1eb6b..fb5cff99 100755 --- a/examples/example1/main.py +++ b/examples/example1/main.py @@ -18,6 +18,7 @@ # pylint: disable=wrong-import-order import argparse +import pprint from diffsync import Diff from diffsync.logging import enable_console_logging @@ -64,6 +65,9 @@ def main(): diff_a_b = backend_a.diff_to(backend_b, diff_class=MyDiff) print(diff_a_b.str()) + print("Diffs can also be represented as a dictionary...") + pprint.pprint(diff_a_b.dict(), width=120) + print("Syncing changes from Backend A to Backend B...") backend_a.sync_to(backend_b) print("Getting updated diffs from Backend A to Backend B...")