Skip to content

Commit

Permalink
renames 'diffsync' fields and variables to 'adapter'
Browse files Browse the repository at this point in the history
  • Loading branch information
Kircheneer committed Feb 1, 2024
1 parent 6928565 commit 615ed7c
Show file tree
Hide file tree
Showing 14 changed files with 263 additions and 222 deletions.
89 changes: 64 additions & 25 deletions diffsync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,31 @@
"""
import sys
from inspect import isclass
from typing import Callable, ClassVar, Dict, List, Optional, Tuple, Type, Union, Any, Set
from typing import (
Callable,
ClassVar,
Dict,
List,
Optional,
Tuple,
Type,
Union,
Any,
Set,
)
import warnings

from pydantic import ConfigDict, BaseModel, PrivateAttr
import structlog # type: ignore

from diffsync.diff import Diff
from diffsync.enum import DiffSyncModelFlags, DiffSyncFlags, DiffSyncStatus
from diffsync.exceptions import DiffClassMismatch, ObjectAlreadyExists, ObjectStoreWrongType, ObjectNotFound
from diffsync.exceptions import (
DiffClassMismatch,
ObjectAlreadyExists,
ObjectStoreWrongType,
ObjectNotFound,
)
from diffsync.helpers import DiffSyncDiffer, DiffSyncSyncer
from diffsync.store import BaseStore
from diffsync.store.local import LocalStore
Expand Down Expand Up @@ -97,15 +113,17 @@ class DiffSyncModel(BaseModel):
Can be set as a class attribute or an instance attribute as needed.
"""

diffsync: Optional["Adapter"] = None
"""Optional: the DiffSync instance that owns this model instance."""
adapter: Optional["Adapter"] = None
"""Optional: the Adapter instance that owns this model instance."""

_status: DiffSyncStatus = PrivateAttr(DiffSyncStatus.SUCCESS)
"""Status of the last attempt at creating/updating/deleting this model."""

_status_message: str = PrivateAttr("")
"""Message, if any, associated with the create/update/delete status value."""

model_config = ConfigDict(arbitrary_types_allowed=True)
"""Pydantic-specific configuration to allow arbitrary types on this class."""

@classmethod
def __pydantic_init_subclass__(cls, **kwargs: Any) -> None:
Expand Down Expand Up @@ -145,15 +163,15 @@ def __str__(self) -> str:
return self.get_unique_id()

def dict(self, **kwargs: Any) -> Dict:
"""Convert this DiffSyncModel to a dict, excluding the diffsync field by default as it is not serializable."""
"""Convert this DiffSyncModel to a dict, excluding the adapter field by default as it is not serializable."""
if "exclude" not in kwargs:
kwargs["exclude"] = {"diffsync"}
kwargs["exclude"] = {"adapter"}
return super().model_dump(**kwargs)

def json(self, **kwargs: Any) -> StrType:
"""Convert this DiffSyncModel to a JSON string, excluding the diffsync field by default as it is not serializable."""
"""Convert this DiffSyncModel to a JSON string, excluding the adapter field by default as it is not serializable."""
if "exclude" not in kwargs:
kwargs["exclude"] = {"diffsync"}
kwargs["exclude"] = {"adapter"}
if "exclude_defaults" not in kwargs:
kwargs["exclude_defaults"] = True
return super().model_dump_json(**kwargs)
Expand All @@ -167,12 +185,12 @@ def str(self, include_children: bool = True, indent: int = 0) -> StrType:
child_ids = getattr(self, fieldname)
if not child_ids:
output += ": []"
elif not self.diffsync or not include_children:
elif not self.adapter or not include_children:
output += f": {child_ids}"
else:
for child_id in child_ids:
try:
child = self.diffsync.get(modelname, child_id)
child = self.adapter.get(modelname, child_id)
output += "\n" + child.str(include_children=include_children, indent=indent + 4)
except ObjectNotFound:
output += f"\n{margin} {child_id} (ERROR: details unavailable)"
Expand All @@ -184,32 +202,32 @@ def set_status(self, status: DiffSyncStatus, message: StrType = "") -> None:
self._status_message = message

@classmethod
def create_base(cls, diffsync: "Adapter", ids: Dict, attrs: Dict) -> Optional[Self]:
def create_base(cls, adapter: "Adapter", ids: Dict, attrs: Dict) -> Optional[Self]:
"""Instantiate this class, along with any platform-specific data creation.
This method is not meant to be subclassed, users should redefine create() instead.
Args:
diffsync: The master data store for other DiffSyncModel instances that we might need to reference
adapter: The master data store for other DiffSyncModel instances that we might need to reference
ids: Dictionary of unique-identifiers needed to create the new object
attrs: Dictionary of additional attributes to set on the new object
Returns:
DiffSyncModel: instance of this class.
"""
model = cls(**ids, diffsync=diffsync, **attrs)
model = cls(**ids, adapter=adapter, **attrs)
model.set_status(DiffSyncStatus.SUCCESS, "Created successfully")
return model

@classmethod
def create(cls, diffsync: "Adapter", ids: Dict, attrs: Dict) -> Optional[Self]:
def create(cls, adapter: "Adapter", ids: Dict, attrs: Dict) -> Optional[Self]:
"""Instantiate this class, along with any platform-specific data creation.
Subclasses must call `super().create()` or `self.create_base()`; they may wish to then override the default status information
by calling `set_status()` to provide more context (such as details of any interactions with underlying systems).
Args:
diffsync: The master data store for other DiffSyncModel instances that we might need to reference
adapter: The master data store for other DiffSyncModel instances that we might need to reference
ids: Dictionary of unique-identifiers needed to create the new object
attrs: Dictionary of additional attributes to set on the new object
Expand All @@ -220,7 +238,7 @@ def create(cls, diffsync: "Adapter", ids: Dict, attrs: Dict) -> Optional[Self]:
Raises:
ObjectNotCreated: if an error occurred.
"""
return cls.create_base(diffsync=diffsync, ids=ids, attrs=attrs)
return cls.create_base(adapter=adapter, ids=ids, attrs=attrs)

def update_base(self, attrs: Dict) -> Optional[Self]:
"""Base Update method to update the attributes of this instance, along with any platform-specific data updates.
Expand Down Expand Up @@ -376,7 +394,10 @@ def add_child(self, child: "DiffSyncModel") -> None:
attr_name = self._children[child_type]
childs = getattr(self, attr_name)
if child.get_unique_id() in childs:
raise ObjectAlreadyExists(f"Already storing a {child_type} with unique_id {child.get_unique_id()}", child)
raise ObjectAlreadyExists(
f"Already storing a {child_type} with unique_id {child.get_unique_id()}",
child,
)
childs.append(child.get_unique_id())

def remove_child(self, child: "DiffSyncModel") -> None:
Expand Down Expand Up @@ -417,7 +438,9 @@ class Adapter: # pylint: disable=too-many-public-methods
"""List of top-level modelnames to begin from when diffing or synchronizing."""

def __init__(
self, name: Optional[str] = None, internal_storage_engine: Union[Type[BaseStore], BaseStore] = LocalStore
self,
name: Optional[str] = None,
internal_storage_engine: Union[Type[BaseStore], BaseStore] = LocalStore,
) -> None:
"""Generic initialization function.
Expand All @@ -426,9 +449,9 @@ def __init__(

if isinstance(internal_storage_engine, BaseStore):
self.store = internal_storage_engine
self.store.diffsync = self
self.store.adapter = self
else:
self.store = internal_storage_engine(diffsync=self)
self.store = internal_storage_engine(adapter=self)

# If the type is not defined, use the name of the class as the default value
if self.type is None:
Expand Down Expand Up @@ -565,7 +588,13 @@ def sync_from( # pylint: disable=too-many-arguments
# Generate the diff if an existing diff was not provided
if not diff:
diff = self.diff_from(source, diff_class=diff_class, flags=flags, callback=callback)
syncer = DiffSyncSyncer(diff=diff, src_diffsync=source, dst_diffsync=self, flags=flags, callback=callback)
syncer = DiffSyncSyncer(
diff=diff,
src_diffsync=source,
dst_diffsync=self,
flags=flags,
callback=callback,
)
result = syncer.perform_sync()
if result:
self.sync_complete(source, diff, flags, syncer.base_logger)
Expand Down Expand Up @@ -639,7 +668,11 @@ def diff_from(
calculation of the diff proceeds.
"""
differ = DiffSyncDiffer(
src_diffsync=source, dst_diffsync=self, flags=flags, diff_class=diff_class, callback=callback
src_diffsync=source,
dst_diffsync=self,
flags=flags,
diff_class=diff_class,
callback=callback,
)
return differ.calculate_diffs()

Expand Down Expand Up @@ -674,7 +707,9 @@ def get_all_model_names(self) -> Set[StrType]:
return self.store.get_all_model_names()

def get(
self, obj: Union[StrType, DiffSyncModel, Type[DiffSyncModel]], identifier: Union[StrType, Dict]
self,
obj: Union[StrType, DiffSyncModel, Type[DiffSyncModel]],
identifier: Union[StrType, Dict],
) -> DiffSyncModel:
"""Get one object from the data store based on its unique id.
Expand All @@ -689,7 +724,9 @@ def get(
return self.store.get(model=obj, identifier=identifier)

def get_or_none(
self, obj: Union[StrType, DiffSyncModel, Type[DiffSyncModel]], identifier: Union[StrType, Dict]
self,
obj: Union[StrType, DiffSyncModel, Type[DiffSyncModel]],
identifier: Union[StrType, Dict],
) -> Optional[DiffSyncModel]:
"""Get one object from the data store based on its unique id or get a None
Expand Down Expand Up @@ -720,7 +757,9 @@ def get_all(self, obj: Union[StrType, DiffSyncModel, Type[DiffSyncModel]]) -> Li
return self.store.get_all(model=obj)

def get_by_uids(
self, uids: List[StrType], obj: Union[StrType, DiffSyncModel, Type[DiffSyncModel]]
self,
uids: List[StrType],
obj: Union[StrType, DiffSyncModel, Type[DiffSyncModel]],
) -> List[DiffSyncModel]:
"""Get multiple objects from the store by their unique IDs/Keys and type.
Expand Down
2 changes: 1 addition & 1 deletion diffsync/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ def sync_model( # pylint: disable=too-many-branches, unused-argument
if self.action == DiffSyncActions.CREATE:
if dst_model is not None:
raise ObjectNotCreated(f"Failed to create {self.model_class.get_type()} {ids} - it already exists!")
dst_model = self.model_class.create(diffsync=self.dst_diffsync, ids=ids, attrs=attrs)
dst_model = self.model_class.create(adapter=self.dst_diffsync, ids=ids, attrs=attrs)
elif self.action == DiffSyncActions.UPDATE:
if dst_model is None:
raise ObjectNotUpdated(f"Failed to update {self.model_class.get_type()} {ids} - not found!")
Expand Down
12 changes: 6 additions & 6 deletions diffsync/store/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ class BaseStore:
def __init__(
self, # pylint: disable=unused-argument
*args: Any, # pylint: disable=unused-argument
diffsync: Optional["Adapter"] = None,
adapter: Optional["Adapter"] = None,
name: str = "",
**kwargs: Any, # pylint: disable=unused-argument
) -> None:
"""Init method for BaseStore."""
self.diffsync = diffsync
self.adapter = adapter
self.name = name or self.__class__.__name__
self._log = structlog.get_logger().new(store=self)

Expand Down Expand Up @@ -95,8 +95,8 @@ def remove(self, *, obj: "DiffSyncModel", remove_children: bool = False) -> None

self.remove_item(modelname, uid)

if obj.diffsync:
obj.diffsync = None
if obj.adapter:
obj.adapter = None

if remove_children:
for child_type, child_fieldname in obj.get_children_mapping().items():
Expand Down Expand Up @@ -243,9 +243,9 @@ def _get_object_class_and_model(
"""Get object class and model name for a model."""
if isinstance(model, str):
modelname = model
if not hasattr(self.diffsync, model):
if not hasattr(self.adapter, model):
return None, modelname
object_class = getattr(self.diffsync, model)
object_class = getattr(self.adapter, model)
else:
object_class = model
modelname = model.get_type()
Expand Down
4 changes: 2 additions & 2 deletions diffsync/store/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ def add(self, *, obj: "DiffSyncModel") -> None:
# Return so we don't have to change anything on the existing object and underlying data
return

if not obj.diffsync:
obj.diffsync = self.diffsync
if not obj.adapter:
obj.adapter = self.adapter

self._data[modelname][uid] = obj

Expand Down
6 changes: 3 additions & 3 deletions diffsync/store/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def _get_object_from_redis_key(self, key: str) -> "DiffSyncModel":
pickled_object = self._store.get(key)
if pickled_object:
obj_result = loads(pickled_object) # nosec
obj_result.diffsync = self.diffsync
obj_result.adapter = self.adapter
return obj_result
raise ObjectNotFound(f"{key} not present in Cache")

Expand Down Expand Up @@ -178,7 +178,7 @@ def add(self, *, obj: "DiffSyncModel") -> None:

# Remove the diffsync object before sending to Redis
obj_copy = copy.copy(obj)
obj_copy.diffsync = None
obj_copy.adapter = None

self._store.set(object_key, dumps(obj_copy))

Expand All @@ -193,7 +193,7 @@ def update(self, *, obj: "DiffSyncModel") -> None:

object_key = self._get_key_for_object(modelname, uid)
obj_copy = copy.copy(obj)
obj_copy.diffsync = None
obj_copy.adapter = None

self._store.set(object_key, dumps(obj_copy))

Expand Down
4 changes: 2 additions & 2 deletions docs/source/getting_started/01-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,10 @@ class Device(DiffSyncModel):
[...]

@classmethod
def create(cls, diffsync, ids, attrs):
def create(cls, adapter, ids, attrs):
## TODO add your own logic here to create the device on the remote system
# Call the super().create() method to create the in-memory DiffSyncModel instance
return super().create(ids=ids, diffsync=diffsync, attrs=attrs)
return super().create(ids=ids, adapter=adapter, attrs=attrs)

def update(self, attrs):
## TODO add your own logic here to update the device on the remote system
Expand Down
2 changes: 2 additions & 0 deletions docs/source/upgrading/01-upgrading-to-2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ With diffsync 2.0, there a couple of breaking changes. What they are and how to

The main diffsync class `diffsync.Diffsync` has been renamed to `diffsync.Adapter` as we have found that this is the verbiage that is most often used by users and explains the intent of the class clearer. The old name will still be around until 2.1, but is considered deprecated at this point.

As a consequence, a lot of fields have been renamed all across diffsync. To the end user, this will most prominently appear in the signature of the `create` method, where you will have to rename the `diffsync` parameter to `adapter`.

## Upgrade to Pydantic's major version 2

A [migration guide](https://docs.pydantic.dev/latest/migration/) is available in the Pydantic documentation. Here are the key things that may apply to your usage of diffsync:
Expand Down
14 changes: 7 additions & 7 deletions examples/03-remote-system/nautobot_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ class NautobotCountry(Country):
"""Store the nautobot uuid in the object to allow update and delete of existing object."""

@classmethod
def create(cls, diffsync: Adapter, ids: dict, attrs: dict):
def create(cls, adapter: Adapter, ids: dict, attrs: dict):
"""Create a country object in Nautobot.
Args:
diffsync: The master data store for other DiffSyncModel instances that we might need to reference
adapter: The master data store for other DiffSyncModel instances that we might need to reference
ids: Dictionary of unique-identifiers needed to create the new object
attrs: Dictionary of additional attributes to set on the new object
Expand All @@ -43,11 +43,11 @@ def create(cls, diffsync: Adapter, ids: dict, attrs: dict):
"""
# Retrieve the parent region in internal cache to access its UUID
# because the UUID is required to associate the object to its parent region in Nautobot
region = diffsync.get(diffsync.region, attrs.get("region"))
region = adapter.get(adapter.region, attrs.get("region"))

# Create the new country in Nautobot and attach it to its parent
try:
country = diffsync.nautobot.dcim.regions.create(
country = adapter.nautobot.dcim.regions.create(
slug=ids.get("slug"),
name=attrs.get("name"),
custom_fields=dict(population=attrs.get("population")),
Expand All @@ -61,7 +61,7 @@ def create(cls, diffsync: Adapter, ids: dict, attrs: dict):

# Add the newly created remote_id and create the internal object for this resource.
attrs["remote_id"] = country.id
item = super().create(ids=ids, diffsync=diffsync, attrs=attrs)
item = super().create(ids=ids, adapter=adapter, attrs=attrs)
return item

def update(self, attrs: dict):
Expand All @@ -78,7 +78,7 @@ def update(self, attrs: dict):
ObjectNotUpdated: if an error occurred.
"""
# Retrive the pynautobot object from Nautobot since we only have the UUID internally
remote = self.diffsync.nautobot.dcim.regions.get(self.remote_id)
remote = self.adapter.nautobot.dcim.regions.get(self.remote_id)

# Convert the internal attrs to Nautobot format
if "population" in attrs:
Expand All @@ -98,7 +98,7 @@ def delete(self):
NautobotCountry: DiffSync object
"""
# Retrieve the pynautobot object and delete the object in Nautobot
remote = self.diffsync.nautobot.dcim.regions.get(self.remote_id)
remote = self.adapter.nautobot.dcim.regions.get(self.remote_id)
remote.delete()

super().delete()
Expand Down
Loading

0 comments on commit 615ed7c

Please sign in to comment.