Skip to content

Commit 49af479

Browse files
committed
move flatten funcs under utils
1 parent 2c46df4 commit 49af479

File tree

11 files changed

+188
-140
lines changed

11 files changed

+188
-140
lines changed

netcompare/runner.py

Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import re
33
import jmespath
44
from typing import Mapping, List, Generator, Union
5-
from .utils.jmspath.parsers import jmspath_value_parser, jmspath_refkey_parser, exclude_filter
5+
from .utils.jmspath.parsers import jmspath_value_parser, jmspath_refkey_parser
6+
from .utils.data.parsers import exclude_filter, get_values
67

78

89
def extract_values_from_output(value: Mapping, path: Mapping, exclude: List) -> Union[Mapping, List, int, str, bool]:
@@ -37,8 +38,7 @@ def extract_values_from_output(value: Mapping, path: Mapping, exclude: List) ->
3738
exclude_filter(wanted_value, exclude)
3839
filtered_value = wanted_value
3940

40-
pdb.set_trace()
41-
filtered_value = get_meaningful_values(path, wanted_value)
41+
filtered_value = get_values(path, wanted_value)
4242

4343
if path and re.search(r"\$.*\$", path):
4444
wanted_reference_keys = jmespath.search(jmspath_refkey_parser(path), value)
@@ -48,67 +48,6 @@ def extract_values_from_output(value: Mapping, path: Mapping, exclude: List) ->
4848
return filtered_value
4949

5050

51-
def get_meaningful_values(path: Mapping, wanted_value):
52-
if path:
53-
# check if list of lists
54-
if not any(isinstance(i, list) for i in wanted_value):
55-
raise TypeError(
56-
"Catching value must be defined as list in jmespath expression i.e. result[*].state -> result[*].[state]. You have {}'.".format(
57-
path
58-
)
59-
)
60-
for element in wanted_value:
61-
for item in element:
62-
if isinstance(item, dict):
63-
raise TypeError(
64-
'Must be list of lists i.e. [["Idle", 75759616], ["Idle", 75759620]]. You have {}\'.'.format(
65-
wanted_value
66-
)
67-
)
68-
elif isinstance(item, list):
69-
wanted_value = flatten_list(wanted_value)
70-
break
71-
72-
filtered_value = associate_key_of_my_value(jmspath_value_parser(path), wanted_value)
73-
else:
74-
filtered_value = wanted_value
75-
return filtered_value
76-
77-
78-
def flatten_list(my_list: List) -> List:
79-
"""
80-
Flatten a multi level nested list and returns a list of lists.
81-
82-
Args:
83-
my_list: nested list to be flattened.
84-
85-
Return:
86-
[[-1, 0], [-1, 0], [-1, 0], ...]
87-
88-
Example:
89-
>>> my_list = [[[[-1, 0], [-1, 0]]]]
90-
>>> flatten_list(my_list)
91-
[[-1, 0], [-1, 0]]
92-
"""
93-
if not isinstance(my_list, list):
94-
raise ValueError(f"Argument provided must be a list. You passed a {type(my_list)}")
95-
if is_flat_list(my_list):
96-
return my_list
97-
return list(iter_flatten_list(my_list))
98-
99-
100-
def iter_flatten_list(my_list: List) -> Generator[List, None, None]:
101-
"""Recursively yield all flat lists within a given list."""
102-
if is_flat_list(my_list):
103-
yield my_list
104-
else:
105-
for item in my_list:
106-
yield from iter_flatten_list(item)
107-
108-
109-
def is_flat_list(obj: List) -> bool:
110-
"""Return True is obj is a list that does not contain any lists as its first order elements."""
111-
return isinstance(obj, list) and not any(isinstance(i, list) for i in obj)
11251

11352

11453
def associate_key_of_my_value(paths: Mapping, wanted_value: List) -> List:

netcompare/utils/data/__init__.py

Whitespace-only changes.

netcompare/utils/data/parsers.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from typing import Mapping, List
2+
from ..jmspath.parsers import jmspath_value_parser
3+
from ..list.flatten import flatten_list
4+
from ...runner import associate_key_of_my_value
5+
6+
def exclude_filter(data: Mapping, exclude: List):
7+
"""
8+
Recusively look through all dict keys and pop out the one defined in "exclude".
9+
10+
Update in place existing dictionary. Look into unit test for example.
11+
12+
Args:
13+
data: {
14+
"interfaces": {
15+
"Management1": {
16+
"name": "Management1",
17+
"interfaceStatus": "connected",
18+
"autoNegotiate": "success",
19+
"interfaceStatistics": {
20+
"inBitsRate": 3403.4362520883615,
21+
"inPktsRate": 3.7424095978179257,
22+
"outBitsRate": 16249.69114419833,
23+
"updateInterval": 300,
24+
"outPktsRate": 2.1111866059750692
25+
},...
26+
exclude: ["interfaceStatistics", "interfaceCounters"]
27+
"""
28+
if isinstance(data, dict):
29+
for exclude_element in exclude:
30+
try:
31+
data.pop(exclude_element)
32+
except KeyError:
33+
pass
34+
35+
for key in data:
36+
if isinstance(data[key], dict) or isinstance(data[key], list):
37+
exclude_filter(data[key], exclude)
38+
39+
elif isinstance(data, list):
40+
for element in data:
41+
if isinstance(element, dict) or isinstance(element, list):
42+
exclude_filter(element, exclude)
43+
44+
45+
def get_values(path: Mapping, wanted_value):
46+
if path:
47+
# check if list of lists
48+
if not any(isinstance(i, list) for i in wanted_value):
49+
raise TypeError(
50+
"Catching value must be defined as list in jmespath expression i.e. result[*].state -> result[*].[state]. You have {}'.".format(
51+
path
52+
)
53+
)
54+
for element in wanted_value:
55+
for item in element:
56+
if isinstance(item, dict):
57+
raise TypeError(
58+
'Must be list of lists i.e. [["Idle", 75759616], ["Idle", 75759620]]. You have {}\'.'.format(
59+
wanted_value
60+
)
61+
)
62+
elif isinstance(item, list):
63+
wanted_value = flatten_list(wanted_value)
64+
break
65+
66+
filtered_value = associate_key_of_my_value(jmspath_value_parser(path), wanted_value)
67+
else:
68+
filtered_value = wanted_value
69+
return filtered_value

netcompare/utils/data/tests/__init__.py

Whitespace-only changes.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python3
2+
3+
import pytest
4+
import sys
5+
from ..parsers import exclude_filter
6+
sys.path.append("..")
7+
8+
9+
assertion_failed_message = """Test output is different from expected output.
10+
output: {output}
11+
expected output: {expected_output}
12+
"""
13+
14+
exclude_filter_case_1 = (
15+
["interfaceStatistics"],
16+
{
17+
"interfaces": {
18+
"Management1": {
19+
"name": "Management1",
20+
"interfaceStatus": "connected",
21+
"autoNegotiate": "success",
22+
"interfaceStatistics": {
23+
"inBitsRate": 3403.4362520883615,
24+
"inPktsRate": 3.7424095978179257,
25+
"outBitsRate": 16249.69114419833,
26+
"updateInterval": 300,
27+
"outPktsRate": 2.1111866059750692
28+
}
29+
}
30+
}
31+
},
32+
{
33+
"interfaces": {
34+
"Management1": {
35+
"name": "Management1",
36+
"interfaceStatus": "connected",
37+
"autoNegotiate": "success"
38+
}
39+
}
40+
}
41+
)
42+
43+
exclude_filter_tests = [
44+
exclude_filter_case_1,
45+
]
46+
47+
48+
@pytest.mark.parametrize("exclude, data, expected_output", exclude_filter_tests)
49+
def test_exclude_filter(exclude, data, expected_output):
50+
exclude_filter(data, exclude)
51+
assert expected_output == data, assertion_failed_message.format(output=data, expected_output=expected_output)

netcompare/utils/jmspath/parsers.py

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -46,41 +46,3 @@ def jmspath_refkey_parser(path: Mapping):
4646

4747
return ".".join(splitted_jmspath)
4848

49-
50-
def exclude_filter(data: Mapping, exclude: List):
51-
"""
52-
Recusively look through all dict keys and pop out the one defined in "exclude".
53-
54-
Update in place existing dictionary. Look into unit test for example.
55-
56-
Args:
57-
data: {
58-
"interfaces": {
59-
"Management1": {
60-
"name": "Management1",
61-
"interfaceStatus": "connected",
62-
"autoNegotiate": "success",
63-
"interfaceStatistics": {
64-
"inBitsRate": 3403.4362520883615,
65-
"inPktsRate": 3.7424095978179257,
66-
"outBitsRate": 16249.69114419833,
67-
"updateInterval": 300,
68-
"outPktsRate": 2.1111866059750692
69-
},...
70-
exclude: ["interfaceStatistics", "interfaceCounters"]
71-
"""
72-
if isinstance(data, dict):
73-
for exclude_element in exclude:
74-
try:
75-
data.pop(exclude_element)
76-
except KeyError:
77-
pass
78-
79-
for key in data:
80-
if isinstance(data[key], dict) or isinstance(data[key], list):
81-
exclude_filter(data[key], exclude)
82-
83-
elif isinstance(data, list):
84-
for element in data:
85-
if isinstance(element, dict) or isinstance(element, list):
86-
exclude_filter(element, exclude)

netcompare/utils/jmspath/tests/test_parsers.py

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44
import sys
5-
from ..parsers import jmspath_value_parser, jmspath_refkey_parser, exclude_filter
5+
from ..parsers import jmspath_value_parser, jmspath_refkey_parser
66
sys.path.append("..")
77

88

@@ -45,34 +45,6 @@
4545
"result[0].vrfs",
4646
)
4747

48-
exclude_filter_case_1 = (
49-
["interfaceStatistics"],
50-
{
51-
"interfaces": {
52-
"Management1": {
53-
"name": "Management1",
54-
"interfaceStatus": "connected",
55-
"autoNegotiate": "success",
56-
"interfaceStatistics": {
57-
"inBitsRate": 3403.4362520883615,
58-
"inPktsRate": 3.7424095978179257,
59-
"outBitsRate": 16249.69114419833,
60-
"updateInterval": 300,
61-
"outPktsRate": 2.1111866059750692
62-
}
63-
}
64-
}
65-
},
66-
{
67-
"interfaces": {
68-
"Management1": {
69-
"name": "Management1",
70-
"interfaceStatus": "connected",
71-
"autoNegotiate": "success"
72-
}
73-
}
74-
}
75-
)
7648

7749
value_parser_tests = [
7850
value_parser_case_1,
@@ -88,10 +60,6 @@
8860
keyref_parser_case_4,
8961
]
9062

91-
exclude_filter_tests = [
92-
exclude_filter_case_1,
93-
]
94-
9563

9664
@pytest.mark.parametrize("path, expected_output", value_parser_tests)
9765
def test_value_parser(path, expected_output):
@@ -102,8 +70,3 @@ def test_value_parser(path, expected_output):
10270
def test_keyref_parser(path, expected_output):
10371
output = jmspath_refkey_parser(path)
10472
assert expected_output == output, assertion_failed_message.format(output=output, expected_output=expected_output)
105-
106-
@pytest.mark.parametrize("exclude, data, expected_output", exclude_filter_tests)
107-
def test_exclude_filter(exclude, data, expected_output):
108-
exclude_filter(data, exclude)
109-
assert expected_output == data, assertion_failed_message.format(output=data, expected_output=expected_output)

netcompare/utils/list/__init__.py

Whitespace-only changes.

netcompare/utils/list/flatten.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from typing import Mapping, List, Generator, Union
2+
3+
def flatten_list(my_list: List) -> List:
4+
"""
5+
Flatten a multi level nested list and returns a list of lists.
6+
7+
Args:
8+
my_list: nested list to be flattened.
9+
10+
Return:
11+
[[-1, 0], [-1, 0], [-1, 0], ...]
12+
13+
Example:
14+
>>> my_list = [[[[-1, 0], [-1, 0]]]]
15+
>>> flatten_list(my_list)
16+
[[[[-1, 0], [-1, 0]]]]
17+
"""
18+
def iter_flatten_list(my_list: List) -> Generator[List, None, None]:
19+
"""Recursively yield all flat lists within a given list."""
20+
if is_flat_list(my_list):
21+
yield my_list
22+
else:
23+
for item in my_list:
24+
yield from iter_flatten_list(item)
25+
26+
27+
def is_flat_list(obj: List) -> bool:
28+
"""Return True is obj is a list that does not contain any lists as its first order elements."""
29+
return isinstance(obj, list) and not any(isinstance(i, list) for i in obj)
30+
31+
if not isinstance(my_list, list):
32+
raise ValueError(f"Argument provided must be a list. You passed a {type(my_list)}")
33+
if is_flat_list(my_list):
34+
return my_list
35+
return list(iter_flatten_list(my_list))
36+
37+

netcompare/utils/list/tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)