Coverage for jsonschema_diff/core/tools/compare.py: 95%

20 statements  

« prev     ^ index     » next       coverage.py v7.10.5, created at 2025-08-25 07:00 +0000

1from __future__ import annotations 

2 

3from types import NoneType 

4from typing import TYPE_CHECKING, Any, TypeAlias, cast 

5 

6if TYPE_CHECKING: # prevents import cycle 

7 from .. import Compare 

8 

9 

10COMPARE_RULES_TYPE: TypeAlias = dict[ 

11 type | tuple[type, type] | str | tuple[str, type, type] | tuple[str, type], 

12 type["Compare"], 

13] 

14"""Mapping *search pattern* → *Compare* subclass.""" 

15 

16 

17class CompareRules: 

18 """Pick an appropriate comparator class according to rule precedence.""" 

19 

20 # ------------------------------------------------------------------ # 

21 # Public helpers 

22 # ------------------------------------------------------------------ # 

23 

24 @staticmethod 

25 def get_comparator_from_values( 

26 rules: COMPARE_RULES_TYPE, 

27 default: type["Compare"], 

28 key: str, 

29 old: Any, 

30 new: Any, 

31 ) -> type["Compare"]: 

32 """Wrapper that resolves comparator from **values**.""" 

33 return CompareRules.get_comparator(rules, default, key, type(old), type(new)) 

34 

35 @staticmethod 

36 def get_comparator( 

37 rules: COMPARE_RULES_TYPE, 

38 default: type["Compare"], 

39 key: str, 

40 old: type, 

41 new: type, 

42 ) -> type["Compare"]: 

43 """ 

44 Resolve a comparator class according to the following lookup order: 

45 

46 1. ``(key, old_type, new_type)`` 

47 2. ``key`` 

48 3. ``(old_type, new_type)`` 

49 4. ``old_type`` or ``new_type`` (if one of them is ``NoneType``) 

50 5. *default* 

51 

52 Parameters 

53 ---------- 

54 rules : dict 

55 Precedence map. 

56 default : Compare subclass 

57 Fallback comparator. 

58 key : str 

59 Field name. 

60 old, new : type 

61 Types being compared. 

62 """ 

63 for search in [ 

64 ((key, old, new)), 

65 (key), 

66 ((old, new)), 

67 ]: 

68 tuple_types = rules.get(cast(Any, search), None) 

69 if tuple_types is not None: 

70 return tuple_types 

71 else: 

72 if old is NoneType: 

73 return rules.get(new, default) 

74 elif old is not NoneType or old is new: 

75 return rules.get(old, default) 

76 else: 

77 return default