Skip to content

Commit 21fbbba

Browse files
authored
Merge pull request #348 from networktocode/release-2.2.3
Release 2.2.3
2 parents ebc9923 + ee288d5 commit 21fbbba

File tree

4 files changed

+7
-142
lines changed

4 files changed

+7
-142
lines changed

diffsync/__init__.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"""
1717

1818
import sys
19-
from copy import deepcopy
2019
from inspect import isclass
2120
from typing import (
2221
Any,
@@ -482,19 +481,6 @@ def __init_subclass__(cls) -> None:
482481
if not isclass(value) or not issubclass(value, DiffSyncModel):
483482
raise AttributeError(f'top_level references attribute "{name}" but it is not a DiffSyncModel subclass!')
484483

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 Exception: # pylint: disable=broad-exception-caught
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-
498484
def __str__(self) -> StrType:
499485
"""String representation of an Adapter."""
500486
if self.type != self.name:

docs/admin/release_notes/version_2.2.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,9 @@ Remove Python 3.9 support as it's EOL.
2424
### Fixed
2525

2626
- [#339](https://github.com/networktocode/diffsync/issues/339) - Fixed bug with deepcopy in dunder new.
27+
28+
## [v2.2.3 (2026-03-20)](https://github.com/networktocode/diffsync/releases/tag/v2.2.3)
29+
30+
### Fixed
31+
32+
- [#346](https://github.com/networktocode/diffsync/issues/346) - Reverted addition of __new__ method to Adapter class to resolve instantiation issues.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "diffsync"
3-
version = "2.2.2"
3+
version = "2.2.3"
44
description = "Library to easily sync/diff/update 2 different data sources"
55
authors = ["Network to Code, LLC <info@networktocode.com>"]
66
license = "Apache-2.0"

tests/unit/test_diffsync.py

Lines changed: 0 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from diffsync import Adapter, DiffSyncModel
1010
from diffsync.enum import DiffSyncFlags, DiffSyncModelFlags
1111
from diffsync.exceptions import DiffClassMismatch, ObjectAlreadyExists, ObjectCrudException, ObjectNotFound
12-
from diffsync.store.local import LocalStore
1312

1413
from .conftest import BackendA, Device, Interface, PersonA, Site, TrackedDiff
1514

@@ -1142,129 +1141,3 @@ def test_diffsync_get_initial_value_order():
11421141
"interface",
11431142
"person",
11441143
]
1145-
1146-
1147-
def test_adapter_new_stores_kwargs():
1148-
"""Test that __new__ stores keyword arguments in _meta_kwargs."""
1149-
adapter = Adapter(name="test_adapter")
1150-
assert hasattr(adapter, "_meta_kwargs")
1151-
assert adapter._meta_kwargs == {"name": "test_adapter"} # pylint: disable=protected-access
1152-
1153-
1154-
def test_adapter_new_with_no_kwargs():
1155-
"""Test that __new__ works with no keyword arguments."""
1156-
adapter = Adapter()
1157-
assert hasattr(adapter, "_meta_kwargs")
1158-
assert adapter._meta_kwargs == {} # pylint: disable=protected-access
1159-
1160-
1161-
def test_adapter_new_with_multiple_kwargs():
1162-
"""Test that __new__ stores multiple keyword arguments."""
1163-
adapter = Adapter(name="test", internal_storage_engine=LocalStore)
1164-
assert adapter._meta_kwargs == { # pylint: disable=protected-access
1165-
"name": "test",
1166-
"internal_storage_engine": LocalStore,
1167-
}
1168-
1169-
1170-
def test_adapter_new_with_subclass():
1171-
"""Test that __new__ works correctly with Adapter subclasses."""
1172-
adapter = BackendA(name="test_backend")
1173-
assert hasattr(adapter, "_meta_kwargs") # pylint: disable=protected-access
1174-
assert adapter._meta_kwargs == {"name": "test_backend"} # pylint: disable=protected-access
1175-
1176-
1177-
def test_adapter_new_independent_instances():
1178-
"""Test that different Adapter instances have independent _meta_kwargs."""
1179-
adapter1 = Adapter(name="adapter1", internal_storage_engine=LocalStore)
1180-
adapter2 = Adapter(name="adapter2", internal_storage_engine=LocalStore)
1181-
1182-
assert adapter1._meta_kwargs["name"] == "adapter1" # pylint: disable=protected-access
1183-
assert adapter1._meta_kwargs["internal_storage_engine"] == LocalStore # pylint: disable=protected-access
1184-
assert adapter2._meta_kwargs["name"] == "adapter2" # pylint: disable=protected-access
1185-
assert adapter2._meta_kwargs["internal_storage_engine"] == LocalStore # pylint: disable=protected-access
1186-
1187-
1188-
class _AdapterWithExtraKwargs(Adapter):
1189-
"""Minimal Adapter subclass that accepts extra kwargs for testing __new__ serialization."""
1190-
1191-
def __init__(self, name=None, internal_storage_engine=LocalStore, **kwargs):
1192-
super().__init__(name=name, internal_storage_engine=internal_storage_engine)
1193-
1194-
1195-
def test_adapter_new_serializable_objects_are_deep_copied():
1196-
"""Test that serializable objects passed to __new__ are deep-copied into _meta_kwargs."""
1197-
mutable_config = {"host": "localhost", "port": 5432}
1198-
mutable_list = [1, 2, 3]
1199-
adapter = _AdapterWithExtraKwargs(
1200-
name="test",
1201-
config=mutable_config,
1202-
tags=mutable_list,
1203-
internal_storage_engine=LocalStore,
1204-
)
1205-
1206-
# Verify values are stored
1207-
assert adapter._meta_kwargs["config"] == {"host": "localhost", "port": 5432} # pylint: disable=protected-access
1208-
assert adapter._meta_kwargs["tags"] == [1, 2, 3] # pylint: disable=protected-access
1209-
1210-
# Mutate the original objects - _meta_kwargs should retain the original values (deep copy)
1211-
mutable_config["port"] = 9999
1212-
mutable_list.append(4)
1213-
1214-
assert adapter._meta_kwargs["config"] == {"host": "localhost", "port": 5432} # pylint: disable=protected-access
1215-
assert adapter._meta_kwargs["tags"] == [1, 2, 3] # pylint: disable=protected-access
1216-
1217-
1218-
def test_adapter_new_non_serializable_type_error_stored_as_is():
1219-
"""Test that objects raising TypeError on deepcopy are stored as-is in _meta_kwargs."""
1220-
1221-
class NonCopyableTypeError:
1222-
"""Object that raises TypeError when deep-copied (e.g. DB connection, Kafka Consumer)."""
1223-
1224-
def __deepcopy__(self, memo=None):
1225-
raise TypeError("Cannot deep copy this object")
1226-
1227-
non_copyable = NonCopyableTypeError()
1228-
adapter = _AdapterWithExtraKwargs(name="test", non_copyable=non_copyable, internal_storage_engine=LocalStore)
1229-
1230-
assert adapter._meta_kwargs["non_copyable"] is non_copyable # pylint: disable=protected-access
1231-
1232-
1233-
def test_adapter_new_non_serializable_attribute_error_stored_as_is():
1234-
"""Test that objects raising AttributeError on deepcopy are stored as-is in _meta_kwargs."""
1235-
1236-
class NonCopyableAttributeError:
1237-
"""Object that raises AttributeError when deep-copied."""
1238-
1239-
def __deepcopy__(self, memo=None):
1240-
raise AttributeError("Cannot deep copy - missing attribute")
1241-
1242-
non_copyable = NonCopyableAttributeError()
1243-
adapter = _AdapterWithExtraKwargs(name="test", non_copyable=non_copyable, internal_storage_engine=LocalStore)
1244-
1245-
assert adapter._meta_kwargs["non_copyable"] is non_copyable # pylint: disable=protected-access
1246-
1247-
1248-
def test_adapter_new_mixed_serializable_and_non_serializable_kwargs():
1249-
"""Test that __new__ handles mix of serializable and non-serializable kwargs correctly."""
1250-
1251-
class NonCopyable:
1252-
def __deepcopy__(self, memo=None):
1253-
raise TypeError("Cannot copy")
1254-
1255-
serializable_dict = {"key": "value"}
1256-
non_copyable = NonCopyable()
1257-
1258-
adapter = _AdapterWithExtraKwargs(
1259-
name="test",
1260-
config=serializable_dict,
1261-
connection=non_copyable,
1262-
internal_storage_engine=LocalStore,
1263-
)
1264-
1265-
# Serializable: deep-copied (independent copy)
1266-
assert adapter._meta_kwargs["config"] == {"key": "value"} # pylint: disable=protected-access
1267-
assert adapter._meta_kwargs["config"] is not serializable_dict
1268-
1269-
# Non-serializable: stored by reference
1270-
assert adapter._meta_kwargs["connection"] is non_copyable # pylint: disable=protected-access

0 commit comments

Comments
 (0)