1313from enum import Enum
1414from copy import deepcopy
1515from math import isclose as is_close
16- from typing import List , Dict , Callable , Union , Any , Pattern , Tuple , Optional , Set , FrozenSet , TYPE_CHECKING , Protocol
16+ from typing import List , Dict , Callable , Union , Any , Pattern , Tuple , Optional , Set , FrozenSet , TYPE_CHECKING , Protocol , Literal
1717from collections .abc import Mapping , Iterable , Sequence
1818from collections import defaultdict
1919from inspect import getmembers
6767PROGRESS_MSG = "DeepDiff {} seconds in progress. Pass #{}, Diff #{}"
6868
6969
70- def _report_progress (_stats , progress_logger , duration ) :
70+ def _report_progress (_stats : Dict [ str , Any ], progress_logger : Callable [[ str ], None ], duration : float ) -> None :
7171 """
7272 Report the progress every few seconds.
7373 """
@@ -130,6 +130,7 @@ class DeepDiffProtocol(Protocol):
130130 use_log_scale : bool
131131 log_scale_similarity_threshold : float
132132 view : str
133+ math_epsilon : Optional [float ]
133134
134135
135136
@@ -141,7 +142,7 @@ class DeepDiff(ResultDict, SerializationMixin, DistanceMixin, DeepDiffProtocol,
141142 def __init__ (self ,
142143 t1 : Any ,
143144 t2 : Any ,
144- _original_type = None ,
145+ _original_type : Optional [ Any ] = None ,
145146 cache_purge_level : int = 1 ,
146147 cache_size : int = 0 ,
147148 cache_tuning_sample_size : int = 0 ,
@@ -154,12 +155,12 @@ def __init__(self,
154155 exclude_obj_callback_strict : Optional [Callable ]= None ,
155156 exclude_paths : Union [str , List [str ], Set [str ], FrozenSet [str ], None ]= None ,
156157 exclude_regex_paths : Union [str , List [str ], Pattern [str ], List [Pattern [str ]], None ]= None ,
157- exclude_types : Optional [List [Any ]]= None ,
158+ exclude_types : Optional [List [type ]]= None ,
158159 get_deep_distance : bool = False ,
159160 group_by : Union [str , Tuple [str , str ], None ]= None ,
160161 group_by_sort_key : Union [str , Callable , None ]= None ,
161162 hasher : Optional [Callable ]= None ,
162- hashes : Optional [Dict ]= None ,
163+ hashes : Optional [Dict [ Any , Any ] ]= None ,
163164 ignore_encoding_errors : bool = False ,
164165 ignore_nan_inequality : bool = False ,
165166 ignore_numeric_type_changes : bool = False ,
@@ -168,7 +169,7 @@ def __init__(self,
168169 ignore_private_variables : bool = True ,
169170 ignore_string_case : bool = False ,
170171 ignore_string_type_changes : bool = False ,
171- ignore_type_in_groups : Optional [List [Tuple ]]= None ,
172+ ignore_type_in_groups : Optional [List [Tuple [ Any , ...] ]]= None ,
172173 ignore_type_subclasses : bool = False ,
173174 ignore_uuid_types : bool = False ,
174175 include_obj_callback : Optional [Callable ]= None ,
@@ -181,9 +182,9 @@ def __init__(self,
181182 math_epsilon : Optional [float ]= None ,
182183 max_diffs : Optional [int ]= None ,
183184 max_passes : int = 10000000 ,
184- number_format_notation : str = "f" ,
185+ number_format_notation : Literal [ "f" , "e" ] = "f" ,
185186 number_to_string_func : Optional [Callable ]= None ,
186- progress_logger : Callable = logger .info ,
187+ progress_logger : Callable [[ str ], None ] = logger .info ,
187188 report_repetition : bool = False ,
188189 significant_digits : Optional [int ]= None ,
189190 threshold_to_diff_deeper : float = 0.33 ,
@@ -193,8 +194,8 @@ def __init__(self,
193194 verbose_level : int = 1 ,
194195 view : str = TEXT_VIEW ,
195196 zip_ordered_iterables : bool = False ,
196- _parameters = None ,
197- _shared_parameters = None ,
197+ _parameters : Optional [ Dict [ str , Any ]] = None ,
198+ _shared_parameters : Optional [ Dict [ str , Any ]] = None ,
198199 ** kwargs ):
199200 super ().__init__ ()
200201 if kwargs :
@@ -443,8 +444,8 @@ def custom_report_result(self, report_type, level, extra_info=None):
443444 self .tree [report_type ].add (level )
444445
445446 @staticmethod
446- def _dict_from_slots (object ) :
447- def unmangle (attribute ) :
447+ def _dict_from_slots (object : Any ) -> Dict [ str , Any ] :
448+ def unmangle (attribute : str ) -> str :
448449 if attribute .startswith ('__' ) and attribute != '__weakref__' :
449450 return '_{type}{attribute}' .format (
450451 type = type (object ).__name__ ,
@@ -469,7 +470,7 @@ def unmangle(attribute):
469470
470471 return {i : getattr (object , key ) for i in all_slots if hasattr (object , key := unmangle (i ))}
471472
472- def _diff_enum (self , level , parents_ids = frozenset (), local_tree = None ):
473+ def _diff_enum (self , level : Any , parents_ids : FrozenSet [ int ] = frozenset (), local_tree : Optional [ Any ] = None ) -> None :
473474 t1 = detailed__dict__ (level .t1 , include_keys = ENUM_INCLUDE_KEYS )
474475 t2 = detailed__dict__ (level .t2 , include_keys = ENUM_INCLUDE_KEYS )
475476
@@ -483,9 +484,11 @@ def _diff_enum(self, level, parents_ids=frozenset(), local_tree=None):
483484 local_tree = local_tree ,
484485 )
485486
486- def _diff_obj (self , level , parents_ids = frozenset (), is_namedtuple = False , local_tree = None , is_pydantic_object = False ):
487+ def _diff_obj (self , level : Any , parents_ids : FrozenSet [ int ] = frozenset (), is_namedtuple : bool = False , local_tree : Optional [ Any ] = None , is_pydantic_object : bool = False ) -> None :
487488 """Difference of 2 objects"""
488489 processing_error = False
490+ t1 : Optional [Dict [str , Any ]] = None
491+ t2 : Optional [Dict [str , Any ]] = None
489492 try :
490493 if is_namedtuple :
491494 t1 = level .t1 ._asdict ()
@@ -504,7 +507,7 @@ def _diff_obj(self, level, parents_ids=frozenset(), is_namedtuple=False, local_t
504507 t2 = {k : v for k , v in getmembers (level .t2 ) if not callable (v )}
505508 except AttributeError :
506509 processing_error = True
507- if processing_error is True :
510+ if processing_error is True or t1 is None or t2 is None :
508511 self ._report_result ('unprocessed' , level , local_tree = local_tree )
509512 return
510513
@@ -518,7 +521,7 @@ def _diff_obj(self, level, parents_ids=frozenset(), is_namedtuple=False, local_t
518521 local_tree = local_tree ,
519522 )
520523
521- def _skip_this (self , level ) :
524+ def _skip_this (self , level : Any ) -> bool :
522525 """
523526 Check whether this comparison should be skipped because one of the objects to compare meets exclusion criteria.
524527 :rtype: bool
@@ -559,7 +562,7 @@ def _skip_this(self, level):
559562
560563 return skip
561564
562- def _skip_this_key (self , level , key ) :
565+ def _skip_this_key (self , level : Any , key : Any ) -> bool :
563566 # if include_paths is not set, than treet every path as included
564567 if self .include_paths is None :
565568 return False
@@ -585,7 +588,7 @@ def _skip_this_key(self, level, key):
585588 up = up .up
586589 return True
587590
588- def _get_clean_to_keys_mapping (self , keys , level ) :
591+ def _get_clean_to_keys_mapping (self , keys : Any , level : Any ) -> Dict [ Any , Any ] :
589592 """
590593 Get a dictionary of cleaned value of keys to the keys themselves.
591594 This is mainly used to transform the keys when the type changes of keys should be ignored.
@@ -607,14 +610,14 @@ def _get_clean_to_keys_mapping(self, keys, level):
607610 clean_key = key
608611 else :
609612 clean_key = self .number_to_string (key , significant_digits = self .significant_digits ,
610- number_format_notation = self .number_format_notation )
613+ number_format_notation = self .number_format_notation ) # type: ignore # type: ignore
611614 else :
612615 type_ = "number" if self .ignore_numeric_type_changes else key .__class__ .__name__
613616 if self .significant_digits is None :
614617 clean_key = key
615618 else :
616619 clean_key = self .number_to_string (key , significant_digits = self .significant_digits ,
617- number_format_notation = self .number_format_notation )
620+ number_format_notation = self .number_format_notation ) # type: ignore # type: ignore
618621 clean_key = KEY_TO_VAL_STR .format (type_ , clean_key )
619622 else :
620623 clean_key = key
@@ -630,14 +633,14 @@ def _get_clean_to_keys_mapping(self, keys, level):
630633
631634 def _diff_dict (
632635 self ,
633- level ,
634- parents_ids = frozenset ([]),
635- print_as_attribute = False ,
636- override = False ,
637- override_t1 = None ,
638- override_t2 = None ,
639- local_tree = None ,
640- ):
636+ level : Any ,
637+ parents_ids : FrozenSet [ int ] = frozenset ([]),
638+ print_as_attribute : bool = False ,
639+ override : bool = False ,
640+ override_t1 : Optional [ Any ] = None ,
641+ override_t2 : Optional [ Any ] = None ,
642+ local_tree : Optional [ Any ] = None ,
643+ ) -> None :
641644 """Difference of 2 dictionaries"""
642645 if override :
643646 # for special stuff like custom objects and named tuples we receive preprocessed t1 and t2
@@ -735,7 +738,7 @@ def _diff_dict(
735738 )
736739 self ._diff (next_level , parents_ids_added , local_tree = local_tree )
737740
738- def _diff_set (self , level , local_tree = None ):
741+ def _diff_set (self , level : Any , local_tree : Optional [ Any ] = None ) -> None :
739742 """Difference of sets"""
740743 t1_hashtable = self ._create_hashtable (level , 't1' )
741744 t2_hashtable = self ._create_hashtable (level , 't2' )
@@ -766,7 +769,7 @@ def _diff_set(self, level, local_tree=None):
766769 self ._report_result ('set_item_removed' , change_level , local_tree = local_tree )
767770
768771 @staticmethod
769- def _iterables_subscriptable (t1 , t2 ) :
772+ def _iterables_subscriptable (t1 : Any , t2 : Any ) -> bool :
770773 try :
771774 if getattr (t1 , '__getitem__' ) and getattr (t2 , '__getitem__' ):
772775 return True
@@ -775,7 +778,7 @@ def _iterables_subscriptable(t1, t2):
775778 except AttributeError :
776779 return False
777780
778- def _diff_iterable (self , level , parents_ids = frozenset (), _original_type = None , local_tree = None ):
781+ def _diff_iterable (self , level : Any , parents_ids : FrozenSet [ int ] = frozenset (), _original_type : Optional [ type ] = None , local_tree : Optional [ Any ] = None ) -> None :
779782 """Difference of iterables"""
780783 if (self .ignore_order_func and self .ignore_order_func (level )) or self .ignore_order :
781784 self ._diff_iterable_with_deephash (level , parents_ids , _original_type = _original_type , local_tree = local_tree )
@@ -919,7 +922,7 @@ def _diff_iterable_in_order(self, level, parents_ids=frozenset(), _original_type
919922 local_tree = local_tree ,
920923 )
921924
922- def _all_values_basic_hashable (self , iterable ) :
925+ def _all_values_basic_hashable (self , iterable : Iterable [ Any ]) -> bool :
923926 """
924927 Are all items basic hashable types?
925928 Or there are custom types too?
@@ -1540,10 +1543,10 @@ def _diff_numbers(self, level, local_tree=None, report_type_change=True):
15401543 # For Decimals, format seems to round 2.5 to 2 and 3.5 to 4 (to closest even number)
15411544 t1_s = self .number_to_string (level .t1 ,
15421545 significant_digits = self .significant_digits ,
1543- number_format_notation = self .number_format_notation )
1546+ number_format_notation = self .number_format_notation ) # type: ignore
15441547 t2_s = self .number_to_string (level .t2 ,
15451548 significant_digits = self .significant_digits ,
1546- number_format_notation = self .number_format_notation )
1549+ number_format_notation = self .number_format_notation ) # type: ignore
15471550
15481551 t1_s = KEY_TO_VAL_STR .format (t1_type , t1_s )
15491552 t2_s = KEY_TO_VAL_STR .format (t2_type , t2_s )
0 commit comments