Skip to content

Commit dcb6564

Browse files
author
Patryk Szulczewski
committed
Next round of refactor + typing fixes.
1 parent 9ed2e7c commit dcb6564

File tree

5 files changed

+66
-63
lines changed

5 files changed

+66
-63
lines changed

netcompare/check_type.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""CheckType Implementation."""
2-
from typing import Mapping, Tuple, Union, List
2+
from typing import Mapping, Iterable, Tuple, Union, List
33
from .evaluator import diff_generator
44
from .runner import extract_values_from_output
55

@@ -22,13 +22,14 @@ def init(*args):
2222

2323
@staticmethod
2424
def extract_value_from_json_path(
25-
value: Mapping, path: Mapping, exclude: List = None
25+
output: Union[Mapping, Iterable], path: str, exclude: List = None
2626
) -> Union[Mapping, List, int, str, bool]:
2727
"""Return the value contained into a Mapping for a defined path."""
28-
return extract_values_from_output(value, path, exclude)
28+
return extract_values_from_output(output, path, exclude)
2929

30-
# TODO: Refine this typing
31-
def evaluate(self, reference_value: Mapping, value_to_compare: Mapping) -> Tuple[Mapping, bool]:
30+
def evaluate(
31+
self, reference_value: Union[Mapping, Iterable], value_to_compare: Union[Mapping, Iterable]
32+
) -> Tuple[Mapping, bool]:
3233
"""Return the result of the evaluation and a boolean True if it passes it or False otherwise.
3334
3435
This method is the one that each CheckType has to implement.
@@ -39,7 +40,9 @@ def evaluate(self, reference_value: Mapping, value_to_compare: Mapping) -> Tuple
3940
class ExactMatchType(CheckType):
4041
"""Exact Match class docstring."""
4142

42-
def evaluate(self, reference_value: Mapping, value_to_compare: Mapping) -> Tuple[Mapping, bool]:
43+
def evaluate(
44+
self, reference_value: Union[Mapping, Iterable], value_to_compare: Union[Mapping, Iterable]
45+
) -> Tuple[Mapping, bool]:
4346
"""Returns the difference between values and the boolean."""
4447
diff = diff_generator(reference_value, value_to_compare)
4548
return diff, not diff

netcompare/evaluator.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import sys
44
from collections import defaultdict
55
from functools import partial
6-
from typing import Mapping, List, Dict
6+
from typing import Mapping, List, Dict, Union, Iterable
77
from deepdiff import DeepDiff
88

99

1010
sys.path.append(".")
1111

1212

13-
def diff_generator(pre_result: Mapping, post_result: Mapping) -> Dict:
13+
def diff_generator(pre_result: Union[Mapping, Iterable], post_result: Union[Mapping, Iterable]) -> Dict:
1414
"""Generates diff between pre and post data based on check definition."""
1515
diff_result = DeepDiff(pre_result, post_result)
1616

@@ -27,13 +27,15 @@ def diff_generator(pre_result: Mapping, post_result: Mapping) -> Dict:
2727
return result
2828

2929

30-
def get_diff_iterables_items(diff_result: Mapping) -> Mapping:
31-
"""Return a dict with new and missing values where the values are in a list."""
32-
# DeepDiff iterable_items are returned when the source data is a list
33-
# and provided in the format: "root['Ethernet3'][1]"
34-
# or more generically: root['KEY']['KEY']['KEY']...[numeric_index]
35-
# where the KEYs are dict keys within the original object
36-
# and the "[index]" is appended to indicate the position within the list.
30+
def get_diff_iterables_items(diff_result: Mapping) -> Dict:
31+
"""Return a dict with new and missing values where the values are in a list.
32+
33+
DeepDiff iterable_items are returned when the source data is a list
34+
and provided in the format: "root['Ethernet3'][1]"
35+
or more generically: root['KEY']['KEY']['KEY']...[numeric_index]
36+
where the KEYs are dict keys within the original object
37+
and the "[index]" is appended to indicate the position within the list.
38+
"""
3739
get_dict_keys = re.compile(r"^root((\['\w.*'\])+)\[\d+\]$")
3840

3941
defaultdict_list = partial(defaultdict, list)
@@ -54,27 +56,29 @@ def get_diff_iterables_items(diff_result: Mapping) -> Mapping:
5456
return result
5557

5658

57-
def fix_deepdiff_key_names(obj: Mapping) -> Mapping:
58-
"""Return a dict based on the provided dict object where the brackets and quotes are removed from the string."""
59+
def fix_deepdiff_key_names(obj: Mapping) -> Dict:
60+
"""Return a dict based on the provided dict object where the brackets and quotes are removed from the string.
61+
62+
obj sample: root[2]['10.64.207.255']['accepted_prefixes']
63+
"""
5964
pattern = r"'([A-Za-z0-9_\./\\-]*)'"
6065

6166
result = {}
62-
# root[2]['10.64.207.255']['accepted_prefixes']
6367
for key, value in obj.items():
6468
key_parts = re.findall(pattern, key)
6569
partial_res = group_value(key_parts, value)
6670
dict_merger(result, partial_res)
6771
return result
6872

6973

70-
def group_value(tree_list: List, value: Mapping) -> Mapping:
74+
def group_value(tree_list: List, value: Dict) -> Dict:
7175
"""Build dictionary based on value's key and reference key."""
7276
if tree_list:
7377
return {tree_list[0]: group_value(tree_list[1:], value)}
7478
return value
7579

7680

77-
def dict_merger(original_dict: Mapping, merged_dict: Mapping):
81+
def dict_merger(original_dict: Dict, merged_dict: Dict):
7882
"""Merge dictionaries to build final result."""
7983
for key in merged_dict.keys():
8084
if key in original_dict and isinstance(original_dict[key], dict) and isinstance(merged_dict[key], dict):

netcompare/runner.py

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,42 +8,40 @@
88
from .utils.flatten import flatten_list
99

1010

11-
def extract_values_from_output(value: Mapping, path: Mapping, exclude: List) -> Union[Mapping, List, int, str, bool]:
12-
"""Return data from output depending on the check path. See unit text for complete example."""
13-
# Get the wanted values to be evaluated if jmspath expression is defined, otherwise
14-
# use the entire output if jmespath is not defined in check. This cover the "raw" diff type.
15-
if path and not exclude:
16-
wanted_value = jmespath.search(jmspath_value_parser(path), value)
17-
18-
elif path and exclude:
19-
wanted_value = jmespath.search(jmspath_value_parser(path), value)
20-
exclude_filter(wanted_value, exclude)
21-
elif not path and exclude:
22-
exclude_filter(value, exclude)
23-
return value
24-
25-
# data type check
26-
if path:
27-
if not any(isinstance(i, list) for i in wanted_value):
28-
return wanted_value
29-
30-
for element in wanted_value:
31-
for item in element:
32-
if isinstance(item, dict):
33-
raise TypeError(
34-
f'Must be list of lists i.e. [["Idle", 75759616], ["Idle", 75759620]]. You have {wanted_value}\'.'
35-
)
36-
if isinstance(item, list):
37-
wanted_value = flatten_list(wanted_value)
38-
break
39-
40-
paired_key_value = associate_key_of_my_value(jmspath_value_parser(path), wanted_value)
41-
else:
42-
paired_key_value = value
43-
44-
if path and re.search(r"\$.*\$", path):
45-
wanted_reference_keys = jmespath.search(jmspath_refkey_parser(path), value)
11+
def extract_values_from_output(
12+
output: Union[Mapping, List], path: str, exclude: List = None
13+
) -> Union[Mapping, List, int, str, bool]:
14+
"""Return data from output depending on the check path. See unit test for complete example.
15+
16+
Get the wanted values to be evaluated if jmspath expression is defined,
17+
otherwise use the entire output if jmespath is not defined in check. This covers the "raw" diff type.
18+
"""
19+
if exclude:
20+
exclude_filter(output, exclude) # exclude unwanted elements
21+
22+
if not path:
23+
return output # return if path is not specified
24+
25+
values = jmespath.search(jmspath_value_parser(path), output)
26+
27+
if not any(isinstance(i, list) for i in values): # check for multi-nested lists if not found return here
28+
return values
29+
30+
for element in values: # process elements to check is lists should be flatten
31+
for item in element:
32+
if isinstance(item, dict): # raise if there is a dict, path must be more specific to extract data
33+
raise TypeError(
34+
f'Must be list of lists i.e. [["Idle", 75759616], ["Idle", 75759620]].' f"You have {values}'."
35+
)
36+
if isinstance(item, list):
37+
values = flatten_list(values) # flatten list and rewrite values
38+
break # items are the same, need to check only first to see if this is a nested list
39+
40+
paired_key_value = associate_key_of_my_value(jmspath_value_parser(path), values)
41+
42+
if re.search(r"\$.*\$", path): # normalize
43+
wanted_reference_keys = jmespath.search(jmspath_refkey_parser(path), output)
4644
list_of_reference_keys = keys_cleaner(wanted_reference_keys)
4745
return keys_values_zipper(list_of_reference_keys, paired_key_value)
4846

49-
return paired_key_value
47+
return values

netcompare/utils/jmspath_parsers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
def jmspath_value_parser(path: str):
66
"""
7-
Get the JMSPath value path from 'path'.
7+
Get the JMSPath value path from 'path'.
88
99
Args:
1010
path: "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesReceived]"

netcompare/utils/refkey.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"""Reference key utilities."""
2-
from typing import Mapping, List
2+
from typing import Mapping, List, Union
33

44

5-
def keys_cleaner(wanted_reference_keys: Mapping) -> List[Mapping]:
5+
def keys_cleaner(wanted_reference_keys: Mapping) -> Union[List[Mapping], None]:
66
"""Get every required reference key from output."""
77
if isinstance(wanted_reference_keys, list):
88
return wanted_reference_keys
@@ -52,13 +52,11 @@ def associate_key_of_my_value(paths: str, wanted_value: List) -> List:
5252
final_list = []
5353

5454
for items in wanted_value:
55-
temp_dict = {}
56-
5755
if len(items) != len(my_key_value_list):
5856
raise ValueError("Key's value len != from value len")
5957

60-
for my_index, my_value in enumerate(items):
61-
temp_dict.update({my_key_value_list[my_index]: my_value})
58+
temp_dict = {my_key_value_list[my_index]: my_value for my_index, my_value in enumerate(items)}
59+
6260
final_list.append(temp_dict)
6361

6462
return final_list

0 commit comments

Comments
 (0)