11"""CheckType Implementation."""
2- from typing import Mapping , Tuple , List , Dict , Any
3- from .evaluator import diff_generator , parameter_evaluator , regex_evaluator
4- from .runner import extract_values_from_output
2+ import re
3+ from typing import Mapping , Tuple , List , Dict , Any , Union
4+ import jmespath
5+
6+ from .utils .jmespath_parsers import (
7+ jmespath_value_parser ,
8+ jmespath_refkey_parser ,
9+ associate_key_of_my_value ,
10+ keys_cleaner ,
11+ keys_values_zipper ,
12+ )
13+ from .utils .data_normalization import exclude_filter , flatten_list
14+ from .evaluators import diff_generator , parameter_evaluator , regex_evaluator
515
616
717class CheckType :
@@ -26,12 +36,58 @@ def init(*args):
2636 return ParameterMatchType (* args )
2737 if check_type == "regex" :
2838 return RegexType (* args )
39+
2940 raise NotImplementedError
3041
3142 @staticmethod
32- def get_value (output : Mapping , path : str , exclude : List = None ) -> Any :
33- """Return the value contained into a Mapping for a defined path."""
34- return extract_values_from_output (output , path , exclude )
43+ def get_value (output : Union [Mapping , List ], path : str , exclude : List = None ) -> Any :
44+ """Return data from output depending on the check path. See unit test for complete example.
45+
46+ Get the wanted values to be evaluated if JMESPath expression is defined,
47+ otherwise use the entire output if jmespath is not defined in check. This covers the "raw" diff type.
48+ Exclude data not desired to compare.
49+
50+ Notes:
51+ https://jmespath.org/ shows how JMESPath works.
52+
53+ Args:
54+ output: json data structure
55+ path: JMESPath to extract specific values
56+ exclude: list of keys to exclude
57+ Returns:
58+ Evaluated data, may be anything depending on JMESPath used.
59+ """
60+ if exclude and isinstance (output , Dict ):
61+ exclude_filter (output , exclude ) # exclude unwanted elements
62+
63+ if not path :
64+ return output # return if path is not specified
65+
66+ values = jmespath .search (jmespath_value_parser (path ), output )
67+
68+ if not any (isinstance (i , list ) for i in values ): # check for multi-nested lists if not found return here
69+ return values
70+
71+ for element in values : # process elements to check is lists should be flatten
72+ # TODO: Not sure how this is working becasyse from `jmespath.search` it's supposed to get a flat list
73+ # of str or Decimals, not another list...
74+ for item in element :
75+ if isinstance (item , dict ): # raise if there is a dict, path must be more specific to extract data
76+ raise TypeError (
77+ f'Must be list of lists i.e. [["Idle", 75759616], ["Idle", 75759620]].' f"You have { values } '."
78+ )
79+ if isinstance (item , list ):
80+ values = flatten_list (values ) # flatten list and rewrite values
81+ break # items are the same, need to check only first to see if this is a nested list
82+
83+ paired_key_value = associate_key_of_my_value (jmespath_value_parser (path ), values )
84+
85+ if re .search (r"\$.*\$" , path ): # normalize
86+ wanted_reference_keys = jmespath .search (jmespath_refkey_parser (path ), output )
87+ list_of_reference_keys = keys_cleaner (wanted_reference_keys )
88+ return keys_values_zipper (list_of_reference_keys , paired_key_value )
89+
90+ return values
3591
3692 def evaluate (self , reference_value : Any , value_to_compare : Any ) -> Tuple [Dict , bool ]:
3793 """Return the result of the evaluation and a boolean True if it passes it or False otherwise.
@@ -53,22 +109,22 @@ class ExactMatchType(CheckType):
53109
54110 def evaluate (self , reference_value : Any , value_to_compare : Any ) -> Tuple [Dict , bool ]:
55111 """Returns the difference between values and the boolean."""
56- diff = diff_generator (reference_value , value_to_compare )
57- return diff , not diff
112+ evaluation_result = diff_generator (reference_value , value_to_compare )
113+ return evaluation_result , not evaluation_result
58114
59115
60116class ToleranceType (CheckType ):
61117 """Tolerance class docstring."""
62118
63119 def __init__ (self , * args ):
64120 """Tolerance init method."""
121+ super ().__init__ ()
122+
65123 try :
66124 tolerance = args [1 ]
67125 except IndexError as error :
68- raise f"Tolerance parameter must be defined as float at index 1. You have: { args } " from error
69-
126+ raise ValueError (f"Tolerance parameter must be defined as float at index 1. You have: { args } " ) from error
70127 self .tolerance_factor = float (tolerance ) / 100
71- super ().__init__ ()
72128
73129 def evaluate (self , reference_value : Mapping , value_to_compare : Mapping ) -> Tuple [Dict , bool ]:
74130 """Returns the difference between values and the boolean. Overwrites method in base class."""
@@ -78,20 +134,21 @@ def evaluate(self, reference_value: Mapping, value_to_compare: Mapping) -> Tuple
78134
79135 def _remove_within_tolerance (self , diff : Dict ) -> None :
80136 """Recursively look into diff and apply tolerance check, remove reported difference when within tolerance."""
137+
138+ def _within_tolerance (* , old_value : float , new_value : float ) -> bool :
139+ """Return True if new value is within the tolerance range of the previous value."""
140+ max_diff = old_value * self .tolerance_factor
141+ return (old_value - max_diff ) < new_value < (old_value + max_diff )
142+
81143 for key , value in list (diff .items ()): # casting list makes copy, so we don't modify object being iterated.
82144 if isinstance (value , dict ):
83- if "new_value" in value .keys () and "old_value" in value .keys () and self . _within_tolerance (** value ):
145+ if "new_value" in value .keys () and "old_value" in value .keys () and _within_tolerance (** value ):
84146 diff .pop (key )
85147 else :
86148 self ._remove_within_tolerance (diff [key ])
87149 if not value :
88150 diff .pop (key )
89151
90- def _within_tolerance (self , * , old_value : float , new_value : float ) -> bool :
91- """Return True if new value is within the tolerance range of the previous value."""
92- max_diff = old_value * self .tolerance_factor
93- return (old_value - max_diff ) < new_value < (old_value + max_diff )
94-
95152
96153class ParameterMatchType (CheckType ):
97154 """Parameter Match class implementation."""
@@ -101,12 +158,14 @@ def evaluate(self, reference_value: Mapping, value_to_compare: Mapping) -> Tuple
101158 try :
102159 parameter = value_to_compare [1 ]
103160 except IndexError as error :
104- raise f"Evaluating parameter must be defined as dict at index 1. You have: { value_to_compare } " from error
161+ raise ValueError (
162+ f"Evaluating parameter must be defined as dict at index 1. You have: { value_to_compare } "
163+ ) from error
105164 if not isinstance (parameter , dict ):
106165 raise TypeError ("check_option must be of type dict()" )
107166
108- diff = parameter_evaluator (reference_value , parameter )
109- return diff , not diff
167+ evaluation_result = parameter_evaluator (reference_value , parameter )
168+ return evaluation_result , not evaluation_result
110169
111170
112171class RegexType (CheckType ):
@@ -140,28 +199,3 @@ def evaluate(self, reference_value: Mapping, value_to_compare: Mapping) -> Tuple
140199
141200 diff = regex_evaluator (reference_value , parameter )
142201 return diff , not diff
143-
144-
145- # TODO: compare is no longer the entry point, we should use the libary as:
146- # netcompare_check = CheckType.init(check_type_info, options)
147- # pre_result = netcompare_check.get_value(pre_obj, path)
148- # post_result = netcompare_check.get_value(post_obj, path)
149- # netcompare_check.evaluate(pre_result, post_result)
150- #
151- # def compare(
152- # pre_obj: Mapping, post_obj: Mapping, path: Mapping, type_info: Iterable, options: Mapping
153- # ) -> Tuple[Mapping, bool]:
154- # """Entry point function.
155-
156- # Returns a diff object and the boolean of the comparison.
157- # """
158-
159- # type_info = type_info.lower()
160-
161- # try:
162- # type_obj = CheckType.init(type_info, options)
163- # except Exception:
164- # # We will be here if we can't infer the type_obj
165- # raise
166-
167- # return type_obj.evaluate(pre_obj, post_obj, path)
0 commit comments