Skip to content

Commit f7b6510

Browse files
committed
Adding type hints to model.py
1 parent 9176359 commit f7b6510

File tree

1 file changed

+72
-64
lines changed

1 file changed

+72
-64
lines changed

deepdiff/model.py

Lines changed: 72 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import logging
22
from collections.abc import Mapping
33
from copy import copy
4+
from typing import Any, Dict, List, Optional, Set, Union, Literal, Type, TYPE_CHECKING
45
from deepdiff.helper import (
56
RemapDict, strings, notpresent, get_type, numpy_numbers, np, literal_eval_extended,
67
dict_, SetOrdered)
78
from deepdiff.path import stringify_element
89

10+
if TYPE_CHECKING:
11+
from deepdiff.diff import DeepDiff
12+
913
logger = logging.getLogger(__name__)
1014

11-
FORCE_DEFAULT = 'fake'
12-
UP_DOWN = {'up': 'down', 'down': 'up'}
15+
FORCE_DEFAULT: Literal['fake'] = 'fake'
16+
UP_DOWN: Dict[str, str] = {'up': 'down', 'down': 'up'}
1317

14-
REPORT_KEYS = {
18+
REPORT_KEYS: Set[str] = {
1519
"type_changes",
1620
"dictionary_item_added",
1721
"dictionary_item_removed",
@@ -27,7 +31,7 @@
2731
"repetition_change",
2832
}
2933

30-
CUSTOM_FIELD = "__internal:custom:extra_info"
34+
CUSTOM_FIELD: str = "__internal:custom:extra_info"
3135

3236

3337
class DoesNotExist(Exception):
@@ -36,7 +40,7 @@ class DoesNotExist(Exception):
3640

3741
class ResultDict(RemapDict):
3842

39-
def remove_empty_keys(self):
43+
def remove_empty_keys(self) -> None:
4044
"""
4145
Remove empty keys from this object. Should always be called after the result is final.
4246
:return:
@@ -48,11 +52,11 @@ def remove_empty_keys(self):
4852

4953

5054
class TreeResult(ResultDict):
51-
def __init__(self):
55+
def __init__(self) -> None:
5256
for key in REPORT_KEYS:
5357
self[key] = SetOrdered()
5458

55-
def mutual_add_removes_to_become_value_changes(self):
59+
def mutual_add_removes_to_become_value_changes(self) -> None:
5660
"""
5761
There might be the same paths reported in the results as removed and added.
5862
In such cases they should be reported as value_changes.
@@ -84,12 +88,16 @@ def mutual_add_removes_to_become_value_changes(self):
8488
if 'iterable_item_added' in self and not iterable_item_added:
8589
del self['iterable_item_added']
8690

87-
def __getitem__(self, item):
91+
def __getitem__(self, item: str) -> SetOrdered:
8892
if item not in self:
8993
self[item] = SetOrdered()
90-
return self.get(item)
94+
result = self.get(item)
95+
if result is None:
96+
result = SetOrdered()
97+
self[item] = result
98+
return result
9199

92-
def __len__(self):
100+
def __len__(self) -> int:
93101
length = 0
94102
for value in self.values():
95103
if isinstance(value, SetOrdered):
@@ -100,9 +108,9 @@ def __len__(self):
100108

101109

102110
class TextResult(ResultDict):
103-
ADD_QUOTES_TO_STRINGS = True
111+
ADD_QUOTES_TO_STRINGS: bool = True
104112

105-
def __init__(self, tree_results=None, verbose_level=1):
113+
def __init__(self, tree_results: Optional['TreeResult'] = None, verbose_level: int = 1) -> None:
106114
self.verbose_level = verbose_level
107115
# TODO: centralize keys
108116
self.update({
@@ -124,10 +132,10 @@ def __init__(self, tree_results=None, verbose_level=1):
124132
if tree_results:
125133
self._from_tree_results(tree_results)
126134

127-
def __set_or_dict(self):
135+
def __set_or_dict(self) -> Union[Dict[str, Any], SetOrdered]:
128136
return {} if self.verbose_level >= 2 else SetOrdered()
129137

130-
def _from_tree_results(self, tree):
138+
def _from_tree_results(self, tree: 'TreeResult') -> None:
131139
"""
132140
Populate this object by parsing an existing reference-style result dictionary.
133141
:param tree: A TreeResult
@@ -149,7 +157,7 @@ def _from_tree_results(self, tree):
149157
self._from_tree_deep_distance(tree)
150158
self._from_tree_custom_results(tree)
151159

152-
def _from_tree_default(self, tree, report_type, ignore_if_in_iterable_opcodes=False):
160+
def _from_tree_default(self, tree: 'TreeResult', report_type: str, ignore_if_in_iterable_opcodes: bool = False) -> None:
153161
if report_type in tree:
154162

155163
for change in tree[report_type]: # report each change
@@ -291,9 +299,9 @@ def _from_tree_custom_results(self, tree):
291299

292300

293301
class DeltaResult(TextResult):
294-
ADD_QUOTES_TO_STRINGS = False
302+
ADD_QUOTES_TO_STRINGS: bool = False
295303

296-
def __init__(self, tree_results=None, ignore_order=None, always_include_values=False, _iterable_opcodes=None):
304+
def __init__(self, tree_results: Optional['TreeResult'] = None, ignore_order: Optional[bool] = None, always_include_values: bool = False, _iterable_opcodes: Optional[Dict[str, Any]] = None) -> None:
297305
self.ignore_order = ignore_order
298306
self.always_include_values = always_include_values
299307

@@ -517,15 +525,15 @@ class DiffLevel:
517525
"""
518526

519527
def __init__(self,
520-
t1,
521-
t2,
522-
down=None,
523-
up=None,
524-
report_type=None,
525-
child_rel1=None,
526-
child_rel2=None,
527-
additional=None,
528-
verbose_level=1):
528+
t1: Any,
529+
t2: Any,
530+
down: Optional['DiffLevel'] = None,
531+
up: Optional['DiffLevel'] = None,
532+
report_type: Optional[str] = None,
533+
child_rel1: Optional['ChildRelationship'] = None,
534+
child_rel2: Optional['ChildRelationship'] = None,
535+
additional: Optional[Dict[str, Any]] = None,
536+
verbose_level: int = 1) -> None:
529537
"""
530538
:param child_rel1: Either:
531539
- An existing ChildRelationship object describing the "down" relationship for t1; or
@@ -581,7 +589,7 @@ def __init__(self,
581589

582590
self.verbose_level = verbose_level
583591

584-
def __repr__(self):
592+
def __repr__(self) -> str:
585593
if self.verbose_level:
586594
from deepdiff.summarize import summarize
587595

@@ -596,7 +604,7 @@ def __repr__(self):
596604
result = "<{}>".format(self.path())
597605
return result
598606

599-
def __setattr__(self, key, value):
607+
def __setattr__(self, key: str, value: Any) -> None:
600608
# Setting up or down, will set the opposite link in this linked list.
601609
if key in UP_DOWN and value is not None:
602610
self.__dict__[key] = value
@@ -605,15 +613,15 @@ def __setattr__(self, key, value):
605613
else:
606614
self.__dict__[key] = value
607615

608-
def __iter__(self):
616+
def __iter__(self) -> Any:
609617
yield self.t1
610618
yield self.t2
611619

612620
@property
613-
def repetition(self):
621+
def repetition(self) -> Dict[str, Any]:
614622
return self.additional['repetition']
615623

616-
def auto_generate_child_rel(self, klass, param, param2=None):
624+
def auto_generate_child_rel(self, klass: Type['ChildRelationship'], param: Any, param2: Optional[Any] = None) -> None:
617625
"""
618626
Auto-populate self.child_rel1 and self.child_rel2.
619627
This requires self.down to be another valid DiffLevel object.
@@ -630,7 +638,7 @@ def auto_generate_child_rel(self, klass, param, param2=None):
630638
klass=klass, parent=self.t2, child=self.down.t2, param=param if param2 is None else param2) # type: ignore
631639

632640
@property
633-
def all_up(self):
641+
def all_up(self) -> 'DiffLevel':
634642
"""
635643
Get the root object of this comparison.
636644
(This is a convenient wrapper for following the up attribute as often as you can.)
@@ -642,7 +650,7 @@ def all_up(self):
642650
return level
643651

644652
@property
645-
def all_down(self):
653+
def all_down(self) -> 'DiffLevel':
646654
"""
647655
Get the leaf object of this comparison.
648656
(This is a convenient wrapper for following the down attribute as often as you can.)
@@ -654,10 +662,10 @@ def all_down(self):
654662
return level
655663

656664
@staticmethod
657-
def _format_result(root, result):
665+
def _format_result(root: str, result: Optional[str]) -> Optional[str]:
658666
return None if result is None else "{}{}".format(root, result)
659667

660-
def get_root_key(self, use_t2=False):
668+
def get_root_key(self, use_t2: bool = False) -> Any:
661669
"""
662670
Get the path's root key value for this change
663671
@@ -674,7 +682,7 @@ def get_root_key(self, use_t2=False):
674682
return next_rel.param
675683
return notpresent
676684

677-
def path(self, root="root", force=None, get_parent_too=False, use_t2=False, output_format='str', reporting_move=False):
685+
def path(self, root: str = "root", force: Optional[str] = None, get_parent_too: bool = False, use_t2: bool = False, output_format: Literal['str', 'list'] = 'str', reporting_move: bool = False) -> Any:
678686
"""
679687
A python syntax string describing how to descend to this level, assuming the top level object is called root.
680688
Returns None if the path is not representable as a string.
@@ -765,18 +773,18 @@ def path(self, root="root", force=None, get_parent_too=False, use_t2=False, outp
765773
output = (self._format_result(root, parent), param, self._format_result(root, result)) # type: ignore
766774
else:
767775
self._path[cache_key] = result
768-
output = self._format_result(root, result)
776+
output = self._format_result(root, result) if isinstance(result, (str, type(None))) else None
769777
else:
770778
output = result
771779
return output
772780

773781
def create_deeper(self,
774-
new_t1,
775-
new_t2,
776-
child_relationship_class,
777-
child_relationship_param=None,
778-
child_relationship_param2=None,
779-
report_type=None):
782+
new_t1: Any,
783+
new_t2: Any,
784+
child_relationship_class: Type['ChildRelationship'],
785+
child_relationship_param: Optional[Any] = None,
786+
child_relationship_param2: Optional[Any] = None,
787+
report_type: Optional[str] = None) -> 'DiffLevel':
780788
"""
781789
Start a new comparison level and correctly link it to this one.
782790
:rtype: DiffLevel
@@ -791,12 +799,12 @@ def create_deeper(self,
791799
return result
792800

793801
def branch_deeper(self,
794-
new_t1,
795-
new_t2,
796-
child_relationship_class,
797-
child_relationship_param=None,
798-
child_relationship_param2=None,
799-
report_type=None):
802+
new_t1: Any,
803+
new_t2: Any,
804+
child_relationship_class: Type['ChildRelationship'],
805+
child_relationship_param: Optional[Any] = None,
806+
child_relationship_param2: Optional[Any] = None,
807+
report_type: Optional[str] = None) -> 'DiffLevel':
800808
"""
801809
Branch this comparison: Do not touch this comparison line, but create a new one with exactly the same content,
802810
just one level deeper.
@@ -807,7 +815,7 @@ def branch_deeper(self,
807815
return branch.create_deeper(new_t1, new_t2, child_relationship_class,
808816
child_relationship_param, child_relationship_param2, report_type)
809817

810-
def copy(self):
818+
def copy(self) -> 'DiffLevel':
811819
"""
812820
Get a deep copy of this comparision line.
813821
:return: The leaf ("downmost") object of the copy.
@@ -850,20 +858,20 @@ class ChildRelationship:
850858

851859
# Format to a be used for representing param.
852860
# E.g. for a dict, this turns a formatted param param "42" into "[42]".
853-
param_repr_format = None
861+
param_repr_format: Optional[str] = None
854862

855863
# This is a hook allowing subclasses to manipulate param strings.
856864
# :param string: Input string
857865
# :return: Manipulated string, as appropriate in this context.
858-
quote_str = None
866+
quote_str: Optional[str] = None
859867

860868
@staticmethod
861-
def create(klass, parent, child, param=None):
869+
def create(klass: Type['ChildRelationship'], parent: Any, child: Any, param: Optional[Any] = None) -> 'ChildRelationship':
862870
if not issubclass(klass, ChildRelationship):
863871
raise TypeError
864872
return klass(parent, child, param)
865873

866-
def __init__(self, parent, child, param=None):
874+
def __init__(self, parent: Any, child: Any, param: Optional[Any] = None) -> None:
867875
# The parent object of this relationship, e.g. a dict
868876
self.parent = parent
869877

@@ -873,7 +881,7 @@ def __init__(self, parent, child, param=None):
873881
# A subclass-dependent parameter describing how to get from parent to child, e.g. the key in a dict
874882
self.param = param
875883

876-
def __repr__(self):
884+
def __repr__(self) -> str:
877885
from deepdiff.summarize import summarize
878886

879887
name = "<{} parent:{}, child:{}, param:{}>"
@@ -882,7 +890,7 @@ def __repr__(self):
882890
param = summarize(self.param, max_length=15)
883891
return name.format(self.__class__.__name__, parent, child, param)
884892

885-
def get_param_repr(self, force=None):
893+
def get_param_repr(self, force: Optional[str] = None) -> Optional[str]:
886894
"""
887895
Returns a formatted param python parsable string describing this relationship,
888896
or None if the relationship is not representable as a string.
@@ -899,7 +907,7 @@ def get_param_repr(self, force=None):
899907
"""
900908
return self.stringify_param(force)
901909

902-
def stringify_param(self, force=None):
910+
def stringify_param(self, force: Optional[str] = None) -> Optional[str]:
903911
"""
904912
Convert param to a string. Return None if there is no string representation.
905913
This is called by get_param_repr()
@@ -946,13 +954,13 @@ def stringify_param(self, force=None):
946954

947955

948956
class DictRelationship(ChildRelationship):
949-
param_repr_format = "[{}]"
950-
quote_str = "'{}'"
957+
param_repr_format: Optional[str] = "[{}]"
958+
quote_str: Optional[str] = "'{}'"
951959

952960

953961
class NumpyArrayRelationship(ChildRelationship):
954-
param_repr_format = "[{}]"
955-
quote_str = None
962+
param_repr_format: Optional[str] = "[{}]"
963+
quote_str: Optional[str] = None
956964

957965

958966
class SubscriptableIterableRelationship(DictRelationship):
@@ -970,9 +978,9 @@ class SetRelationship(InaccessibleRelationship):
970978

971979
class NonSubscriptableIterableRelationship(InaccessibleRelationship):
972980

973-
param_repr_format = "[{}]"
981+
param_repr_format: Optional[str] = "[{}]"
974982

975-
def get_param_repr(self, force=None):
983+
def get_param_repr(self, force: Optional[str] = None) -> Optional[str]:
976984
if force == 'yes':
977985
result = "(unrepresentable)"
978986
elif force == 'fake' and self.param:
@@ -984,4 +992,4 @@ def get_param_repr(self, force=None):
984992

985993

986994
class AttributeRelationship(ChildRelationship):
987-
param_repr_format = ".{}"
995+
param_repr_format: Optional[str] = ".{}"

0 commit comments

Comments
 (0)