Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```

Expand Down
4 changes: 2 additions & 2 deletions diffsync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
32 changes: 16 additions & 16 deletions diffsync/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -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: <value>, key2: ...}, dst: {key1: <value>, key2: ...}}`,
where the `src` or `dst` dicts may be empty.
dict: of the form `{"-": {key1: <value>, key2: ...}, "+": {key1: <value>, 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.
Expand Down Expand Up @@ -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}"
Expand All @@ -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
4 changes: 4 additions & 0 deletions examples/example1/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# pylint: disable=wrong-import-order

import argparse
import pprint

from diffsync import Diff
from diffsync.logging import enable_console_logging
Expand Down Expand Up @@ -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...")
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"}}},
}


Expand Down
19 changes: 12 additions & 7 deletions tests/unit/test_diff_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand All @@ -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)\
Expand All @@ -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"}}},
}