Skip to content

Commit 70336ac

Browse files
committed
Type hinting overhaul.
1 parent 95fb491 commit 70336ac

File tree

20 files changed

+211
-138
lines changed

20 files changed

+211
-138
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ B.load()
3939

4040
# Show the difference between both systems, that is, what would change if we applied changes from System B to System A
4141
diff_a_b = A.diff_from(B)
42-
print(diff_a_b.str())
42+
print(diff_a_b.to_detailed_string())
4343

4444
# Update System A to align with the current status of system B
4545
A.sync_from(B)

diffsync/__init__.py

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
limitations under the License.
1616
"""
1717
from inspect import isclass
18-
from typing import Callable, ClassVar, Dict, List, Mapping, Optional, Text, Tuple, Type, Union
18+
from typing import Callable, ClassVar, Dict, List, Mapping, Optional, Text, Tuple, Type, Union, Any, Set
1919

2020
from pydantic import BaseModel, PrivateAttr
2121
import structlog # type: ignore
@@ -72,7 +72,7 @@ class DiffSyncModel(BaseModel):
7272
Note: inclusion in `_attributes` is mutually exclusive from inclusion in `_identifiers`; a field cannot be in both!
7373
"""
7474

75-
_children: ClassVar[Mapping[str, str]] = {}
75+
_children: ClassVar[Dict[str, str]] = {}
7676
"""Optional: dict of `{_modelname: field_name}` entries describing how to store "child" models in this model.
7777
7878
When calculating a Diff or performing a sync, DiffSync will automatically recurse into these child models.
@@ -101,7 +101,7 @@ class Config: # pylint: disable=too-few-public-methods
101101
# Let us have a DiffSync as an instance variable even though DiffSync is not a Pydantic model itself.
102102
arbitrary_types_allowed = True
103103

104-
def __init_subclass__(cls):
104+
def __init_subclass__(cls) -> None:
105105
"""Validate that the various class attribute declarations correspond to actual instance fields.
106106
107107
Called automatically on subclass declaration.
@@ -132,27 +132,27 @@ def __init_subclass__(cls):
132132
if attr_child_overlap:
133133
raise AttributeError(f"Fields {attr_child_overlap} are included in both _attributes and _children.")
134134

135-
def __repr__(self):
135+
def __repr__(self) -> str:
136136
return f'{self.get_type()} "{self.get_unique_id()}"'
137137

138-
def __str__(self):
138+
def __str__(self) -> str:
139139
return self.get_unique_id()
140140

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

147-
def json(self, **kwargs) -> str:
147+
def json(self, **kwargs: Any) -> str:
148148
"""Convert this DiffSyncModel to a JSON string, excluding the diffsync field by default as it is not serializable."""
149149
if "exclude" not in kwargs:
150150
kwargs["exclude"] = {"diffsync"}
151151
if "exclude_defaults" not in kwargs:
152152
kwargs["exclude_defaults"] = True
153153
return super().json(**kwargs)
154154

155-
def str(self, include_children: bool = True, indent: int = 0) -> str:
155+
def to_detailed_string(self, include_children: bool = True, indent: int = 0) -> str:
156156
"""Build a detailed string representation of this DiffSyncModel and optionally its children."""
157157
margin = " " * indent
158158
output = f"{margin}{self.get_type()}: {self.get_unique_id()}: {self.get_attrs()}"
@@ -167,12 +167,12 @@ def str(self, include_children: bool = True, indent: int = 0) -> str:
167167
for child_id in child_ids:
168168
try:
169169
child = self.diffsync.get(modelname, child_id)
170-
output += "\n" + child.str(include_children=include_children, indent=indent + 4)
170+
output += "\n" + child.to_detailed_string(include_children=include_children, indent=indent + 4)
171171
except ObjectNotFound:
172172
output += f"\n{margin} {child_id} (ERROR: details unavailable)"
173173
return output
174174

175-
def set_status(self, status: DiffSyncStatus, message: Text = ""):
175+
def set_status(self, status: DiffSyncStatus, message: Text = "") -> None:
176176
"""Update the status (and optionally status message) of this model in response to a create/update/delete call."""
177177
self._status = status
178178
self._status_message = message
@@ -288,7 +288,7 @@ def get_type(cls) -> Text:
288288
return cls._modelname
289289

290290
@classmethod
291-
def create_unique_id(cls, **identifiers) -> Text:
291+
def create_unique_id(cls, **identifiers: Dict[str, Any]) -> str:
292292
"""Construct a unique identifier for this model class.
293293
294294
Args:
@@ -297,7 +297,7 @@ def create_unique_id(cls, **identifiers) -> Text:
297297
return "__".join(str(identifiers[key]) for key in cls._identifiers)
298298

299299
@classmethod
300-
def get_children_mapping(cls) -> Mapping[Text, Text]:
300+
def get_children_mapping(cls) -> Dict[str, str]:
301301
"""Get the mapping of types to fieldnames for child models of this model."""
302302
return cls._children
303303

@@ -347,9 +347,9 @@ def get_shortname(self) -> Text:
347347

348348
def get_status(self) -> Tuple[DiffSyncStatus, Text]:
349349
"""Get the status of the last create/update/delete operation on this object, and any associated message."""
350-
return (self._status, self._status_message)
350+
return self._status, self._status_message
351351

352-
def add_child(self, child: "DiffSyncModel"):
352+
def add_child(self, child: "DiffSyncModel") -> None:
353353
"""Add a child reference to an object.
354354
355355
The child object isn't stored, only its unique id.
@@ -373,7 +373,7 @@ def add_child(self, child: "DiffSyncModel"):
373373
raise ObjectAlreadyExists(f"Already storing a {child_type} with unique_id {child.get_unique_id()}", child)
374374
childs.append(child.get_unique_id())
375375

376-
def remove_child(self, child: "DiffSyncModel"):
376+
def remove_child(self, child: "DiffSyncModel") -> None:
377377
"""Remove a child reference from an object.
378378
379379
The name of the storage attribute is defined in `_children` per object type.
@@ -404,13 +404,15 @@ class DiffSync: # pylint: disable=too-many-public-methods
404404
# modelname1 = MyModelClass1
405405
# modelname2 = MyModelClass2
406406

407-
type: ClassVar[Optional[str]] = None
407+
type: Optional[str] = None
408408
"""Type of the object, will default to the name of the class if not provided."""
409409

410410
top_level: ClassVar[List[str]] = []
411411
"""List of top-level modelnames to begin from when diffing or synchronizing."""
412412

413-
def __init__(self, name=None, internal_storage_engine=LocalStore):
413+
def __init__(
414+
self, name: Optional[str] = None, internal_storage_engine: Union[Type[BaseStore], BaseStore] = LocalStore
415+
) -> None:
414416
"""Generic initialization function.
415417
416418
Subclasses should be careful to call super().__init__() if they override this method.
@@ -429,7 +431,7 @@ def __init__(self, name=None, internal_storage_engine=LocalStore):
429431
# If the name has not been provided, use the type as the name
430432
self.name = name if name else self.type
431433

432-
def __init_subclass__(cls):
434+
def __init_subclass__(cls) -> None:
433435
"""Validate that references to specific DiffSyncModels use the correct modelnames.
434436
435437
Called automatically on subclass declaration.
@@ -448,16 +450,16 @@ def __init_subclass__(cls):
448450
if not isclass(value) or not issubclass(value, DiffSyncModel):
449451
raise AttributeError(f'top_level references attribute "{name}" but it is not a DiffSyncModel subclass!')
450452

451-
def __str__(self):
453+
def __str__(self) -> str:
452454
"""String representation of a DiffSync."""
453455
if self.type != self.name:
454456
return f'{self.type} "{self.name}"'
455457
return self.type
456458

457-
def __repr__(self):
459+
def __repr__(self) -> str:
458460
return f"<{str(self)}>"
459461

460-
def __len__(self):
462+
def __len__(self) -> int:
461463
"""Total number of elements stored."""
462464
return self.store.count()
463465

@@ -481,11 +483,11 @@ def _get_initial_value_order(cls) -> List[str]:
481483
value_order.append(item)
482484
return value_order
483485

484-
def load(self):
486+
def load(self) -> None:
485487
"""Load all desired data from whatever backend data source into this instance."""
486488
# No-op in this generic class
487489

488-
def dict(self, exclude_defaults: bool = True, **kwargs) -> Mapping:
490+
def dict(self, exclude_defaults: bool = True, **kwargs: Any) -> Dict[str, Dict[str, Dict]]:
489491
"""Represent the DiffSync contents as a dict, as if it were a Pydantic model."""
490492
data: Dict[str, Dict[str, Dict]] = {}
491493
for modelname in self.store.get_all_model_names():
@@ -494,7 +496,7 @@ def dict(self, exclude_defaults: bool = True, **kwargs) -> Mapping:
494496
data[obj.get_type()][obj.get_unique_id()] = obj.dict(exclude_defaults=exclude_defaults, **kwargs)
495497
return data
496498

497-
def str(self, indent: int = 0) -> str:
499+
def to_detailed_string(self, indent: int = 0) -> str:
498500
"""Build a detailed string representation of this DiffSync."""
499501
margin = " " * indent
500502
output = ""
@@ -507,10 +509,10 @@ def str(self, indent: int = 0) -> str:
507509
output += ": []"
508510
else:
509511
for model in models:
510-
output += "\n" + model.str(indent=indent + 2)
512+
output += "\n" + model.to_detailed_string(indent=indent + 2)
511513
return output
512514

513-
def load_from_dict(self, data: Dict):
515+
def load_from_dict(self, data: Dict) -> None:
514516
"""The reverse of `dict` method, taking a dictionary and loading into the inventory.
515517
516518
Args:
@@ -594,7 +596,7 @@ def sync_complete(
594596
diff: Diff,
595597
flags: DiffSyncFlags = DiffSyncFlags.NONE,
596598
logger: Optional[structlog.BoundLogger] = None,
597-
):
599+
) -> None:
598600
"""Callback triggered after a `sync_from` operation has completed and updated the model data of this instance.
599601
600602
Note that this callback is **only** triggered if the sync actually resulted in data changes. If there are no
@@ -657,11 +659,11 @@ def diff_to(
657659
# Object Storage Management
658660
# ------------------------------------------------------------------------------
659661

660-
def get_all_model_names(self):
662+
def get_all_model_names(self) -> Set[str]:
661663
"""Get all model names.
662664
663665
Returns:
664-
List[str]: List of model names
666+
List of model names
665667
"""
666668
return self.store.get_all_model_names()
667669

@@ -730,7 +732,7 @@ def get_tree_traversal(cls, as_dict: bool = False) -> Union[Text, Mapping]:
730732
"""Get a string describing the tree traversal for the diffsync object.
731733
732734
Args:
733-
as_dict: Whether or not to return as a dictionary
735+
as_dict: Whether to return as a dictionary
734736
735737
Returns:
736738
A string or dictionary representation of tree
@@ -751,7 +753,7 @@ def get_tree_traversal(cls, as_dict: bool = False) -> Union[Text, Mapping]:
751753
return output_dict
752754
return tree_string(output_dict, cls.__name__)
753755

754-
def add(self, obj: DiffSyncModel):
756+
def add(self, obj: DiffSyncModel) -> None:
755757
"""Add a DiffSyncModel object to the store.
756758
757759
Args:
@@ -762,7 +764,7 @@ def add(self, obj: DiffSyncModel):
762764
"""
763765
return self.store.add(obj=obj)
764766

765-
def update(self, obj: DiffSyncModel):
767+
def update(self, obj: DiffSyncModel) -> None:
766768
"""Update a DiffSyncModel object to the store.
767769
768770
Args:
@@ -773,7 +775,7 @@ def update(self, obj: DiffSyncModel):
773775
"""
774776
return self.store.update(obj=obj)
775777

776-
def remove(self, obj: DiffSyncModel, remove_children: bool = False):
778+
def remove(self, obj: DiffSyncModel, remove_children: bool = False) -> None:
777779
"""Remove a DiffSyncModel object from the store.
778780
779781
Args:
@@ -835,14 +837,14 @@ def update_or_add_model_instance(self, obj: DiffSyncModel) -> Tuple[DiffSyncMode
835837
"""
836838
return self.store.update_or_add_model_instance(obj=obj)
837839

838-
def count(self, model: Union[Text, "DiffSyncModel", Type["DiffSyncModel"], None] = None):
840+
def count(self, model: Union[Text, "DiffSyncModel", Type["DiffSyncModel"], None] = None) -> int:
839841
"""Count how many objects of one model type exist in the backend store.
840842
841843
Args:
842844
model (DiffSyncModel): The DiffSyncModel to check the number of elements. If not provided, default to all.
843845
844846
Returns:
845-
Int: Number of elements of the model type
847+
Number of elements of the model type
846848
"""
847849
return self.store.count(model=model)
848850

0 commit comments

Comments
 (0)