Skip to content

Commit 5a893b5

Browse files
authored
Merge pull request #63 from networktocode-llc/lvrfrc87
Code review feedback
2 parents d3cb8cd + fa66e68 commit 5a893b5

21 files changed

+573
-411
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ jobs:
1919
uses: "networktocode/gh-action-setup-poetry-environment@v2"
2020
- name: "Linting: black"
2121
run: "poetry run invoke black"
22+
mypy:
23+
runs-on: "ubuntu-20.04"
24+
env:
25+
INVOKE_LOCAL: "True"
26+
steps:
27+
- name: "Check out repository code"
28+
uses: "actions/checkout@v2"
29+
- name: "Setup environment"
30+
uses: "networktocode/gh-action-setup-poetry-environment@v2"
31+
- name: "Linting: mypy"
32+
run: "poetry run invoke mypy"
2233
bandit:
2334
runs-on: "ubuntu-20.04"
2435
env:
@@ -75,7 +86,7 @@ jobs:
7586
strategy:
7687
fail-fast: true
7788
matrix:
78-
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
89+
python-version: ["3.7", "3.8", "3.9", "3.10"]
7990
runs-on: "ubuntu-20.04"
8091
env:
8192
PYTHON_VER: "${{ matrix.python-version }}"
@@ -114,6 +125,7 @@ jobs:
114125
python-version: ["3.7"]
115126
env:
116127
PYTHON_VER: "${{ matrix.python-version }}"
128+
INVOKE_LOCAL: "True"
117129
steps:
118130
- name: "Check out repository code"
119131
uses: "actions/checkout@v2"
@@ -147,10 +159,11 @@ jobs:
147159
strategy:
148160
fail-fast: true
149161
matrix:
150-
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
162+
python-version: ["3.7", "3.8", "3.9", "3.10"]
151163
runs-on: "ubuntu-20.04"
152164
env:
153165
PYTHON_VER: "${{ matrix.python-version }}"
166+
INVOKE_LOCAL: "True"
154167
steps:
155168
- name: "Check out repository code"
156169
uses: "actions/checkout@v2"

netcompare/check_types.py

Lines changed: 63 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -59,28 +59,30 @@ def get_value(output: Union[Mapping, List], path: str = "*", exclude: List = Non
5959
if exclude and isinstance(output, Dict):
6060
if not isinstance(exclude, list):
6161
raise ValueError(f"Exclude list must be defined as a list. You have {type(exclude)}")
62-
63-
exclude_filter(output, exclude) # exclude unwanted elements
62+
# exclude unwanted elements
63+
exclude_filter(output, exclude)
6464

6565
if not path:
66-
warnings.warn("JMSPath cannot be of type 'None'. Path argumente reverted to default value '*'")
66+
warnings.warn("JMSPath cannot be empty string or type 'None'. Path argument reverted to default value '*'")
6767
path = "*"
6868

6969
if path == "*":
70-
return output # return if path is not specified
70+
# return if path is not specified
71+
return output
7172

7273
values = jmespath.search(jmespath_value_parser(path), output)
7374

74-
if not any(isinstance(i, list) for i in values): # check for multi-nested lists if not found return here
75+
# check for multi-nested lists if not found return here
76+
if not any(isinstance(i, list) for i in values):
7577
return values
7678

77-
for element in values: # process elements to check is lists should be flatten
78-
# TODO: Not sure how this is working because from `jmespath.search` it's supposed to get a flat list
79-
# of str or Decimals, not another list...
79+
# process elements to check if lists should be flattened
80+
for element in values:
8081
for item in element:
81-
if isinstance(item, dict): # raise if there is a dict, path must be more specific to extract data
82+
# raise if there is a dict, path must be more specific to extract data
83+
if isinstance(item, dict):
8284
raise TypeError(
83-
f'Must be list of lists i.e. [["Idle", 75759616], ["Idle", 75759620]].' f"You have {values}'."
85+
f'Must be list of lists i.e. [["Idle", 75759616], ["Idle", 75759620]]. You have "{values}".'
8486
)
8587
if isinstance(item, list):
8688
values = flatten_list(values) # flatten list and rewrite values
@@ -123,50 +125,55 @@ def evaluate(self, *args, **kwargs) -> Tuple[Dict, bool]:
123125
tuple: Dictionary representing check result, bool indicating if differences are found.
124126
"""
125127
# This method should call before any other logic the validation of the arguments
126-
# self.validate(**kwargs)
128+
# self._validate(**kwargs)
127129

128130
@staticmethod
129131
@abstractmethod
130-
def validate(**kwargs) -> None:
132+
def _validate(*args) -> None:
131133
"""Method to validate arguments that raises proper exceptions."""
132134

135+
@staticmethod
136+
def result(evaluation_result) -> Tuple[Dict, bool]:
137+
"""Result method implementation. Will return diff data and bool for checking failed result."""
138+
return evaluation_result, not evaluation_result
139+
133140

134141
class ExactMatchType(CheckType):
135142
"""Exact Match class docstring."""
136143

137144
@staticmethod
138-
def validate(**kwargs) -> None:
139-
"""Method to validate arguments."""
140-
# reference_data = getattr(kwargs, "reference_data")
145+
def _validate(reference_data):
146+
# No need for _validate method as exact-match does not take any specific arguments.
147+
pass
141148

142-
def evaluate(self, value_to_compare: Any, reference_data: Any) -> Tuple[Dict, bool]:
149+
def evaluate(self, value_to_compare: Any, reference_data: Any) -> Tuple[Dict, bool]: # type: ignore[override]
143150
"""Returns the difference between values and the boolean."""
144-
self.validate(reference_data=reference_data)
145151
evaluation_result = diff_generator(reference_data, value_to_compare)
146-
return evaluation_result, not evaluation_result
152+
return self.result(evaluation_result)
147153

148154

149155
class ToleranceType(CheckType):
150156
"""Tolerance class docstring."""
151157

152158
@staticmethod
153-
def validate(**kwargs) -> None:
159+
def _validate(tolerance) -> None: # type: ignore[override]
154160
"""Method to validate arguments."""
155161
# reference_data = getattr(kwargs, "reference_data")
156-
tolerance = kwargs.get("tolerance")
157162
if not tolerance:
158163
raise ValueError("'tolerance' argument is mandatory for Tolerance Check Type.")
159-
if not isinstance(tolerance, int):
160-
raise ValueError(f"Tolerance argument's value must be an integer. You have: {type(tolerance)}.")
164+
if not isinstance(tolerance, (int, float)):
165+
raise ValueError(f"Tolerance argument's value must be a number. You have: {type(tolerance)}.")
166+
if tolerance < 0:
167+
raise ValueError(f"Tolerance value must be greater than 0. You have: {tolerance}.")
161168

162-
def evaluate(self, value_to_compare: Any, reference_data: Any, tolerance: int) -> Tuple[Dict, bool]:
169+
def evaluate(self, value_to_compare: Any, reference_data: Any, tolerance: int) -> Tuple[Dict, bool]: # type: ignore[override]
163170
"""Returns the difference between values and the boolean. Overwrites method in base class."""
164-
self.validate(reference_data=reference_data, tolerance=tolerance)
165-
diff = diff_generator(reference_data, value_to_compare)
166-
self._remove_within_tolerance(diff, tolerance)
167-
return diff, not diff
171+
self._validate(tolerance=tolerance)
172+
evaluation_result = diff_generator(reference_data, value_to_compare)
173+
self._remove_within_tolerance(evaluation_result, tolerance)
174+
return self.result(evaluation_result)
168175

169-
def _remove_within_tolerance(self, diff: Dict, tolerance: int) -> None:
176+
def _remove_within_tolerance(self, diff: Dict, tolerance: Union[int, float]) -> None:
170177
"""Recursively look into diff and apply tolerance check, remove reported difference when within tolerance."""
171178

172179
def _make_float(value: Any) -> float:
@@ -197,87 +204,81 @@ class ParameterMatchType(CheckType):
197204
"""Parameter Match class implementation."""
198205

199206
@staticmethod
200-
def validate(**kwargs) -> None:
207+
def _validate(params, mode) -> None: # type: ignore[override]
201208
"""Method to validate arguments."""
202209
mode_options = ["match", "no-match"]
203-
params = kwargs.get("params")
204210
if not params:
205211
raise ValueError("'params' argument is mandatory for ParameterMatch Check Type.")
206212
if not isinstance(params, dict):
207213
raise ValueError(f"'params' argument must be a dict. You have: {type(params)}.")
208214

209-
mode = kwargs.get("mode")
210215
if not mode:
211216
raise ValueError("'mode' argument is mandatory for ParameterMatch Check Type.")
212217
if mode not in mode_options:
213218
raise ValueError(
214219
f"'mode' argument should be one of the following: {', '.join(mode_options)}. You have: {mode}"
215220
)
216221

217-
def evaluate(self, value_to_compare: Mapping, params: Dict, mode: str) -> Tuple[Dict, bool]:
222+
def evaluate(self, value_to_compare: Mapping, params: Dict, mode: str) -> Tuple[Dict, bool]: # type: ignore[override]
218223
"""Parameter Match evaluator implementation."""
219-
self.validate(params=params, mode=mode)
224+
self._validate(params=params, mode=mode)
220225
# TODO: we don't use the mode?
221226
evaluation_result = parameter_evaluator(value_to_compare, params, mode)
222-
return evaluation_result, not evaluation_result
227+
return self.result(evaluation_result)
223228

224229

225230
class RegexType(CheckType):
226231
"""Regex Match class implementation."""
227232

228233
@staticmethod
229-
def validate(**kwargs) -> None:
234+
def _validate(regex, mode) -> None: # type: ignore[override]
230235
"""Method to validate arguments."""
231236
mode_options = ["match", "no-match"]
232-
regex = kwargs.get("regex")
233237
if not regex:
234238
raise ValueError("'regex' argument is mandatory for Regex Check Type.")
235239
if not isinstance(regex, str):
236240
raise ValueError(f"'regex' argument must be a string. You have: {type(regex)}.")
237241

238-
mode = kwargs.get("mode")
239242
if not mode:
240243
raise ValueError("'mode' argument is mandatory for Regex Check Type.")
241244
if mode not in mode_options:
242245
raise ValueError(f"'mode' argument should be {mode_options}. You have: {mode}")
243246

244-
def evaluate(self, value_to_compare: Mapping, regex: str, mode: str) -> Tuple[Mapping, bool]:
247+
def evaluate(self, value_to_compare: Mapping, regex: str, mode: str) -> Tuple[Dict, bool]: # type: ignore[override]
245248
"""Regex Match evaluator implementation."""
246-
self.validate(regex=regex, mode=mode)
247-
diff = regex_evaluator(value_to_compare, regex, mode)
248-
return diff, not diff
249+
self._validate(regex=regex, mode=mode)
250+
evaluation_result = regex_evaluator(value_to_compare, regex, mode)
251+
return self.result(evaluation_result)
249252

250253

251254
class OperatorType(CheckType):
252255
"""Operator class implementation."""
253256

254257
@staticmethod
255-
def validate(**kwargs) -> None:
258+
def _validate(params) -> None: # type: ignore[override]
256259
"""Validate operator parameters."""
257260
in_operators = ("is-in", "not-in", "in-range", "not-range")
258261
bool_operators = ("all-same",)
259262
number_operators = ("is-gt", "is-lt")
260-
# "equals" is redundant with check type "exact_match" an "parameter_match"
261-
# equal_operators = ("is-equal", "not-equal")
262263
string_operators = ("contains", "not-contains")
263264
valid_options = (
264265
in_operators,
265266
bool_operators,
266267
number_operators,
267268
string_operators,
268-
# equal_operators,
269269
)
270270

271271
# Validate "params" argument is not None.
272-
if not kwargs or list(kwargs.keys())[0] != "params":
273-
raise ValueError(f"'params' argument must be provided. You have: {list(kwargs.keys())[0]}.")
272+
# {'params': {'mode': 'all-same', 'operator_data': True}}
273+
if not params or list(params.keys())[0] != "params":
274+
raise ValueError(f"'params' argument must be provided. You have: {list(params.keys())[0]}.")
274275

275-
params_key = kwargs["params"].get("mode")
276-
params_value = kwargs["params"].get("operator_data")
276+
params_key = params.get("params", {}).get("mode")
277+
params_value = params.get("params", {}).get("operator_data")
277278

278279
if not params_key or not params_value:
279280
raise ValueError(
280-
f"'mode' and 'operator_data' arguments must be provided. You have: {list(kwargs['params'].keys())}."
281+
f"'mode' and 'operator_data' arguments must be provided. You have: {list(params['params'].keys())}."
281282
)
282283

283284
# Validate "params" value is legal.
@@ -326,10 +327,18 @@ def validate(**kwargs) -> None:
326327
f"check option all-same must have value of type bool. You have: {params_value} of type {type(params_value)}"
327328
)
328329

329-
def evaluate(self, value_to_compare: Any, params: Any) -> Tuple[Dict, bool]:
330+
def evaluate(self, value_to_compare: Any, params: Any) -> Tuple[Dict, bool]: # type: ignore[override]
330331
"""Operator evaluator implementation."""
331-
self.validate(**params)
332+
self._validate(params)
332333
# For name consistency.
333334
reference_data = params
334-
evaluation_result, evaluation_bool = operator_evaluator(reference_data["params"], value_to_compare)
335-
return evaluation_result, not evaluation_bool
335+
evaluation_result = operator_evaluator(reference_data["params"], value_to_compare)
336+
return self.result(evaluation_result)
337+
338+
def result(self, evaluation_result):
339+
"""
340+
Operator result method overwrite.
341+
342+
This is required as Opertor return its own boolean within result.
343+
"""
344+
return evaluation_result[0], not evaluation_result[1]

netcompare/evaluators.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@ def regex_evaluator(values: Mapping, regex_expression: str, mode: str) -> Dict:
104104
return result
105105

106106

107-
def operator_evaluator(referance_data: Mapping, value_to_compare: Mapping) -> Tuple[Dict, bool]:
107+
def operator_evaluator(reference_data: Mapping, value_to_compare: Mapping) -> Tuple[Dict, bool]:
108108
"""Operator evaluator call."""
109-
# referance_data
109+
# reference_data
110110
# {'mode': 'all-same', 'operator_data': True}
111-
operator_mode = referance_data["mode"].replace("-", "_")
112-
operator = Operator(referance_data["operator_data"], value_to_compare)
111+
operator_mode = reference_data["mode"].replace("-", "_")
112+
operator = Operator(reference_data["operator_data"], value_to_compare)
113113
return getattr(operator, operator_mode)()

0 commit comments

Comments
 (0)