diffsync 2.0.1__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. {diffsync-2.0.1 → diffsync-2.2.0}/LICENSE +4 -4
  2. {diffsync-2.0.1 → diffsync-2.2.0}/PKG-INFO +40 -24
  3. {diffsync-2.0.1 → diffsync-2.2.0}/README.md +33 -16
  4. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/__init__.py +31 -18
  5. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/diff.py +5 -6
  6. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/exceptions.py +2 -1
  7. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/helpers.py +11 -9
  8. diffsync-2.2.0/diffsync/log.py +74 -0
  9. diffsync-2.2.0/diffsync/static/diffsync/docs/404.html +1505 -0
  10. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/install.html +1628 -0
  11. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/index.html +1607 -0
  12. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.0.html +1637 -0
  13. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.1.html +1678 -0
  14. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.10.html +1665 -0
  15. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.2.html +1656 -0
  16. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.3.html +1656 -0
  17. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.4.html +1738 -0
  18. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.5.html +1678 -0
  19. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.6.html +1656 -0
  20. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.7.html +1686 -0
  21. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.8.html +1671 -0
  22. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_1.9.html +1662 -0
  23. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_2.0.html +1707 -0
  24. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_2.1.html +1671 -0
  25. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/release_notes/version_2.2.html +1636 -0
  26. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/uninstall.html +1619 -0
  27. diffsync-2.2.0/diffsync/static/diffsync/docs/admin/upgrade.html +1619 -0
  28. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/_mkdocstrings.css +143 -0
  29. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/extra.css +152 -0
  30. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/favicon.ico +0 -0
  31. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/images/favicon.png +0 -0
  32. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/bundle.56ea9cef.min.js +16 -0
  33. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/bundle.56ea9cef.min.js.map +7 -0
  34. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/glightbox.min.js +1 -0
  35. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
  36. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
  37. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
  38. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
  39. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
  40. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
  41. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
  42. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
  43. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
  44. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
  45. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
  46. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
  47. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
  48. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
  49. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
  50. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
  51. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
  52. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
  53. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
  54. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
  55. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
  56. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
  57. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
  58. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
  59. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
  60. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
  61. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
  62. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
  63. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
  64. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
  65. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
  66. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
  67. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/tinyseg.js +206 -0
  68. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/lunr/wordcut.js +6708 -0
  69. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/workers/search.d50fe291.min.js +42 -0
  70. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/javascripts/workers/search.d50fe291.min.js.map +7 -0
  71. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/networktocode_bw.png +0 -0
  72. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/networktocode_logo.png +0 -0
  73. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/overrides/partials/copyright.html +21 -0
  74. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/stylesheets/glightbox.min.css +1 -0
  75. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/stylesheets/main.342714a4.min.css +1 -0
  76. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/stylesheets/main.342714a4.min.css.map +1 -0
  77. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/stylesheets/palette.06af60db.min.css +1 -0
  78. diffsync-2.2.0/diffsync/static/diffsync/docs/assets/stylesheets/palette.06af60db.min.css.map +1 -0
  79. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/__init__.html +6026 -0
  80. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/diff.html +3063 -0
  81. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/enum.html +2343 -0
  82. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/exceptions.html +2226 -0
  83. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/helpers.html +2423 -0
  84. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/log.html +1796 -0
  85. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/logging.html +1770 -0
  86. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/store/__init__.html +2934 -0
  87. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/store/local.html +2376 -0
  88. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/store/redis.html +2393 -0
  89. diffsync-2.2.0/diffsync/static/diffsync/docs/code-reference/diffsync/utils.html +2022 -0
  90. diffsync-2.2.0/diffsync/static/diffsync/docs/dev/arch_decision.html +1617 -0
  91. diffsync-2.2.0/diffsync/static/diffsync/docs/dev/contributing.html +1775 -0
  92. diffsync-2.2.0/diffsync/static/diffsync/docs/dev/dev_environment.html +1913 -0
  93. diffsync-2.2.0/diffsync/static/diffsync/docs/dev/extending.html +1617 -0
  94. diffsync-2.2.0/diffsync/static/diffsync/docs/dev/release_checklist.html +2037 -0
  95. diffsync-2.2.0/diffsync/static/diffsync/docs/generate_code_reference_pages.py +38 -0
  96. diffsync-2.2.0/diffsync/static/diffsync/docs/images/diffsync_components.png +0 -0
  97. diffsync-2.2.0/diffsync/static/diffsync/docs/images/diffsync_diff_creation.png +0 -0
  98. diffsync-2.2.0/diffsync/static/diffsync/docs/images/diffsync_sync.png +0 -0
  99. diffsync-2.2.0/diffsync/static/diffsync/docs/images/networktocode_logo.svg +150 -0
  100. diffsync-2.2.0/diffsync/static/diffsync/docs/images/preorder-tree-traversal.drawio.png +0 -0
  101. diffsync-2.2.0/diffsync/static/diffsync/docs/index.html +1906 -0
  102. diffsync-2.2.0/diffsync/static/diffsync/docs/objects.inv +0 -0
  103. diffsync-2.2.0/diffsync/static/diffsync/docs/requirements.txt +5 -0
  104. diffsync-2.2.0/diffsync/static/diffsync/docs/search/search_index.json +1 -0
  105. diffsync-2.2.0/diffsync/static/diffsync/docs/sitemap.xml +179 -0
  106. diffsync-2.2.0/diffsync/static/diffsync/docs/sitemap.xml.gz +0 -0
  107. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.diff.rst +7 -0
  108. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.enum.rst +7 -0
  109. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.exceptions.rst +7 -0
  110. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.helpers.rst +7 -0
  111. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.logging.rst +7 -0
  112. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.rst +26 -0
  113. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.store.local.rst +7 -0
  114. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.store.redis.rst +7 -0
  115. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.store.rst +14 -0
  116. diffsync-2.2.0/diffsync/static/diffsync/docs/source/api/diffsync.utils.rst +7 -0
  117. diffsync-2.2.0/diffsync/static/diffsync/docs/source/conf.py +99 -0
  118. diffsync-2.2.0/diffsync/static/diffsync/docs/source/core_engine/01-flags.html +1989 -0
  119. diffsync-2.2.0/diffsync/static/diffsync/docs/source/core_engine/02-customize-diff-class.html +1734 -0
  120. diffsync-2.2.0/diffsync/static/diffsync/docs/source/core_engine/03-store.html +1729 -0
  121. diffsync-2.2.0/diffsync/static/diffsync/docs/source/core_engine/index.rst +11 -0
  122. diffsync-2.2.0/diffsync/static/diffsync/docs/source/examples/index.rst +12 -0
  123. diffsync-2.2.0/diffsync/static/diffsync/docs/source/getting_started/01-getting-started.html +1861 -0
  124. diffsync-2.2.0/diffsync/static/diffsync/docs/source/getting_started/index.rst +5 -0
  125. diffsync-2.2.0/diffsync/static/diffsync/docs/source/index.rst +22 -0
  126. diffsync-2.2.0/diffsync/static/diffsync/docs/source/license/index.rst +5 -0
  127. diffsync-2.2.0/diffsync/static/diffsync/docs/source/overview/index.rst +7 -0
  128. diffsync-2.2.0/diffsync/static/diffsync/docs/source/static/schema-page.css +62 -0
  129. diffsync-2.2.0/diffsync/static/diffsync/docs/source/static/theme_overrides.css +13 -0
  130. diffsync-2.2.0/diffsync/static/diffsync/docs/source/template/api/module.rst_t +9 -0
  131. diffsync-2.2.0/diffsync/static/diffsync/docs/source/template/api/package.rst_t +51 -0
  132. diffsync-2.2.0/diffsync/static/diffsync/docs/source/upgrading/01-upgrading-to-2.0.html +1721 -0
  133. diffsync-2.2.0/diffsync/static/diffsync/docs/source/upgrading/index.rst +5 -0
  134. diffsync-2.2.0/diffsync/static/diffsync/docs/user/faq.html +1616 -0
  135. diffsync-2.2.0/diffsync/static/diffsync/docs/user/lib_getting_started.html +1724 -0
  136. diffsync-2.2.0/diffsync/static/diffsync/docs/user/lib_overview.html +1722 -0
  137. diffsync-2.2.0/diffsync/static/diffsync/docs/user/lib_use_cases.html +1718 -0
  138. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/store/__init__.py +4 -3
  139. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/store/local.py +2 -3
  140. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/store/redis.py +7 -6
  141. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/utils.py +3 -2
  142. diffsync-2.2.0/pyproject.toml +235 -0
  143. diffsync-2.0.1/CHANGELOG.md +0 -198
  144. diffsync-2.0.1/pyproject.toml +0 -113
  145. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/enum.py +0 -0
  146. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/logging.py +0 -0
  147. {diffsync-2.0.1 → diffsync-2.2.0}/diffsync/py.typed +0 -0
@@ -1,12 +1,12 @@
1
- Copyright 2020-2023 Network to Code <info@networktocode.com>
2
- Network to Code, LLC
1
+ Apache Software License 2.0
2
+
3
+ Copyright (c) 2025, Network to Code, LLC
3
4
 
4
5
  Licensed under the Apache License, Version 2.0 (the "License");
5
6
  you may not use this file except in compliance with the License.
6
-
7
7
  You may obtain a copy of the License at
8
8
 
9
- http://www.apache.org/licenses/LICENSE-2.0
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
10
 
11
11
  Unless required by applicable law or agreed to in writing, software
12
12
  distributed under the License is distributed on an "AS IS" BASIS,
@@ -1,29 +1,28 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: diffsync
3
- Version: 2.0.1
3
+ Version: 2.2.0
4
4
  Summary: Library to easily sync/diff/update 2 different data sources
5
- Home-page: https://diffsync.readthedocs.io
6
5
  License: Apache-2.0
7
6
  Keywords: source-of-truth,synchronization
8
7
  Author: Network to Code, LLC
9
8
  Author-email: info@networktocode.com
10
- Requires-Python: >=3.8,<4.0
9
+ Requires-Python: >=3.10,<3.14
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
11
12
  Classifier: License :: OSI Approved :: Apache Software License
12
13
  Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.8
14
- Classifier: Programming Language :: Python :: 3.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
18
17
  Classifier: Programming Language :: Python :: 3.13
19
18
  Provides-Extra: redis
20
- Requires-Dist: colorama (>=0.4.3,<0.5.0)
21
- Requires-Dist: packaging (>=21.3,<24.0)
19
+ Requires-Dist: packaging (>=21.3)
22
20
  Requires-Dist: pydantic (>=2.0.0,<3.0.0)
23
21
  Requires-Dist: redis (>=4.3,<5.0) ; extra == "redis"
24
22
  Requires-Dist: structlog (>=20.1.0)
25
23
  Requires-Dist: typing-extensions (>=4.0.1) ; python_version < "3.11"
26
24
  Project-URL: Documentation, https://diffsync.readthedocs.io
25
+ Project-URL: Homepage, https://diffsync.readthedocs.io
27
26
  Project-URL: Repository, https://github.com/networktocode/diffsync
28
27
  Description-Content-Type: text/markdown
29
28
 
@@ -33,7 +32,7 @@ DiffSync is a utility library that can be used to compare and synchronize differ
33
32
 
34
33
  For example, it can be used to compare a list of devices from 2 inventory systems and, if required, synchronize them in either direction.
35
34
 
36
- # Primary Use Cases
35
+ ## Primary Use Cases
37
36
 
38
37
  DiffSync is at its most useful when you have multiple sources or sets of data to compare and/or synchronize, and especially if any of the following are true:
39
38
 
@@ -42,7 +41,7 @@ DiffSync is at its most useful when you have multiple sources or sets of data to
42
41
  - If various types of data in your data set naturally form a tree-like or parent-child relationship with other data.
43
42
  - If the different data sets have some attributes in common and other attributes that are exclusive to one or the other.
44
43
 
45
- # Overview of DiffSync
44
+ ## Overview of DiffSync
46
45
 
47
46
  DiffSync acts as an intermediate translation layer between all of the data sets you are diffing and/or syncing. In practical terms, this means that to use DiffSync, you will define a set of data models as well as the “adapters” needed to translate between each base data source and the data model. In Python terms, the adapters will be subclasses of the `Adapter` class, and each data model class will be a subclass of the `DiffSyncModel` class.
48
47
 
@@ -57,7 +56,7 @@ You can also ask DiffSync to “sync” one data set onto the other, and it will
57
56
 
58
57
  ![DiffSync Sync](https://raw.githubusercontent.com/networktocode/diffsync/develop/docs/images/diffsync_sync.png "DiffSync Sync")
59
58
 
60
- # Simple Example
59
+ ## Simple Example
61
60
 
62
61
  ```python
63
62
  A = DiffSyncSystemA()
@@ -79,34 +78,51 @@ A.sync_to(B)
79
78
 
80
79
  > You may wish to peruse the `diffsync` [GitHub topic](https://github.com/topics/diffsync) for examples of projects using this library.
81
80
 
82
- # Documentation
81
+ ## Documentation
83
82
 
84
- The documentation is available [on Read The Docs](https://diffsync.readthedocs.io/en/latest/index.html).
83
+ Full documentation for this library can be found over on the [Diffsync Docs](https://diffsync.readthedocs.io/) website:
85
84
 
86
- # Installation
85
+ - [User Guide](https://diffsync.readthedocs.io/user/app_overview/) - Overview, Using the Library, Getting Started.
86
+ - [Administrator Guide](https://diffsync.readthedocs.io/admin/install/) - How to Install, Configure, Upgrade, or Uninstall the Library.
87
+ - [Developer Guide](https://diffsync.readthedocs.io/dev/contributing/) - Extending the Library, Code Reference, Contribution Guide.
88
+ - [Release Notes / Changelog](https://diffsync.readthedocs.io/admin/release_notes/).
89
+ - [Frequently Asked Questions](https://diffsync.readthedocs.io/user/faq/).
87
90
 
88
- ### Option 1: Install from PyPI.
91
+ ## Installation
89
92
 
90
- ```
91
- $ pip install diffsync
93
+ ### Option 1: Install from PyPI
94
+
95
+ ```shell
96
+ pip install diffsync
92
97
  ```
93
98
 
94
99
  ### Option 2: Install from a GitHub branch, such as main as shown below.
100
+
101
+ ```shell
102
+ pip install git+https://github.com/networktocode/diffsync.git@main
95
103
  ```
96
- $ pip install git+https://github.com/networktocode/diffsync.git@main
97
- ```
98
104
 
99
- # Contributing
105
+ ## Contributing
106
+
100
107
  Pull requests are welcomed and automatically built and tested against multiple versions of Python through GitHub Actions.
101
108
 
102
- The project is following Network to Code software development guidelines and are leveraging the following:
109
+ The project is following Network to Code software development guidelines and is leveraging the following:
103
110
 
104
- - Black, Pylint, Bandit, flake8, and pydocstyle, mypy for Python linting, formatting and type hint checking.
111
+ - Ruff, mypy for Python linting, formatting and type hint checking.
105
112
  - pytest, coverage, and unittest for unit tests.
106
113
 
107
114
  You can ensure your contribution adheres to these checks by running `invoke tests` from the CLI.
108
115
  The command `invoke build` builds a docker container with all the necessary dependencies (including the redis backend) locally to facilitate the execution of these tests.
109
116
 
110
- # Questions
111
- Please see the [documentation](https://diffsync.readthedocs.io/en/latest/index.html) for detailed documentation on how to use `diffsync`. For any additional questions or comments, feel free to swing by the [Network to Code slack channel](https://networktocode.slack.com/) (channel #networktocode). Sign up [here](http://slack.networktocode.com/)
117
+ ## Contributing to the Documentation
118
+
119
+ You can find all the Markdown source for the App documentation under the [`docs`](https://github.com/networktocode/diffsync/tree/develop/docs) folder in this repository. For simple edits, a Markdown capable editor is sufficient: clone the repository and edit away.
120
+
121
+ If you need to view the fully-generated documentation site, you can build it with [MkDocs](https://www.mkdocs.org/). A container hosting the documentation can be started using the `invoke` commands (details in the [Development Environment Guide](https://diffsync/dev/dev_environment/#docker-development-environment)) on [http://localhost:8001](http://localhost:8001). Using this container, as your changes to the documentation are saved, they will be automatically rebuilt and any pages currently being viewed will be reloaded in your browser.
122
+
123
+ Any PRs with fixes or improvements are very welcome!
124
+
125
+ ## Questions
126
+
127
+ For any questions or comments, please check the [FAQ](https://diffsync.readthedocs.io/en/latest/user/faq/) first. Feel free to also swing by the [Network to Code Slack](https://networktocode.slack.com/) (channel `#networktocode`), sign up [here](http://slack.networktocode.com/) if you don't have an account.
112
128
 
@@ -4,7 +4,7 @@ DiffSync is a utility library that can be used to compare and synchronize differ
4
4
 
5
5
  For example, it can be used to compare a list of devices from 2 inventory systems and, if required, synchronize them in either direction.
6
6
 
7
- # Primary Use Cases
7
+ ## Primary Use Cases
8
8
 
9
9
  DiffSync is at its most useful when you have multiple sources or sets of data to compare and/or synchronize, and especially if any of the following are true:
10
10
 
@@ -13,7 +13,7 @@ DiffSync is at its most useful when you have multiple sources or sets of data to
13
13
  - If various types of data in your data set naturally form a tree-like or parent-child relationship with other data.
14
14
  - If the different data sets have some attributes in common and other attributes that are exclusive to one or the other.
15
15
 
16
- # Overview of DiffSync
16
+ ## Overview of DiffSync
17
17
 
18
18
  DiffSync acts as an intermediate translation layer between all of the data sets you are diffing and/or syncing. In practical terms, this means that to use DiffSync, you will define a set of data models as well as the “adapters” needed to translate between each base data source and the data model. In Python terms, the adapters will be subclasses of the `Adapter` class, and each data model class will be a subclass of the `DiffSyncModel` class.
19
19
 
@@ -28,7 +28,7 @@ You can also ask DiffSync to “sync” one data set onto the other, and it will
28
28
 
29
29
  ![DiffSync Sync](https://raw.githubusercontent.com/networktocode/diffsync/develop/docs/images/diffsync_sync.png "DiffSync Sync")
30
30
 
31
- # Simple Example
31
+ ## Simple Example
32
32
 
33
33
  ```python
34
34
  A = DiffSyncSystemA()
@@ -50,33 +50,50 @@ A.sync_to(B)
50
50
 
51
51
  > You may wish to peruse the `diffsync` [GitHub topic](https://github.com/topics/diffsync) for examples of projects using this library.
52
52
 
53
- # Documentation
53
+ ## Documentation
54
54
 
55
- The documentation is available [on Read The Docs](https://diffsync.readthedocs.io/en/latest/index.html).
55
+ Full documentation for this library can be found over on the [Diffsync Docs](https://diffsync.readthedocs.io/) website:
56
56
 
57
- # Installation
57
+ - [User Guide](https://diffsync.readthedocs.io/user/app_overview/) - Overview, Using the Library, Getting Started.
58
+ - [Administrator Guide](https://diffsync.readthedocs.io/admin/install/) - How to Install, Configure, Upgrade, or Uninstall the Library.
59
+ - [Developer Guide](https://diffsync.readthedocs.io/dev/contributing/) - Extending the Library, Code Reference, Contribution Guide.
60
+ - [Release Notes / Changelog](https://diffsync.readthedocs.io/admin/release_notes/).
61
+ - [Frequently Asked Questions](https://diffsync.readthedocs.io/user/faq/).
58
62
 
59
- ### Option 1: Install from PyPI.
63
+ ## Installation
60
64
 
61
- ```
62
- $ pip install diffsync
65
+ ### Option 1: Install from PyPI
66
+
67
+ ```shell
68
+ pip install diffsync
63
69
  ```
64
70
 
65
71
  ### Option 2: Install from a GitHub branch, such as main as shown below.
72
+
73
+ ```shell
74
+ pip install git+https://github.com/networktocode/diffsync.git@main
66
75
  ```
67
- $ pip install git+https://github.com/networktocode/diffsync.git@main
68
- ```
69
76
 
70
- # Contributing
77
+ ## Contributing
78
+
71
79
  Pull requests are welcomed and automatically built and tested against multiple versions of Python through GitHub Actions.
72
80
 
73
- The project is following Network to Code software development guidelines and are leveraging the following:
81
+ The project is following Network to Code software development guidelines and is leveraging the following:
74
82
 
75
- - Black, Pylint, Bandit, flake8, and pydocstyle, mypy for Python linting, formatting and type hint checking.
83
+ - Ruff, mypy for Python linting, formatting and type hint checking.
76
84
  - pytest, coverage, and unittest for unit tests.
77
85
 
78
86
  You can ensure your contribution adheres to these checks by running `invoke tests` from the CLI.
79
87
  The command `invoke build` builds a docker container with all the necessary dependencies (including the redis backend) locally to facilitate the execution of these tests.
80
88
 
81
- # Questions
82
- Please see the [documentation](https://diffsync.readthedocs.io/en/latest/index.html) for detailed documentation on how to use `diffsync`. For any additional questions or comments, feel free to swing by the [Network to Code slack channel](https://networktocode.slack.com/) (channel #networktocode). Sign up [here](http://slack.networktocode.com/)
89
+ ## Contributing to the Documentation
90
+
91
+ You can find all the Markdown source for the App documentation under the [`docs`](https://github.com/networktocode/diffsync/tree/develop/docs) folder in this repository. For simple edits, a Markdown capable editor is sufficient: clone the repository and edit away.
92
+
93
+ If you need to view the fully-generated documentation site, you can build it with [MkDocs](https://www.mkdocs.org/). A container hosting the documentation can be started using the `invoke` commands (details in the [Development Environment Guide](https://diffsync/dev/dev_environment/#docker-development-environment)) on [http://localhost:8001](http://localhost:8001). Using this container, as your changes to the documentation are saved, they will be automatically rebuilt and any pages currently being viewed will be reloaded in your browser.
94
+
95
+ Any PRs with fixes or improvements are very welcome!
96
+
97
+ ## Questions
98
+
99
+ For any questions or comments, please check the [FAQ](https://diffsync.readthedocs.io/en/latest/user/faq/) first. Feel free to also swing by the [Network to Code Slack](https://networktocode.slack.com/) (channel `#networktocode`), sign up [here](http://slack.networktocode.com/) if you don't have an account.
@@ -14,32 +14,34 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
+
17
18
  import sys
19
+ from copy import deepcopy
18
20
  from inspect import isclass
19
21
  from typing import (
22
+ Any,
20
23
  Callable,
21
24
  ClassVar,
22
25
  Dict,
23
26
  List,
24
27
  Optional,
28
+ Set,
25
29
  Tuple,
26
30
  Type,
27
31
  Union,
28
- Any,
29
- Set,
30
32
  )
31
- from typing_extensions import deprecated
32
33
 
33
- from pydantic import ConfigDict, BaseModel, PrivateAttr
34
34
  import structlog # type: ignore
35
+ from pydantic import BaseModel, ConfigDict, PrivateAttr
36
+ from typing_extensions import deprecated
35
37
 
36
38
  from diffsync.diff import Diff
37
- from diffsync.enum import DiffSyncModelFlags, DiffSyncFlags, DiffSyncStatus
39
+ from diffsync.enum import DiffSyncFlags, DiffSyncModelFlags, DiffSyncStatus
38
40
  from diffsync.exceptions import (
39
41
  DiffClassMismatch,
40
42
  ObjectAlreadyExists,
41
- ObjectStoreWrongType,
42
43
  ObjectNotFound,
44
+ ObjectStoreWrongType,
43
45
  )
44
46
  from diffsync.helpers import DiffSyncDiffer, DiffSyncSyncer
45
47
  from diffsync.store import BaseStore
@@ -59,7 +61,7 @@ StrType = str
59
61
  class DiffSyncModel(BaseModel):
60
62
  """Base class for all DiffSync object models.
61
63
 
62
- Note that read-only APIs of this class are implemented as `get_*()` functions rather than as properties;
64
+ Note that read-only APIs of this class are implemented as `get_*()` methods rather than as properties;
63
65
  this is intentional as specific model classes may want to use these names (`type`, `keys`, `attrs`, etc.)
64
66
  as model attributes and we want to avoid any ambiguity or collisions.
65
67
 
@@ -69,7 +71,7 @@ class DiffSyncModel(BaseModel):
69
71
  be included in **at most** one of these three tuples.
70
72
  """
71
73
 
72
- _modelname: ClassVar[str] = "diffsyncmodel"
74
+ _modelname: ClassVar[str] = "diffsyncmodel" # pylint: disable=used-before-assignment
73
75
  """Name of this model, used by DiffSync to store and look up instances of this model or its equivalents.
74
76
 
75
77
  Lowercase by convention; typically corresponds to the class name, but that is not enforced.
@@ -133,16 +135,16 @@ class DiffSyncModel(BaseModel):
133
135
  """
134
136
  # Make sure that any field referenced by name actually exists on the model
135
137
  for attr in cls._identifiers:
136
- if attr not in cls.model_fields and not hasattr(cls, attr):
138
+ if attr not in cls.model_fields and not hasattr(cls, attr): # pylint: disable=unsupported-membership-test
137
139
  raise AttributeError(f"_identifiers {cls._identifiers} references missing or un-annotated attr {attr}")
138
140
  for attr in cls._shortname:
139
- if attr not in cls.model_fields:
141
+ if attr not in cls.model_fields: # pylint: disable=unsupported-membership-test
140
142
  raise AttributeError(f"_shortname {cls._shortname} references missing or un-annotated attr {attr}")
141
143
  for attr in cls._attributes:
142
- if attr not in cls.model_fields:
144
+ if attr not in cls.model_fields: # pylint: disable=unsupported-membership-test
143
145
  raise AttributeError(f"_attributes {cls._attributes} references missing or un-annotated attr {attr}")
144
146
  for attr in cls._children.values():
145
- if attr not in cls.model_fields:
147
+ if attr not in cls.model_fields: # pylint: disable=unsupported-membership-test
146
148
  raise AttributeError(f"_children {cls._children} references missing or un-annotated attr {attr}")
147
149
 
148
150
  # Any given field can only be in one of (_identifiers, _attributes, _children)
@@ -157,9 +159,11 @@ class DiffSyncModel(BaseModel):
157
159
  raise AttributeError(f"Fields {attr_child_overlap} are included in both _attributes and _children.")
158
160
 
159
161
  def __repr__(self) -> str:
162
+ """Return a string representation of this DiffSyncModel."""
160
163
  return f'{self.get_type()} "{self.get_unique_id()}"'
161
164
 
162
165
  def __str__(self) -> str:
166
+ """Return a string representation of this DiffSyncModel."""
163
167
  return self.get_unique_id()
164
168
 
165
169
  def dict(self, **kwargs: Any) -> Dict:
@@ -304,7 +308,7 @@ class DiffSyncModel(BaseModel):
304
308
 
305
309
  @classmethod
306
310
  def get_type(cls) -> StrType:
307
- """Return the type AKA modelname of the object or the class
311
+ """Return the type AKA modelname of the object or the class.
308
312
 
309
313
  Returns:
310
314
  str: modelname of the class, used in to store all objects
@@ -431,7 +435,7 @@ class Adapter: # pylint: disable=too-many-public-methods
431
435
  # modelname1 = MyModelClass1
432
436
  # modelname2 = MyModelClass2
433
437
 
434
- type: Optional[str] = None
438
+ type: Optional[str] = None # pylint: disable=used-before-assignment
435
439
  """Type of the object, will default to the name of the class if not provided."""
436
440
 
437
441
  top_level: ClassVar[List[str]] = []
@@ -446,7 +450,6 @@ class Adapter: # pylint: disable=too-many-public-methods
446
450
 
447
451
  Subclasses should be careful to call super().__init__() if they override this method.
448
452
  """
449
-
450
453
  if isinstance(internal_storage_engine, BaseStore):
451
454
  self.store = internal_storage_engine
452
455
  self.store.adapter = self
@@ -479,6 +482,13 @@ class Adapter: # pylint: disable=too-many-public-methods
479
482
  if not isclass(value) or not issubclass(value, DiffSyncModel):
480
483
  raise AttributeError(f'top_level references attribute "{name}" but it is not a DiffSyncModel subclass!')
481
484
 
485
+ def __new__(cls, **kwargs): # type: ignore[no-untyped-def]
486
+ """Document keyword arguments that were used to initialize Adapter."""
487
+ meta_kwargs = deepcopy(kwargs)
488
+ instance = super().__new__(cls)
489
+ instance._meta_kwargs = meta_kwargs
490
+ return instance
491
+
482
492
  def __str__(self) -> StrType:
483
493
  """String representation of an Adapter."""
484
494
  if self.type != self.name:
@@ -486,6 +496,7 @@ class Adapter: # pylint: disable=too-many-public-methods
486
496
  return self.type
487
497
 
488
498
  def __repr__(self) -> StrType:
499
+ """Representation of an Adapter."""
489
500
  return f"<{str(self)}>"
490
501
 
491
502
  def __len__(self) -> int:
@@ -557,7 +568,7 @@ class Adapter: # pylint: disable=too-many-public-methods
557
568
  # Synchronization between DiffSync instances
558
569
  # ------------------------------------------------------------------------------
559
570
 
560
- def sync_from( # pylint: disable=too-many-arguments
571
+ def sync_from( # pylint: disable=too-many-arguments, too-many-positional-arguments
561
572
  self,
562
573
  source: "Adapter",
563
574
  diff_class: Type[Diff] = Diff,
@@ -574,6 +585,7 @@ class Adapter: # pylint: disable=too-many-public-methods
574
585
  callback: Function with parameters (stage, current, total), to be called at intervals as the calculation of
575
586
  the diff and subsequent sync proceed.
576
587
  diff: An existing diff to be used rather than generating a completely new diff.
588
+
577
589
  Returns:
578
590
  Diff between origin object and source
579
591
  Raises:
@@ -601,7 +613,7 @@ class Adapter: # pylint: disable=too-many-public-methods
601
613
 
602
614
  return diff
603
615
 
604
- def sync_to( # pylint: disable=too-many-arguments
616
+ def sync_to( # pylint: disable=too-many-arguments, too-many-positional-arguments
605
617
  self,
606
618
  target: "Adapter",
607
619
  diff_class: Type[Diff] = Diff,
@@ -618,6 +630,7 @@ class Adapter: # pylint: disable=too-many-public-methods
618
630
  callback: Function with parameters (stage, current, total), to be called at intervals as the calculation of
619
631
  the diff and subsequent sync proceed.
620
632
  diff: An existing diff that will be used when determining what needs to be synced.
633
+
621
634
  Returns:
622
635
  Diff between origin object and target
623
636
  Raises:
@@ -728,7 +741,7 @@ class Adapter: # pylint: disable=too-many-public-methods
728
741
  obj: Union[StrType, DiffSyncModel, Type[DiffSyncModel]],
729
742
  identifier: Union[StrType, Dict],
730
743
  ) -> Optional[DiffSyncModel]:
731
- """Get one object from the data store based on its unique id or get a None
744
+ """Get one object from the data store based on its unique id or get a None.
732
745
 
733
746
  Args:
734
747
  obj: DiffSyncModel class or instance, or modelname string, that defines the type of the object to retrieve
@@ -16,11 +16,11 @@ limitations under the License.
16
16
  """
17
17
 
18
18
  from functools import total_ordering
19
- from typing import Any, Iterator, Optional, Type, List, Dict, Iterable
19
+ from typing import Any, Dict, Iterable, Iterator, List, Optional, Type
20
20
 
21
- from .exceptions import ObjectAlreadyExists
22
- from .utils import intersection, OrderedDefaultDict
23
21
  from .enum import DiffSyncActions
22
+ from .exceptions import ObjectAlreadyExists
23
+ from .utils import OrderedDefaultDict, intersection
24
24
 
25
25
  # This workaround is used because we are defining a method called `str` in our class definition, which therefore renders
26
26
  # the builtin `str` type unusable.
@@ -105,8 +105,7 @@ class Diff:
105
105
 
106
106
  Since children is already an OrderedDefaultDict, this method is not doing anything special.
107
107
  """
108
- for child in children.values():
109
- yield child
108
+ yield from children.values()
110
109
 
111
110
  def summary(self) -> Dict[StrType, int]:
112
111
  """Build a dict summary of this Diff and its child DiffElements."""
@@ -161,7 +160,7 @@ class Diff:
161
160
  class DiffElement: # pylint: disable=too-many-instance-attributes
162
161
  """DiffElement object, designed to represent a single item/object that may or may not have any diffs."""
163
162
 
164
- def __init__(
163
+ def __init__( # pylint: disable=too-many-positional-arguments
165
164
  self,
166
165
  obj_type: StrType,
167
166
  name: StrType,
@@ -14,7 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
- from typing import TYPE_CHECKING, Union, Any
17
+
18
+ from typing import TYPE_CHECKING, Any, Union
18
19
 
19
20
  if TYPE_CHECKING:
20
21
  from diffsync import DiffSyncModel
@@ -14,14 +14,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
- from collections.abc import Iterable as ABCIterable, Mapping as ABCMapping
18
- from typing import Callable, List, Optional, Tuple, Type, TYPE_CHECKING, Dict, Iterable
17
+
18
+ from collections.abc import Iterable as ABCIterable
19
+ from collections.abc import Mapping as ABCMapping
20
+ from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Optional, Tuple, Type
19
21
 
20
22
  import structlog # type: ignore
21
23
 
22
24
  from .diff import Diff, DiffElement
23
- from .enum import DiffSyncModelFlags, DiffSyncFlags, DiffSyncStatus, DiffSyncActions
24
- from .exceptions import ObjectNotFound, ObjectNotCreated, ObjectNotUpdated, ObjectNotDeleted, ObjectCrudException
25
+ from .enum import DiffSyncActions, DiffSyncFlags, DiffSyncModelFlags, DiffSyncStatus
26
+ from .exceptions import ObjectCrudException, ObjectNotCreated, ObjectNotDeleted, ObjectNotFound, ObjectNotUpdated
25
27
  from .utils import intersection, symmetric_difference
26
28
 
27
29
  if TYPE_CHECKING: # pragma: no cover
@@ -35,7 +37,7 @@ class DiffSyncDiffer: # pylint: disable=too-many-instance-attributes
35
37
  Independent from Diff and DiffElement as those classes are purely data objects, while this stores some state.
36
38
  """
37
39
 
38
- def __init__( # pylint: disable=too-many-arguments
40
+ def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
39
41
  self,
40
42
  src_diffsync: "Adapter",
41
43
  dst_diffsync: "Adapter",
@@ -114,9 +116,9 @@ class DiffSyncDiffer: # pylint: disable=too-many-instance-attributes
114
116
 
115
117
  combined_dict = {}
116
118
  for uid in dict_src:
117
- combined_dict[uid] = (dict_src.get(uid), dict_dst.get(uid))
119
+ combined_dict[uid] = (dict_src.get(uid), dict_dst.get(uid)) # type: ignore
118
120
  for uid in dict_dst:
119
- combined_dict[uid] = (dict_src.get(uid), dict_dst.get(uid))
121
+ combined_dict[uid] = (dict_src.get(uid), dict_dst.get(uid)) # type: ignore
120
122
  else:
121
123
  # In the future we might support set, etc...
122
124
  raise TypeError(f"Type combination {type(src)}/{type(dst)} is not supported... for now")
@@ -137,7 +139,7 @@ class DiffSyncDiffer: # pylint: disable=too-many-instance-attributes
137
139
 
138
140
  @staticmethod
139
141
  def validate_objects_for_diff(
140
- object_pairs: Iterable[Tuple[Optional["DiffSyncModel"], Optional["DiffSyncModel"]]]
142
+ object_pairs: Iterable[Tuple[Optional["DiffSyncModel"], Optional["DiffSyncModel"]]],
141
143
  ) -> None:
142
144
  """Check whether all DiffSyncModels in the given dictionary are valid for comparison to one another.
143
145
 
@@ -285,7 +287,7 @@ class DiffSyncSyncer: # pylint: disable=too-many-instance-attributes
285
287
  Independent from DiffSync and DiffSyncModel as those classes are purely data objects, while this stores some state.
286
288
  """
287
289
 
288
- def __init__( # pylint: disable=too-many-arguments
290
+ def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
289
291
  self,
290
292
  diff: Diff,
291
293
  src_diffsync: "Adapter",
@@ -0,0 +1,74 @@
1
+ """
2
+ Logging utilities for diffsync.
3
+
4
+ This module contains helpers and wrappers for making logging more consistent across applications.
5
+
6
+ How to use me:
7
+
8
+ >>> from diffsync.log import initialize_logging
9
+ >>> log = initialize_logging(level="debug")
10
+ """
11
+
12
+ import logging.config
13
+
14
+ APP = "diffsync"
15
+
16
+
17
+ def initialize_logging(config=None, level="INFO", filename=None):
18
+ """Initialize logging using sensible defaults.
19
+
20
+ Args:
21
+ config (dict): User provided configuration dictionary.
22
+ level (str): The level of logging for STDOUT logging.
23
+ filename (str): Where to output debug logging to file.
24
+
25
+ """
26
+ if not config:
27
+ config = {
28
+ "version": 1,
29
+ "disable_existing_loggers": False,
30
+ "formatters": {
31
+ "standard": {
32
+ "format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
33
+ "datefmt": "%Y-%m-%dT%H:%M:%S%z",
34
+ },
35
+ "debug": {
36
+ "format": "%(asctime)s [%(levelname)s] [%(module)s] [%(funcName)s] %(name)s: %(message)s",
37
+ "datefmt": "%Y-%m-%dT%H:%M:%S%z",
38
+ },
39
+ },
40
+ "handlers": {
41
+ "standard": {
42
+ "class": "logging.StreamHandler",
43
+ "formatter": "standard",
44
+ "level": level.upper(),
45
+ },
46
+ },
47
+ "loggers": {
48
+ "": {
49
+ "handlers": ["standard"],
50
+ "level": "DEBUG",
51
+ }
52
+ },
53
+ }
54
+
55
+ # If a filename is passed in, let's add a FileHandler
56
+ if filename:
57
+ config["handlers"].update(
58
+ {
59
+ "file_output": {
60
+ "class": "logging.FileHandler",
61
+ "formatter": "debug",
62
+ "level": "DEBUG",
63
+ "filename": filename,
64
+ }
65
+ }
66
+ )
67
+ config["loggers"][""]["handlers"].append("file_output")
68
+
69
+ # Configure the logging
70
+ logging.config.dictConfig(config)
71
+
72
+ # Initialize root logger and advise logging has been initialized
73
+ log = logging.getLogger(APP)
74
+ log.debug("Logging initialized.")