1
1
import logging
2
2
from collections .abc import Mapping
3
3
from copy import copy
4
+ from typing import Any , Dict , List , Optional , Set , Union , Literal , Type , TYPE_CHECKING
4
5
from deepdiff .helper import (
5
6
RemapDict , strings , notpresent , get_type , numpy_numbers , np , literal_eval_extended ,
6
7
dict_ , SetOrdered )
7
8
from deepdiff .path import stringify_element
8
9
10
+ if TYPE_CHECKING :
11
+ from deepdiff .diff import DeepDiff
12
+
9
13
logger = logging .getLogger (__name__ )
10
14
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' }
13
17
14
- REPORT_KEYS = {
18
+ REPORT_KEYS : Set [ str ] = {
15
19
"type_changes" ,
16
20
"dictionary_item_added" ,
17
21
"dictionary_item_removed" ,
27
31
"repetition_change" ,
28
32
}
29
33
30
- CUSTOM_FIELD = "__internal:custom:extra_info"
34
+ CUSTOM_FIELD : str = "__internal:custom:extra_info"
31
35
32
36
33
37
class DoesNotExist (Exception ):
@@ -36,7 +40,7 @@ class DoesNotExist(Exception):
36
40
37
41
class ResultDict (RemapDict ):
38
42
39
- def remove_empty_keys (self ):
43
+ def remove_empty_keys (self ) -> None :
40
44
"""
41
45
Remove empty keys from this object. Should always be called after the result is final.
42
46
:return:
@@ -48,11 +52,11 @@ def remove_empty_keys(self):
48
52
49
53
50
54
class TreeResult (ResultDict ):
51
- def __init__ (self ):
55
+ def __init__ (self ) -> None :
52
56
for key in REPORT_KEYS :
53
57
self [key ] = SetOrdered ()
54
58
55
- def mutual_add_removes_to_become_value_changes (self ):
59
+ def mutual_add_removes_to_become_value_changes (self ) -> None :
56
60
"""
57
61
There might be the same paths reported in the results as removed and added.
58
62
In such cases they should be reported as value_changes.
@@ -84,12 +88,16 @@ def mutual_add_removes_to_become_value_changes(self):
84
88
if 'iterable_item_added' in self and not iterable_item_added :
85
89
del self ['iterable_item_added' ]
86
90
87
- def __getitem__ (self , item ) :
91
+ def __getitem__ (self , item : str ) -> SetOrdered :
88
92
if item not in self :
89
93
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
91
99
92
- def __len__ (self ):
100
+ def __len__ (self ) -> int :
93
101
length = 0
94
102
for value in self .values ():
95
103
if isinstance (value , SetOrdered ):
@@ -100,9 +108,9 @@ def __len__(self):
100
108
101
109
102
110
class TextResult (ResultDict ):
103
- ADD_QUOTES_TO_STRINGS = True
111
+ ADD_QUOTES_TO_STRINGS : bool = True
104
112
105
- def __init__ (self , tree_results = None , verbose_level = 1 ) :
113
+ def __init__ (self , tree_results : Optional [ 'TreeResult' ] = None , verbose_level : int = 1 ) -> None :
106
114
self .verbose_level = verbose_level
107
115
# TODO: centralize keys
108
116
self .update ({
@@ -124,10 +132,10 @@ def __init__(self, tree_results=None, verbose_level=1):
124
132
if tree_results :
125
133
self ._from_tree_results (tree_results )
126
134
127
- def __set_or_dict (self ):
135
+ def __set_or_dict (self ) -> Union [ Dict [ str , Any ], SetOrdered ] :
128
136
return {} if self .verbose_level >= 2 else SetOrdered ()
129
137
130
- def _from_tree_results (self , tree ) :
138
+ def _from_tree_results (self , tree : 'TreeResult' ) -> None :
131
139
"""
132
140
Populate this object by parsing an existing reference-style result dictionary.
133
141
:param tree: A TreeResult
@@ -149,7 +157,7 @@ def _from_tree_results(self, tree):
149
157
self ._from_tree_deep_distance (tree )
150
158
self ._from_tree_custom_results (tree )
151
159
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 :
153
161
if report_type in tree :
154
162
155
163
for change in tree [report_type ]: # report each change
@@ -291,9 +299,9 @@ def _from_tree_custom_results(self, tree):
291
299
292
300
293
301
class DeltaResult (TextResult ):
294
- ADD_QUOTES_TO_STRINGS = False
302
+ ADD_QUOTES_TO_STRINGS : bool = False
295
303
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 :
297
305
self .ignore_order = ignore_order
298
306
self .always_include_values = always_include_values
299
307
@@ -517,15 +525,15 @@ class DiffLevel:
517
525
"""
518
526
519
527
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 :
529
537
"""
530
538
:param child_rel1: Either:
531
539
- An existing ChildRelationship object describing the "down" relationship for t1; or
@@ -581,7 +589,7 @@ def __init__(self,
581
589
582
590
self .verbose_level = verbose_level
583
591
584
- def __repr__ (self ):
592
+ def __repr__ (self ) -> str :
585
593
if self .verbose_level :
586
594
from deepdiff .summarize import summarize
587
595
@@ -596,7 +604,7 @@ def __repr__(self):
596
604
result = "<{}>" .format (self .path ())
597
605
return result
598
606
599
- def __setattr__ (self , key , value ) :
607
+ def __setattr__ (self , key : str , value : Any ) -> None :
600
608
# Setting up or down, will set the opposite link in this linked list.
601
609
if key in UP_DOWN and value is not None :
602
610
self .__dict__ [key ] = value
@@ -605,15 +613,15 @@ def __setattr__(self, key, value):
605
613
else :
606
614
self .__dict__ [key ] = value
607
615
608
- def __iter__ (self ):
616
+ def __iter__ (self ) -> Any :
609
617
yield self .t1
610
618
yield self .t2
611
619
612
620
@property
613
- def repetition (self ):
621
+ def repetition (self ) -> Dict [ str , Any ] :
614
622
return self .additional ['repetition' ]
615
623
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 :
617
625
"""
618
626
Auto-populate self.child_rel1 and self.child_rel2.
619
627
This requires self.down to be another valid DiffLevel object.
@@ -630,7 +638,7 @@ def auto_generate_child_rel(self, klass, param, param2=None):
630
638
klass = klass , parent = self .t2 , child = self .down .t2 , param = param if param2 is None else param2 ) # type: ignore
631
639
632
640
@property
633
- def all_up (self ):
641
+ def all_up (self ) -> 'DiffLevel' :
634
642
"""
635
643
Get the root object of this comparison.
636
644
(This is a convenient wrapper for following the up attribute as often as you can.)
@@ -642,7 +650,7 @@ def all_up(self):
642
650
return level
643
651
644
652
@property
645
- def all_down (self ):
653
+ def all_down (self ) -> 'DiffLevel' :
646
654
"""
647
655
Get the leaf object of this comparison.
648
656
(This is a convenient wrapper for following the down attribute as often as you can.)
@@ -654,10 +662,10 @@ def all_down(self):
654
662
return level
655
663
656
664
@staticmethod
657
- def _format_result (root , result ) :
665
+ def _format_result (root : str , result : Optional [ str ]) -> Optional [ str ] :
658
666
return None if result is None else "{}{}" .format (root , result )
659
667
660
- def get_root_key (self , use_t2 = False ):
668
+ def get_root_key (self , use_t2 : bool = False ) -> Any :
661
669
"""
662
670
Get the path's root key value for this change
663
671
@@ -674,7 +682,7 @@ def get_root_key(self, use_t2=False):
674
682
return next_rel .param
675
683
return notpresent
676
684
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 :
678
686
"""
679
687
A python syntax string describing how to descend to this level, assuming the top level object is called root.
680
688
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
765
773
output = (self ._format_result (root , parent ), param , self ._format_result (root , result )) # type: ignore
766
774
else :
767
775
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
769
777
else :
770
778
output = result
771
779
return output
772
780
773
781
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' :
780
788
"""
781
789
Start a new comparison level and correctly link it to this one.
782
790
:rtype: DiffLevel
@@ -791,12 +799,12 @@ def create_deeper(self,
791
799
return result
792
800
793
801
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' :
800
808
"""
801
809
Branch this comparison: Do not touch this comparison line, but create a new one with exactly the same content,
802
810
just one level deeper.
@@ -807,7 +815,7 @@ def branch_deeper(self,
807
815
return branch .create_deeper (new_t1 , new_t2 , child_relationship_class ,
808
816
child_relationship_param , child_relationship_param2 , report_type )
809
817
810
- def copy (self ):
818
+ def copy (self ) -> 'DiffLevel' :
811
819
"""
812
820
Get a deep copy of this comparision line.
813
821
:return: The leaf ("downmost") object of the copy.
@@ -850,20 +858,20 @@ class ChildRelationship:
850
858
851
859
# Format to a be used for representing param.
852
860
# 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
854
862
855
863
# This is a hook allowing subclasses to manipulate param strings.
856
864
# :param string: Input string
857
865
# :return: Manipulated string, as appropriate in this context.
858
- quote_str = None
866
+ quote_str : Optional [ str ] = None
859
867
860
868
@staticmethod
861
- def create (klass , parent , child , param = None ):
869
+ def create (klass : Type [ 'ChildRelationship' ] , parent : Any , child : Any , param : Optional [ Any ] = None ) -> 'ChildRelationship' :
862
870
if not issubclass (klass , ChildRelationship ):
863
871
raise TypeError
864
872
return klass (parent , child , param )
865
873
866
- def __init__ (self , parent , child , param = None ):
874
+ def __init__ (self , parent : Any , child : Any , param : Optional [ Any ] = None ) -> None :
867
875
# The parent object of this relationship, e.g. a dict
868
876
self .parent = parent
869
877
@@ -873,7 +881,7 @@ def __init__(self, parent, child, param=None):
873
881
# A subclass-dependent parameter describing how to get from parent to child, e.g. the key in a dict
874
882
self .param = param
875
883
876
- def __repr__ (self ):
884
+ def __repr__ (self ) -> str :
877
885
from deepdiff .summarize import summarize
878
886
879
887
name = "<{} parent:{}, child:{}, param:{}>"
@@ -882,7 +890,7 @@ def __repr__(self):
882
890
param = summarize (self .param , max_length = 15 )
883
891
return name .format (self .__class__ .__name__ , parent , child , param )
884
892
885
- def get_param_repr (self , force = None ):
893
+ def get_param_repr (self , force : Optional [ str ] = None ) -> Optional [ str ] :
886
894
"""
887
895
Returns a formatted param python parsable string describing this relationship,
888
896
or None if the relationship is not representable as a string.
@@ -899,7 +907,7 @@ def get_param_repr(self, force=None):
899
907
"""
900
908
return self .stringify_param (force )
901
909
902
- def stringify_param (self , force = None ):
910
+ def stringify_param (self , force : Optional [ str ] = None ) -> Optional [ str ] :
903
911
"""
904
912
Convert param to a string. Return None if there is no string representation.
905
913
This is called by get_param_repr()
@@ -946,13 +954,13 @@ def stringify_param(self, force=None):
946
954
947
955
948
956
class DictRelationship (ChildRelationship ):
949
- param_repr_format = "[{}]"
950
- quote_str = "'{}'"
957
+ param_repr_format : Optional [ str ] = "[{}]"
958
+ quote_str : Optional [ str ] = "'{}'"
951
959
952
960
953
961
class NumpyArrayRelationship (ChildRelationship ):
954
- param_repr_format = "[{}]"
955
- quote_str = None
962
+ param_repr_format : Optional [ str ] = "[{}]"
963
+ quote_str : Optional [ str ] = None
956
964
957
965
958
966
class SubscriptableIterableRelationship (DictRelationship ):
@@ -970,9 +978,9 @@ class SetRelationship(InaccessibleRelationship):
970
978
971
979
class NonSubscriptableIterableRelationship (InaccessibleRelationship ):
972
980
973
- param_repr_format = "[{}]"
981
+ param_repr_format : Optional [ str ] = "[{}]"
974
982
975
- def get_param_repr (self , force = None ):
983
+ def get_param_repr (self , force : Optional [ str ] = None ) -> Optional [ str ] :
976
984
if force == 'yes' :
977
985
result = "(unrepresentable)"
978
986
elif force == 'fake' and self .param :
@@ -984,4 +992,4 @@ def get_param_repr(self, force=None):
984
992
985
993
986
994
class AttributeRelationship (ChildRelationship ):
987
- param_repr_format = ".{}"
995
+ param_repr_format : Optional [ str ] = ".{}"
0 commit comments