Skip to content

Commit 4880cdc

Browse files
committed
Add example 2
1 parent e28e5a9 commit 4880cdc

File tree

4 files changed

+150
-8
lines changed

4 files changed

+150
-8
lines changed

examples/example1/README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1+
# Example 1
12

2-
3-
This is a simple example to show how diffsync can be used to compare and synchronize multiple data sources.
3+
This is a simple example to show how DiffSync can be used to compare and synchronize multiple data sources.
44

55
For this example, we have a shared model for Device and Interface defined in `models.py`
66
And we have 3 instances of DiffSync based on the same model but with different values (BackendA, BackendB & BackendC).
77

8+
First create and populate all 3 objects:
89

9-
First create and populate all 3 objects
1010
```python
1111
from backend_a import BackendA
1212
from backend_b import BackendB
@@ -27,33 +27,38 @@ c.load()
2727
print(c.str())
2828
```
2929

30-
Configure verbosity of DiffSync's structured logging to console; the default is full verbosity (all logs including debugging)
30+
Configure verbosity of DiffSync's structured logging to console; the default is full verbosity (all logs including debugging):
31+
3132
```python
3233
from diffsync.logging import enable_console_logging
3334
enable_console_logging(verbosity=0) # Show WARNING and ERROR logs only
3435
# enable_console_logging(verbosity=1) # Also include INFO logs
3536
# enable_console_logging(verbosity=2) # Also include DEBUG logs
3637
```
3738

38-
Show the differences between A and B
39+
Show the differences between A and B:
40+
3941
```python
4042
diff_a_b = a.diff_to(b)
4143
print(diff_a_b.str())
4244
```
4345

44-
Show the differences between B and C
46+
Show the differences between B and C:
47+
4548
```python
4649
diff_b_c = c.diff_from(b)
4750
print(diff_b_c.str())
4851
```
4952

50-
Synchronize A and B (update B with the contents of A)
53+
Synchronize A and B (update B with the contents of A):
54+
5155
```python
5256
a.sync_to(b)
5357
print(a.diff_to(b).str())
5458
```
5559

56-
Now A and B will show no differences
60+
Now A and B will show no differences:
61+
5762
```python
5863
diff_a_b = a.diff_to(b)
5964
print(diff_a_b.str())

examples/example2/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Example 2 - Callback Function
2+
3+
This example shows how you can set up DiffSync to invoke a callback function to update its status as a sync proceeds. This could be used to, for example, update a status bar (such as with the [tqdm](https://github.com/tqdm/tqdm) library), although here for simplicity we'll just have the callback print directly to the console.
4+
5+
```python
6+
from diffsync.logging import enable_console_logging
7+
from example2 import DiffSync1, DiffSync2, print_callback
8+
9+
enable_console_logging(verbosity=0) # Show WARNING and ERROR logs only
10+
11+
# Create a DiffSync1 instance and populate it with records numbered 1-100
12+
ds1 = DiffSync1()
13+
ds1.populate(count=100)
14+
15+
# Create a DiffSync2 instance and populate it with 100 random records in the range 1-200
16+
ds2 = DiffSync2()
17+
ds2.populate(count=100)
18+
19+
# Identify and attempt to resolve the differences between the two,
20+
# periodically invoking print_callback() as DiffSync progresses
21+
ds1.sync_to(ds2, callback=print_callback)
22+
```
23+
24+
You should see output similar to the following:
25+
26+
```
27+
diff: Processed 1/ 200 records.
28+
diff: Processed 3/ 200 records.
29+
...
30+
diff: Processed 199/ 200 records.
31+
diff: Processed 200/ 200 records.
32+
sync: Processed 1/ 134 records.
33+
sync: Processed 2/ 134 records.
34+
...
35+
sync: Processed 134/ 134 records.
36+
```
37+
38+
A few points to note:
39+
40+
- For each record in `ds1` and `ds2`, either it exists in both, exists only in `ds1`, or exists only in `ds2`.
41+
- The total number of records reported during the `"diff"` stage is the sum of the number of records in both `ds1` and `ds2`.
42+
- For this very simple set of models, the progress counter during the `"diff"` stage will increase at each step by 2 (if a corresponding pair of models is identified between`ds1` and `ds2`) or by 1 (if a model exists only in `ds1` or only in `ds2`).
43+
- The total number of records reported during the `"sync"` stage is the number of distinct records existing across `ds1` and `ds2` combined, so it will be less than the total reported during the `"diff"` stage.
44+
- By design for this example, `ds2` is populated semi-randomly with records, so the exact number reported during the `"sync"` stage may differ for you.

examples/example2/example2.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env python
2+
"""Example 2 - simple pair of DiffSync adapters and a callback function to report progress.
3+
4+
Copyright (c) 2021 Network To Code, LLC <info@networktocode.com>
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
"""
18+
import random
19+
20+
from diffsync import DiffSync, DiffSyncModel
21+
from diffsync.logging import enable_console_logging
22+
23+
24+
class Number(DiffSyncModel):
25+
"""Simple model that consists only of a number."""
26+
27+
_modelname = "number"
28+
_identifiers = ("number",)
29+
30+
number: int
31+
32+
33+
class DiffSync1(DiffSync):
34+
"""DiffSync adapter that contains a number of Numbers constructed in order."""
35+
36+
number = Number
37+
38+
top_level = ["number"]
39+
40+
def populate(self, count):
41+
"""Construct Numbers from 1 to count."""
42+
for i in range(count):
43+
self.add(Number(number=(i + 1)))
44+
45+
46+
class DiffSync2(DiffSync):
47+
"""DiffSync adapter that contains a number of Numbers spread randomly across a range."""
48+
49+
number = Number
50+
51+
top_level = ["number"]
52+
53+
def populate(self, count):
54+
"""Construct count numbers in the range (1 - 2*count)."""
55+
prev = 0
56+
for i in range(count): # pylint: disable=unused-variable
57+
num = prev + random.randint(1, 2) # nosec
58+
self.add(Number(number=num))
59+
prev = num
60+
61+
62+
def print_callback(stage, current, total):
63+
"""Callback for DiffSync; stage is "diff"/"sync", current is records processed to date, total is self-evident."""
64+
print(f"{stage}: Processed {current:>5}/{total:>5} records.")
65+
66+
67+
def main():
68+
"""Create instances of DiffSync1 and DiffSync2 and sync them with a progress-reporting callback function."""
69+
enable_console_logging(verbosity=0) # Show WARNING and ERROR logs only
70+
71+
# Create a DiffSync1 instance and populate it with records numbered 1-100
72+
ds1 = DiffSync1()
73+
ds1.populate(count=100)
74+
75+
# Create a DiffSync2 instance and populate it with 100 random records in the range 1-200
76+
ds2 = DiffSync2()
77+
ds2.populate(count=100)
78+
79+
# Identify and attempt to resolve the differences between the two,
80+
# periodically invoking print_callback() as DiffSync progresses
81+
ds1.sync_to(ds2, callback=print_callback)
82+
83+
84+
if __name__ == "__main__":
85+
main()

tests/unit/test_examples.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,11 @@ def test_example_1():
2727
example1_main = join(example1_dir, "main.py")
2828
# Run it and make sure it doesn't raise an exception or otherwise exit with a non-zero code.
2929
subprocess.run(example1_main, cwd=example1_dir, check=True)
30+
31+
32+
def test_example_2():
33+
"""Test that the "example2" script runs successfully."""
34+
example2_dir = join(EXAMPLES, "example2")
35+
example2_main = join(example2_dir, "example2.py")
36+
# Run it and make sure it doesn't raise an exception or otherwise exit with a non-zero code.
37+
subprocess.run(example2_main, cwd=example2_dir, check=True)

0 commit comments

Comments
 (0)