diffsync 2.1.0__tar.gz → 2.2.1__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.
@@ -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,15 +1,16 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: diffsync
3
- Version: 2.1.0
3
+ Version: 2.2.1
4
4
  Summary: Library to easily sync/diff/update 2 different data sources
5
5
  License: Apache-2.0
6
6
  Keywords: source-of-truth,synchronization
7
7
  Author: Network to Code, LLC
8
8
  Author-email: info@networktocode.com
9
- Requires-Python: >=3.9,<4.0
9
+ Requires-Python: >=3.10,<3.14
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
10
12
  Classifier: License :: OSI Approved :: Apache Software License
11
13
  Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.9
13
14
  Classifier: Programming Language :: Python :: 3.10
14
15
  Classifier: Programming Language :: Python :: 3.11
15
16
  Classifier: Programming Language :: Python :: 3.12
@@ -31,7 +32,7 @@ DiffSync is a utility library that can be used to compare and synchronize differ
31
32
 
32
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.
33
34
 
34
- # Primary Use Cases
35
+ ## Primary Use Cases
35
36
 
36
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:
37
38
 
@@ -40,7 +41,7 @@ DiffSync is at its most useful when you have multiple sources or sets of data to
40
41
  - If various types of data in your data set naturally form a tree-like or parent-child relationship with other data.
41
42
  - If the different data sets have some attributes in common and other attributes that are exclusive to one or the other.
42
43
 
43
- # Overview of DiffSync
44
+ ## Overview of DiffSync
44
45
 
45
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.
46
47
 
@@ -55,7 +56,7 @@ You can also ask DiffSync to “sync” one data set onto the other, and it will
55
56
 
56
57
  ![DiffSync Sync](https://raw.githubusercontent.com/networktocode/diffsync/develop/docs/images/diffsync_sync.png "DiffSync Sync")
57
58
 
58
- # Simple Example
59
+ ## Simple Example
59
60
 
60
61
  ```python
61
62
  A = DiffSyncSystemA()
@@ -77,34 +78,51 @@ A.sync_to(B)
77
78
 
78
79
  > You may wish to peruse the `diffsync` [GitHub topic](https://github.com/topics/diffsync) for examples of projects using this library.
79
80
 
80
- # Documentation
81
+ ## Documentation
81
82
 
82
- 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:
83
84
 
84
- # 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/).
85
90
 
86
- ### Option 1: Install from PyPI.
91
+ ## Installation
87
92
 
88
- ```
89
- $ pip install diffsync
93
+ ### Option 1: Install from PyPI
94
+
95
+ ```shell
96
+ pip install diffsync
90
97
  ```
91
98
 
92
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
93
103
  ```
94
- $ pip install git+https://github.com/networktocode/diffsync.git@main
95
- ```
96
104
 
97
- # Contributing
105
+ ## Contributing
106
+
98
107
  Pull requests are welcomed and automatically built and tested against multiple versions of Python through GitHub Actions.
99
108
 
100
- 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:
101
110
 
102
- - 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.
103
112
  - pytest, coverage, and unittest for unit tests.
104
113
 
105
114
  You can ensure your contribution adheres to these checks by running `invoke tests` from the CLI.
106
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.
107
116
 
108
- # Questions
109
- 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.
110
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.
@@ -16,6 +16,7 @@ limitations under the License.
16
16
  """
17
17
 
18
18
  import sys
19
+ from copy import deepcopy
19
20
  from inspect import isclass
20
21
  from typing import (
21
22
  Any,
@@ -60,7 +61,7 @@ StrType = str
60
61
  class DiffSyncModel(BaseModel):
61
62
  """Base class for all DiffSync object models.
62
63
 
63
- 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;
64
65
  this is intentional as specific model classes may want to use these names (`type`, `keys`, `attrs`, etc.)
65
66
  as model attributes and we want to avoid any ambiguity or collisions.
66
67
 
@@ -158,9 +159,11 @@ class DiffSyncModel(BaseModel):
158
159
  raise AttributeError(f"Fields {attr_child_overlap} are included in both _attributes and _children.")
159
160
 
160
161
  def __repr__(self) -> str:
162
+ """Return a string representation of this DiffSyncModel."""
161
163
  return f'{self.get_type()} "{self.get_unique_id()}"'
162
164
 
163
165
  def __str__(self) -> str:
166
+ """Return a string representation of this DiffSyncModel."""
164
167
  return self.get_unique_id()
165
168
 
166
169
  def dict(self, **kwargs: Any) -> Dict:
@@ -305,7 +308,7 @@ class DiffSyncModel(BaseModel):
305
308
 
306
309
  @classmethod
307
310
  def get_type(cls) -> StrType:
308
- """Return the type AKA modelname of the object or the class
311
+ """Return the type AKA modelname of the object or the class.
309
312
 
310
313
  Returns:
311
314
  str: modelname of the class, used in to store all objects
@@ -447,7 +450,6 @@ class Adapter: # pylint: disable=too-many-public-methods
447
450
 
448
451
  Subclasses should be careful to call super().__init__() if they override this method.
449
452
  """
450
-
451
453
  if isinstance(internal_storage_engine, BaseStore):
452
454
  self.store = internal_storage_engine
453
455
  self.store.adapter = self
@@ -480,6 +482,19 @@ class Adapter: # pylint: disable=too-many-public-methods
480
482
  if not isclass(value) or not issubclass(value, DiffSyncModel):
481
483
  raise AttributeError(f'top_level references attribute "{name}" but it is not a DiffSyncModel subclass!')
482
484
 
485
+ def __new__(cls, **kwargs): # type: ignore[no-untyped-def]
486
+ """Document keyword arguments that were used to initialize Adapter."""
487
+ meta_kwargs = {}
488
+ for key, value in kwargs.items():
489
+ try:
490
+ meta_kwargs[key] = deepcopy(value)
491
+ except (TypeError, AttributeError):
492
+ # Some objects (e.g. Kafka Consumer, DB connections) cannot be deep copied
493
+ meta_kwargs[key] = value
494
+ instance = super().__new__(cls)
495
+ instance._meta_kwargs = meta_kwargs
496
+ return instance
497
+
483
498
  def __str__(self) -> StrType:
484
499
  """String representation of an Adapter."""
485
500
  if self.type != self.name:
@@ -487,6 +502,7 @@ class Adapter: # pylint: disable=too-many-public-methods
487
502
  return self.type
488
503
 
489
504
  def __repr__(self) -> StrType:
505
+ """Representation of an Adapter."""
490
506
  return f"<{str(self)}>"
491
507
 
492
508
  def __len__(self) -> int:
@@ -575,6 +591,7 @@ class Adapter: # pylint: disable=too-many-public-methods
575
591
  callback: Function with parameters (stage, current, total), to be called at intervals as the calculation of
576
592
  the diff and subsequent sync proceed.
577
593
  diff: An existing diff to be used rather than generating a completely new diff.
594
+
578
595
  Returns:
579
596
  Diff between origin object and source
580
597
  Raises:
@@ -619,6 +636,7 @@ class Adapter: # pylint: disable=too-many-public-methods
619
636
  callback: Function with parameters (stage, current, total), to be called at intervals as the calculation of
620
637
  the diff and subsequent sync proceed.
621
638
  diff: An existing diff that will be used when determining what needs to be synced.
639
+
622
640
  Returns:
623
641
  Diff between origin object and target
624
642
  Raises:
@@ -729,7 +747,7 @@ class Adapter: # pylint: disable=too-many-public-methods
729
747
  obj: Union[StrType, DiffSyncModel, Type[DiffSyncModel]],
730
748
  identifier: Union[StrType, Dict],
731
749
  ) -> Optional[DiffSyncModel]:
732
- """Get one object from the data store based on its unique id or get a None
750
+ """Get one object from the data store based on its unique id or get a None.
733
751
 
734
752
  Args:
735
753
  obj: DiffSyncModel class or instance, or modelname string, that defines the type of the object to retrieve
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import TYPE_CHECKING, Union, Any
18
+ from typing import TYPE_CHECKING, Any, Union
19
19
 
20
20
  if TYPE_CHECKING:
21
21
  from diffsync import DiffSyncModel
@@ -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.")
@@ -1,13 +1,13 @@
1
1
  """BaseStore module."""
2
2
 
3
- from typing import Dict, List, Tuple, Type, Union, TYPE_CHECKING, Optional, Set, Any
3
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple, Type, Union
4
+
4
5
  import structlog # type: ignore
5
6
 
6
7
  from diffsync.exceptions import ObjectNotFound
7
8
 
8
9
  if TYPE_CHECKING:
9
- from diffsync import DiffSyncModel
10
- from diffsync import Adapter
10
+ from diffsync import Adapter, DiffSyncModel
11
11
 
12
12
 
13
13
  class BaseStore:
@@ -1,12 +1,11 @@
1
1
  """LocalStore module."""
2
2
 
3
3
  from collections import defaultdict
4
- from typing import List, Type, Union, TYPE_CHECKING, Dict, Set, Any
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Set, Type, Union
5
5
 
6
- from diffsync.exceptions import ObjectNotFound, ObjectAlreadyExists
6
+ from diffsync.exceptions import ObjectAlreadyExists, ObjectNotFound
7
7
  from diffsync.store import BaseStore
8
8
 
9
-
10
9
  if TYPE_CHECKING:
11
10
  from diffsync import DiffSyncModel
12
11
 
@@ -65,7 +65,7 @@ class RedisStore(BaseStore):
65
65
  """Get the object from Redis key."""
66
66
  pickled_object = self._store.get(key)
67
67
  if pickled_object:
68
- obj_result = loads(pickled_object) # nosec
68
+ obj_result = loads(pickled_object) # noqa: S301
69
69
  obj_result.adapter = self.adapter
70
70
  return obj_result
71
71
  raise ObjectNotFound(f"{key} not present in Cache")
@@ -168,7 +168,7 @@ class RedisStore(BaseStore):
168
168
 
169
169
  existing_obj_binary = self._store.get(object_key)
170
170
  if existing_obj_binary:
171
- existing_obj = loads(existing_obj_binary) # nosec
171
+ existing_obj = loads(existing_obj_binary) # noqa: S301
172
172
  existing_obj_dict = existing_obj.dict()
173
173
 
174
174
  if existing_obj_dict != obj.dict():
@@ -16,7 +16,7 @@ limitations under the License.
16
16
  """
17
17
 
18
18
  from collections import OrderedDict
19
- from typing import Iterator, List, Dict, Optional, TypeVar, Callable, Generic
19
+ from typing import Callable, Dict, Generic, Iterator, List, Optional, TypeVar
20
20
 
21
21
  SPACE = " "
22
22
  BRANCH = "│ "
@@ -44,7 +44,7 @@ def symmetric_difference(lst1: List[T], lst2: List[T]) -> List[T]:
44
44
  class OrderedDefaultDict(OrderedDict, Generic[K, V]):
45
45
  """A combination of collections.OrderedDict and collections.DefaultDict behavior."""
46
46
 
47
- def __init__(self, dict_type: Callable[[], V]) -> None:
47
+ def __init__(self, dict_type: Callable[[], V] = dict) -> None: # type: ignore[assignment]
48
48
  """Create a new OrderedDefaultDict."""
49
49
  self.factory = dict_type
50
50
  super().__init__(self)
@@ -0,0 +1,235 @@
1
+ [tool.poetry]
2
+ name = "diffsync"
3
+ version = "2.2.1"
4
+ description = "Library to easily sync/diff/update 2 different data sources"
5
+ authors = ["Network to Code, LLC <info@networktocode.com>"]
6
+ license = "Apache-2.0"
7
+ readme = "README.md"
8
+ homepage = "https://diffsync.readthedocs.io"
9
+ repository = "https://github.com/networktocode/diffsync"
10
+ documentation = "https://diffsync.readthedocs.io"
11
+ keywords = ["source-of-truth", "synchronization"]
12
+ classifiers = [
13
+ "Intended Audience :: Developers",
14
+ "Development Status :: 5 - Production/Stable",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ ]
21
+ include = [
22
+ "CHANGELOG.md",
23
+ "LICENSE",
24
+ "README.md",
25
+ ]
26
+
27
+ [tool.poetry.dependencies]
28
+ python = ">=3.10,<3.14"
29
+ pydantic = "^2.0.0"
30
+ structlog = ">= 20.1.0"
31
+ packaging = ">= 21.3"
32
+ colorama = {version = "^0.4.3", optional = true}
33
+ redis = {version = "^4.3", optional = true}
34
+ # typing.Self introduced in 3.11
35
+ typing-extensions = { version = ">=4.0.1", python = "<3.11" }
36
+
37
+ [tool.poetry.extras]
38
+ redis = ["redis"]
39
+
40
+ [tool.poetry.group.dev.dependencies]
41
+ coverage = "*"
42
+ pytest = "*"
43
+ mock = "*"
44
+ pyyaml = ">= 6.0.1"
45
+ pylint = "*"
46
+ yamllint = "^1.35.1"
47
+ invoke = "^2.2.0"
48
+ toml = "^0.10.2"
49
+ attrs = "^23.2.0"
50
+ towncrier = ">=23.6.0,<=24.8.0"
51
+ ruff = "*"
52
+ Markdown = "*"
53
+
54
+ [tool.poetry.group.docs.dependencies]
55
+ # Rendering docs to HTML
56
+ mkdocs = "1.6.1"
57
+ # Embedding YAML files into Markdown documents as tables
58
+ markdown-data-tables = "1.0.0"
59
+ # Render custom markdown for version added/changed/remove notes
60
+ markdown-version-annotations = "1.0.1"
61
+ # Automatically generate some files as part of mkdocs build
62
+ mkdocs-gen-files = "0.5.0"
63
+ # Image lightboxing in mkdocs
64
+ mkdocs-glightbox = "0.4.0"
65
+ # Use Jinja2 templating in docs - see settings.md
66
+ mkdocs-macros-plugin = "1.3.7"
67
+ # Material for mkdocs theme
68
+ mkdocs-material = "9.6.15"
69
+ # Handle docs redirections
70
+ mkdocs-redirects = "1.2.2"
71
+ # Automatically handle index pages for docs sections
72
+ mkdocs-section-index = "0.3.10"
73
+ # Automatic documentation from sources, for MkDocs
74
+ mkdocstrings = "0.27.0"
75
+ # Python-specific extension to mkdocstrings
76
+ mkdocstrings-python = "1.13.0"
77
+ griffe = "1.1.1"
78
+ mypy = "*"
79
+ pytest-cov = "*"
80
+ pytest-structlog = "*"
81
+ coverage = {extras = ["toml"], version = "*"}
82
+ Sphinx = "^6.0.0"
83
+ m2r2 = "*"
84
+ sphinx-rtd-theme = "*"
85
+ toml = "*"
86
+ types-toml = "*"
87
+ types-redis = "*"
88
+ pytest-redis = "^2.4.0"
89
+ types-requests = "^2.28.11.15"
90
+ types-python-slugify = "^8.0.0.1"
91
+
92
+ [tool.ruff]
93
+ line-length = 120
94
+ target-version = "py310"
95
+
96
+ [tool.ruff.lint]
97
+ select = [
98
+ "D", # pydocstyle
99
+ "F", "E", "W", # flake8
100
+ "S", # bandit
101
+ "I", # isort
102
+ ]
103
+ ignore = [
104
+ # warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible.
105
+ "D203", # 1 blank line required before class docstring
106
+
107
+ # D212 is enabled by default in google convention, and complains if we have a docstring like:
108
+ # """
109
+ # My docstring is on the line after the opening quotes instead of on the same line as them.
110
+ # """
111
+ # We've discussed and concluded that we consider this to be a valid style choice.
112
+ "D212", # Multi-line docstring summary should start at the first line
113
+ "D213", # Multi-line docstring summary should start at the second line
114
+
115
+ # Produces a lot of issues in the current codebase.
116
+ "D401", # First line of docstring should be in imperative mood
117
+ "D407", # Missing dashed underline after section
118
+ "D416", # Section name ends in colon
119
+ "E501", # Line too long
120
+ ]
121
+
122
+ [tool.ruff.lint.pydocstyle]
123
+ convention = "google"
124
+
125
+ [tool.ruff.lint.per-file-ignores]
126
+ "tests/*" = [
127
+ "D",
128
+ "S"
129
+ ]
130
+
131
+ [tool.mypy]
132
+ warn_unused_configs = true
133
+ disallow_untyped_defs = true
134
+ ignore_missing_imports = true
135
+
136
+ [tool.pylint.general]
137
+ extension-pkg-whitelist = [
138
+ "pydantic"
139
+ ]
140
+
141
+ [tool.pylint.basic]
142
+ # No docstrings required for private methods (Pylint default), or for test_ functions, or for inner Meta classes.
143
+ no-docstring-rgx = "^(_|test_|Meta$)"
144
+
145
+ [tool.pylint.master]
146
+ ignore=[".venv", "tests"]
147
+
148
+ [tool.pylint.messages_control]
149
+ # Line length is enforced by Black, so pylint doesn't need to check it.
150
+ # Pylint and Black disagree about how to format multi-line arrays; Black wins.
151
+ disable = [
152
+ "line-too-long",
153
+ "duplicate-code",
154
+ "cyclic-import",
155
+ ]
156
+
157
+ [tool.pylint.miscellaneous]
158
+ # Don't flag TODO as a failure, let us commit with things that still need to be done in the code
159
+ notes = """,
160
+ FIXME,
161
+ XXX,
162
+ """
163
+
164
+ [tool.pylint.similarities]
165
+ # There's a lot of duplicate code in the examples/backend_*.py files - don't complain about it for now
166
+ min-similarity-lines = 20
167
+
168
+ [tool.pytest.ini_options]
169
+ python_paths = "./"
170
+ testpaths = [
171
+ "tests/"
172
+ ]
173
+ addopts = "-vv --doctest-modules -p no:warnings --ignore-glob='*mock*'"
174
+
175
+ [tool.towncrier]
176
+ package = "diffsync"
177
+ directory = "changes"
178
+ filename = "docs/admin/release_notes/version_X.Y.md"
179
+ template = "towncrier_template.j2"
180
+ start_string = "<!-- towncrier release notes start -->"
181
+ issue_format = "[#{issue}](https://github.com/networktocode/diffsync/issues/{issue})"
182
+
183
+ [[tool.towncrier.type]]
184
+ directory = "breaking"
185
+ name = "Breaking Changes"
186
+ showcontent = true
187
+
188
+ [[tool.towncrier.type]]
189
+ directory = "security"
190
+ name = "Security"
191
+ showcontent = true
192
+
193
+ [[tool.towncrier.type]]
194
+ directory = "added"
195
+ name = "Added"
196
+ showcontent = true
197
+
198
+ [[tool.towncrier.type]]
199
+ directory = "changed"
200
+ name = "Changed"
201
+ showcontent = true
202
+
203
+ [[tool.towncrier.type]]
204
+ directory = "deprecated"
205
+ name = "Deprecated"
206
+ showcontent = true
207
+
208
+ [[tool.towncrier.type]]
209
+ directory = "removed"
210
+ name = "Removed"
211
+ showcontent = true
212
+
213
+ [[tool.towncrier.type]]
214
+ directory = "fixed"
215
+ name = "Fixed"
216
+ showcontent = true
217
+
218
+ [[tool.towncrier.type]]
219
+ directory = "dependencies"
220
+ name = "Dependencies"
221
+ showcontent = true
222
+
223
+ [[tool.towncrier.type]]
224
+ directory = "documentation"
225
+ name = "Documentation"
226
+ showcontent = true
227
+
228
+ [[tool.towncrier.type]]
229
+ directory = "housekeeping"
230
+ name = "Housekeeping"
231
+ showcontent = true
232
+
233
+ [build-system]
234
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
235
+ build-backend = "poetry.core.masonry.api"
@@ -1,212 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
-
7
- ## [2.1.0]
8
-
9
- ### Changed
10
-
11
- - [284](https://github.com/networktocode/diffsync/pull/284) - Update pyyaml to 6.0.2 in poetry.lock by @gsnider2195
12
- - [286](https://github.com/networktocode/diffsync/pull/286) - Update GitHub Actions runner version by @mjbear
13
- - [289](https://github.com/networktocode/diffsync/pull/289) - Remove upper limit on Packaging by @jdrew82
14
-
15
- ## New Contributors
16
-
17
- - @gsnider2195 made their first contribution in https://github.com/networktocode/diffsync/pull/284
18
- - @mjbear made their first contribution in https://github.com/networktocode/diffsync/pull/286
19
- - @jdrew82 made their first contribution in https://github.com/networktocode/diffsync/pull/289
20
-
21
- ## [2.0.1]
22
-
23
- ### Changed
24
-
25
- - #276 - Removed upper version bound for `structlog` dependency
26
-
27
- ### Fixed
28
-
29
- - #281 - Properly deprecated `DiffSync` class name
30
- - #273 - Properly capitalized `DiffSync` in documentation
31
- - #273 - Removed more mentions of `DiffSync` in favor of `Adapter`
32
- - #274 - Fixed doc section title for getting started
33
- - #269 - Fixed wording for a couple of docstrings
34
- - #265 - Fixed readthedocs build
35
-
36
- ## [2.0.0]
37
-
38
- ### Changed
39
-
40
- - **BREAKING CHANGE** #236/240 - Upgrade to Pydantic v2.
41
-
42
- ## [1.10.0] - 2023-11-16
43
-
44
- ### Fixed
45
-
46
- - #249 - Fixes natural deletion order flag
47
- - #247 - Fixes underspecified typing_extensions dependency
48
-
49
- ### Changed
50
-
51
- - #247 - Deprecates Python 3.7
52
-
53
- ## [1.9.0] - 2023-10-16
54
-
55
- ### Added
56
-
57
- - #220 - Implement DiffSyncModelFlags.NATURAL_DELETION_ORDER.
58
-
59
- ### Changed
60
-
61
- - #219 - Type hinting overhaul
62
-
63
- ## [1.8.0] - 2023-04-18
64
-
65
- ### Added
66
-
67
- - #182 - Added `get_or_add_model_instance()` and `update_or_add_model_instance()` APIs.
68
- - #189 - Added note in `README.md` about running `invoke tests`.
69
- - #190 - Added note in `README.md` about running `invoke build`.
70
-
71
- ### Changed
72
-
73
- - #77/#188 - `sync_from()` and `sync_to()` now return the `Diff` that was applied.
74
- - #211 - Loosened `packaging` and `structlog` library dependency constraints for broader compatibility.
75
-
76
- ## [1.7.0] - 2022-11-03
77
-
78
- ### Changed
79
-
80
- - #176 - Remove pytest-redislite in favor of pytest-redis.
81
- - #174 - Update Dockerfile to install build-essential
82
-
83
- ### Added
84
-
85
- - #174 - Add methods to load data from dictionary and enable tree traversal
86
- - #174 - Add a `get_or_none` method to the DiffSync class
87
- - #168 - Add 'skip' counter to diff.summary()
88
- - #169/#170 - Add documentation about model processing order
89
- - #121/#140 - Add and configure renovate
90
- - #140 - Add renovate configuration validation to the CI
91
-
92
- ### Fixed
93
-
94
- - #149 - Limit redundant CI concurrency
95
-
96
- ## [1.6.0] - 2022-07-09
97
-
98
- ### Changed
99
-
100
- - #120 - Dropped support for Python 3.6, new minimum is Python 3.7
101
-
102
- ## [1.5.1] - 2022-06-30
103
-
104
- ### Added
105
-
106
- - #111 - Added example 6, regarding IP prefixes.
107
-
108
- ### Changed
109
-
110
- - #107 - Updated example 5 to use the Redis backend store.
111
-
112
- ### Fixed
113
-
114
- - #115 - Fixed ReadTheDocs rendering pipeline
115
- - #118 - Fixed a regression in `DiffSync.get(modelname, identifiers)` introduced in 1.5.0
116
-
117
- ## [1.5.0] - 2022-06-07
118
-
119
- ### Added
120
-
121
- - #106 - Add a new, optional, backend store based in Redis
122
-
123
- ## [1.4.3] - 2022-03-03
124
-
125
- ### Fixed
126
-
127
- - #101 - Revert changed introduced in #90 that affected `DiffElement.action`
128
-
129
- **NOTE**: this change is a breaking change against DiffSync 1.4.0 through 1.4.2, but was necessary to restore backward compatibility with DiffSync 1.3.x and earlier. Apologies for any inconvenience this causes.
130
-
131
- ### Changed
132
-
133
- - #103 - Update development dependencies
134
-
135
- ## [1.4.2] - 2022-02-28
136
-
137
- **WARNING** - #90 inadvertently introduced a breaking API change in DiffSync 1.4.0 through 1.4.2 (#101); this change was reverted in #102 for DiffSync 1.4.3 and later. We recommend not using this release, and moving to 1.4.3 instead.
138
-
139
- ### Fixed
140
-
141
- - #100 - Added explicit dependency on `packaging`.
142
-
143
- ## [1.4.1] - 2022-01-26
144
-
145
- **WARNING** - #90 inadvertently introduced a breaking API change in DiffSync 1.4.0 through 1.4.2 (#101); this change was reverted in #102 for DiffSync 1.4.3 and later. We recommend not using this release, and moving to 1.4.3 instead.
146
-
147
- ### Fixed
148
-
149
- - #95 - Removed optional dependencies on `sphinx`, `m2r2`, `sphinx-rtd-theme`, `toml`.
150
-
151
- ## [1.4.0] - 2022-01-24
152
-
153
- **WARNING** - #90 inadvertently introduced a breaking API change in DiffSync 1.4.0 through 1.4.2 (#101); this change was reverted in #102 for DiffSync 1.4.3 and later. We recommend not using this release, and moving to 1.4.3 instead.
154
-
155
- ### Added
156
-
157
- - #53 - Add a new example based on pynautobot and Nautobot REST API
158
- - #59 - Add proper documentation published in Read the doc
159
- - #68 - Cleanup Readme, add link to new documentation site
160
- - #70 - Add `add_or_update()` method to DiffSync class that requires a DiffSyncModel to be passed in and will attempt to add or update an existing object
161
- - #72 - Add core engine section in docs and rename example directories
162
- - #75 - Add support for Structlog v21 in addition to v20.
163
- - #80 - Add support for an existing Diff object to be passed to `sync_to()` & `sync_from()` to prevent another diff from being calculated.
164
- - #81 - Add a new example based on PeeringDB
165
- - #83 - Add support for Python 3.10
166
- - #87 - Add new model flags : `SKIP_UNMATCHED_BOTH`, `SKIP_UNMATCHED_SRC` & `SKIP_UNMATCHED_DST` to match the behavior of the global flags
167
-
168
- ### Changed
169
-
170
- - #62 - Update CI Token
171
- - #69 - Replace Travis CI with Github Actions to run unit tests
172
- - #82 - Update lock file with latest versions.
173
- - #90 - Convert list of actions (`create`, `update`, `delete`) to proper Enum
174
-
175
- ### Fixed
176
-
177
- - #51 - Update minimum Pydantic version due to security advisory GHSA-5jqp-qgf6-3pvh
178
- - #63 - Fix type in Readme
179
-
180
- ## [1.3.0] - 2021-04-07
181
-
182
- ### Added
183
-
184
- - #48 - added optional `callback` argument to `diff_from`/`diff_to`/`sync_from`/`sync_to` for use with progress reporting.
185
-
186
- ## [1.2.0] - 2020-12-08
187
-
188
- ### Added
189
-
190
- - #45 - minimum Python version lowered from 3.7 to 3.6, also now tested against Python 3.9.
191
-
192
- ## [1.1.0] - 2020-12-01
193
-
194
- ### Added
195
-
196
- - #37 - added `sync_complete` callback, triggered on `sync_from` completion with changes.
197
- - #41 - added `summary` API for Diff and DiffElement objects.
198
- - #44 - added `set_status()` and `get_status()` APIs so that DiffSyncModel implementations can provide details for create/update/delete logging
199
-
200
- ### Changed
201
-
202
- - Now requires Pydantic 1.7.2 or later
203
- - #34 - in diff dicts, changed keys `src`/`dst`/`_src`/`_dst` to `-` and `+`
204
- - #43 - `DiffSync.get_by_uids()` now raises `ObjectNotFound` if any of the provided uids cannot be located; `DiffSync.get()` raises `ObjectNotFound` or `ValueError` on failure, instead of returning `None`.
205
-
206
- ### Fixed
207
-
208
- - #44 - On CRUD failure, do not generate an extraneous "success" log message in addition to the "failed" message
209
-
210
- ## [1.0.0] - 2020-10-23
211
-
212
- Initial release
@@ -1,112 +0,0 @@
1
- [tool.poetry]
2
- name = "diffsync"
3
- version = "v2.1.0"
4
- description = "Library to easily sync/diff/update 2 different data sources"
5
- authors = ["Network to Code, LLC <info@networktocode.com>"]
6
- license = "Apache-2.0"
7
- readme = "README.md"
8
- homepage = "https://diffsync.readthedocs.io"
9
- repository = "https://github.com/networktocode/diffsync"
10
- documentation = "https://diffsync.readthedocs.io"
11
- keywords = ["source-of-truth", "synchronization"]
12
- include = [
13
- "CHANGELOG.md",
14
- "LICENSE",
15
- "README.md",
16
- ]
17
-
18
- [tool.poetry.dependencies]
19
- python = ">=3.9,<4.0"
20
- pydantic = "^2.0.0"
21
- structlog = ">= 20.1.0"
22
- packaging = ">= 21.3"
23
- colorama = {version = "^0.4.3", optional = true}
24
- redis = {version = "^4.3", optional = true}
25
- # typing.Self introduced in 3.11
26
- typing-extensions = { version = ">=4.0.1", python = "<3.11" }
27
-
28
- [tool.poetry.extras]
29
- redis = ["redis"]
30
-
31
- [tool.poetry.group.dev.dependencies]
32
- pytest = "*"
33
- pyyaml = ">= 6.0.1"
34
- black = "*"
35
- pylint = "*"
36
- pydocstyle = "*"
37
- yamllint = "*"
38
- bandit = "*"
39
- invoke = "*"
40
- flake8 = "*"
41
- mypy = "*"
42
- pytest-cov = "*"
43
- pytest-structlog = "*"
44
- coverage = {extras = ["toml"], version = "*"}
45
- Sphinx = "^6.0.0"
46
- m2r2 = "*"
47
- sphinx-rtd-theme = "*"
48
- toml = "*"
49
- types-toml = "*"
50
- types-redis = "*"
51
- pytest-redis = "^2.4.0"
52
- types-requests = "^2.28.11.15"
53
- types-python-slugify = "^8.0.0.1"
54
-
55
- [tool.black]
56
- line-length = 120
57
- include = '\.pyi?$'
58
- exclude = '''
59
- /(
60
- \.git
61
- | \.tox
62
- | \.venv
63
- | env/
64
- | _build
65
- | build
66
- | dist
67
- )/
68
- '''
69
-
70
- [tool.coverage.run]
71
- branch = true
72
-
73
- [tool.pylint.general]
74
- extension-pkg-whitelist = [
75
- "pydantic"
76
- ]
77
-
78
- [tool.pylint.basic]
79
- # No docstrings required for private methods (Pylint default), or for test_ functions.
80
- no-docstring-rgx="^(_|test_)"
81
-
82
- [tool.pylint.messages_control]
83
- # Line length is enforced by Black, so pylint doesn't need to check it.
84
- # Pylint and Black disagree about how to format multi-line arrays; Black wins.
85
- disable = """,
86
- line-too-long,
87
- """
88
-
89
- [tool.pylint.miscellaneous]
90
- # Don't flag TODO as a failure, let us commit with things that still need to be done in the code
91
- notes = """,
92
- FIXME,
93
- XXX,
94
- """
95
-
96
- [tool.pylint.similarities]
97
- # There's a lot of duplicate code in the examples/backend_*.py files - don't complain about it for now
98
- min-similarity-lines = 20
99
-
100
- [tool.pytest.ini_options]
101
- testpaths = [
102
- "tests"
103
- ]
104
-
105
- [tool.mypy]
106
- warn_unused_configs = true
107
- disallow_untyped_defs = true
108
- ignore_missing_imports = true
109
-
110
- [build-system]
111
- requires = ["poetry_core>=1.0.8"]
112
- build-backend = "poetry.core.masonry.api"
File without changes
File without changes
File without changes
File without changes
File without changes