diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d10547..c1ed5ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,17 @@ jobs: uses: "networktocode/gh-action-setup-poetry-environment@v2" - name: "Linting: black" run: "poetry run invoke black" + mypy: + runs-on: "ubuntu-20.04" + env: + INVOKE_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: mypy" + run: "poetry run invoke mypy" bandit: runs-on: "ubuntu-20.04" env: @@ -75,7 +86,7 @@ jobs: strategy: fail-fast: true matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10"] runs-on: "ubuntu-20.04" env: PYTHON_VER: "${{ matrix.python-version }}" @@ -114,6 +125,7 @@ jobs: python-version: ["3.7"] env: PYTHON_VER: "${{ matrix.python-version }}" + INVOKE_LOCAL: "True" steps: - name: "Check out repository code" uses: "actions/checkout@v2" @@ -147,10 +159,11 @@ jobs: strategy: fail-fast: true matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10"] runs-on: "ubuntu-20.04" env: PYTHON_VER: "${{ matrix.python-version }}" + INVOKE_LOCAL: "True" steps: - name: "Check out repository code" uses: "actions/checkout@v2" diff --git a/netcompare/check_types.py b/netcompare/check_types.py index 773bc36..10ea841 100644 --- a/netcompare/check_types.py +++ b/netcompare/check_types.py @@ -59,28 +59,30 @@ def get_value(output: Union[Mapping, List], path: str = "*", exclude: List = Non if exclude and isinstance(output, Dict): if not isinstance(exclude, list): raise ValueError(f"Exclude list must be defined as a list. You have {type(exclude)}") - - exclude_filter(output, exclude) # exclude unwanted elements + # exclude unwanted elements + exclude_filter(output, exclude) if not path: - warnings.warn("JMSPath cannot be of type 'None'. Path argumente reverted to default value '*'") + warnings.warn("JMSPath cannot be empty string or type 'None'. Path argument reverted to default value '*'") path = "*" if path == "*": - return output # return if path is not specified + # return if path is not specified + return output values = jmespath.search(jmespath_value_parser(path), output) - if not any(isinstance(i, list) for i in values): # check for multi-nested lists if not found return here + # check for multi-nested lists if not found return here + if not any(isinstance(i, list) for i in values): return values - for element in values: # process elements to check is lists should be flatten - # TODO: Not sure how this is working because from `jmespath.search` it's supposed to get a flat list - # of str or Decimals, not another list... + # process elements to check if lists should be flattened + for element in values: for item in element: - if isinstance(item, dict): # raise if there is a dict, path must be more specific to extract data + # raise if there is a dict, path must be more specific to extract data + if isinstance(item, dict): raise TypeError( - f'Must be list of lists i.e. [["Idle", 75759616], ["Idle", 75759620]].' f"You have {values}'." + f'Must be list of lists i.e. [["Idle", 75759616], ["Idle", 75759620]]. You have "{values}".' ) if isinstance(item, list): values = flatten_list(values) # flatten list and rewrite values @@ -123,50 +125,55 @@ def evaluate(self, *args, **kwargs) -> Tuple[Dict, bool]: tuple: Dictionary representing check result, bool indicating if differences are found. """ # This method should call before any other logic the validation of the arguments - # self.validate(**kwargs) + # self._validate(**kwargs) @staticmethod @abstractmethod - def validate(**kwargs) -> None: + def _validate(*args) -> None: """Method to validate arguments that raises proper exceptions.""" + @staticmethod + def result(evaluation_result) -> Tuple[Dict, bool]: + """Result method implementation. Will return diff data and bool for checking failed result.""" + return evaluation_result, not evaluation_result + class ExactMatchType(CheckType): """Exact Match class docstring.""" @staticmethod - def validate(**kwargs) -> None: - """Method to validate arguments.""" - # reference_data = getattr(kwargs, "reference_data") + def _validate(reference_data): + # No need for _validate method as exact-match does not take any specific arguments. + pass - def evaluate(self, value_to_compare: Any, reference_data: Any) -> Tuple[Dict, bool]: + def evaluate(self, value_to_compare: Any, reference_data: Any) -> Tuple[Dict, bool]: # type: ignore[override] """Returns the difference between values and the boolean.""" - self.validate(reference_data=reference_data) evaluation_result = diff_generator(reference_data, value_to_compare) - return evaluation_result, not evaluation_result + return self.result(evaluation_result) class ToleranceType(CheckType): """Tolerance class docstring.""" @staticmethod - def validate(**kwargs) -> None: + def _validate(tolerance) -> None: # type: ignore[override] """Method to validate arguments.""" # reference_data = getattr(kwargs, "reference_data") - tolerance = kwargs.get("tolerance") if not tolerance: raise ValueError("'tolerance' argument is mandatory for Tolerance Check Type.") - if not isinstance(tolerance, int): - raise ValueError(f"Tolerance argument's value must be an integer. You have: {type(tolerance)}.") + if not isinstance(tolerance, (int, float)): + raise ValueError(f"Tolerance argument's value must be a number. You have: {type(tolerance)}.") + if tolerance < 0: + raise ValueError(f"Tolerance value must be greater than 0. You have: {tolerance}.") - def evaluate(self, value_to_compare: Any, reference_data: Any, tolerance: int) -> Tuple[Dict, bool]: + def evaluate(self, value_to_compare: Any, reference_data: Any, tolerance: int) -> Tuple[Dict, bool]: # type: ignore[override] """Returns the difference between values and the boolean. Overwrites method in base class.""" - self.validate(reference_data=reference_data, tolerance=tolerance) - diff = diff_generator(reference_data, value_to_compare) - self._remove_within_tolerance(diff, tolerance) - return diff, not diff + self._validate(tolerance=tolerance) + evaluation_result = diff_generator(reference_data, value_to_compare) + self._remove_within_tolerance(evaluation_result, tolerance) + return self.result(evaluation_result) - def _remove_within_tolerance(self, diff: Dict, tolerance: int) -> None: + def _remove_within_tolerance(self, diff: Dict, tolerance: Union[int, float]) -> None: """Recursively look into diff and apply tolerance check, remove reported difference when within tolerance.""" def _make_float(value: Any) -> float: @@ -197,16 +204,14 @@ class ParameterMatchType(CheckType): """Parameter Match class implementation.""" @staticmethod - def validate(**kwargs) -> None: + def _validate(params, mode) -> None: # type: ignore[override] """Method to validate arguments.""" mode_options = ["match", "no-match"] - params = kwargs.get("params") if not params: raise ValueError("'params' argument is mandatory for ParameterMatch Check Type.") if not isinstance(params, dict): raise ValueError(f"'params' argument must be a dict. You have: {type(params)}.") - mode = kwargs.get("mode") if not mode: raise ValueError("'mode' argument is mandatory for ParameterMatch Check Type.") if mode not in mode_options: @@ -214,70 +219,66 @@ def validate(**kwargs) -> None: f"'mode' argument should be one of the following: {', '.join(mode_options)}. You have: {mode}" ) - def evaluate(self, value_to_compare: Mapping, params: Dict, mode: str) -> Tuple[Dict, bool]: + def evaluate(self, value_to_compare: Mapping, params: Dict, mode: str) -> Tuple[Dict, bool]: # type: ignore[override] """Parameter Match evaluator implementation.""" - self.validate(params=params, mode=mode) + self._validate(params=params, mode=mode) # TODO: we don't use the mode? evaluation_result = parameter_evaluator(value_to_compare, params, mode) - return evaluation_result, not evaluation_result + return self.result(evaluation_result) class RegexType(CheckType): """Regex Match class implementation.""" @staticmethod - def validate(**kwargs) -> None: + def _validate(regex, mode) -> None: # type: ignore[override] """Method to validate arguments.""" mode_options = ["match", "no-match"] - regex = kwargs.get("regex") if not regex: raise ValueError("'regex' argument is mandatory for Regex Check Type.") if not isinstance(regex, str): raise ValueError(f"'regex' argument must be a string. You have: {type(regex)}.") - mode = kwargs.get("mode") if not mode: raise ValueError("'mode' argument is mandatory for Regex Check Type.") if mode not in mode_options: raise ValueError(f"'mode' argument should be {mode_options}. You have: {mode}") - def evaluate(self, value_to_compare: Mapping, regex: str, mode: str) -> Tuple[Mapping, bool]: + def evaluate(self, value_to_compare: Mapping, regex: str, mode: str) -> Tuple[Dict, bool]: # type: ignore[override] """Regex Match evaluator implementation.""" - self.validate(regex=regex, mode=mode) - diff = regex_evaluator(value_to_compare, regex, mode) - return diff, not diff + self._validate(regex=regex, mode=mode) + evaluation_result = regex_evaluator(value_to_compare, regex, mode) + return self.result(evaluation_result) class OperatorType(CheckType): """Operator class implementation.""" @staticmethod - def validate(**kwargs) -> None: + def _validate(params) -> None: # type: ignore[override] """Validate operator parameters.""" in_operators = ("is-in", "not-in", "in-range", "not-range") bool_operators = ("all-same",) number_operators = ("is-gt", "is-lt") - # "equals" is redundant with check type "exact_match" an "parameter_match" - # equal_operators = ("is-equal", "not-equal") string_operators = ("contains", "not-contains") valid_options = ( in_operators, bool_operators, number_operators, string_operators, - # equal_operators, ) # Validate "params" argument is not None. - if not kwargs or list(kwargs.keys())[0] != "params": - raise ValueError(f"'params' argument must be provided. You have: {list(kwargs.keys())[0]}.") + # {'params': {'mode': 'all-same', 'operator_data': True}} + if not params or list(params.keys())[0] != "params": + raise ValueError(f"'params' argument must be provided. You have: {list(params.keys())[0]}.") - params_key = kwargs["params"].get("mode") - params_value = kwargs["params"].get("operator_data") + params_key = params.get("params", {}).get("mode") + params_value = params.get("params", {}).get("operator_data") if not params_key or not params_value: raise ValueError( - f"'mode' and 'operator_data' arguments must be provided. You have: {list(kwargs['params'].keys())}." + f"'mode' and 'operator_data' arguments must be provided. You have: {list(params['params'].keys())}." ) # Validate "params" value is legal. @@ -326,10 +327,18 @@ def validate(**kwargs) -> None: f"check option all-same must have value of type bool. You have: {params_value} of type {type(params_value)}" ) - def evaluate(self, value_to_compare: Any, params: Any) -> Tuple[Dict, bool]: + def evaluate(self, value_to_compare: Any, params: Any) -> Tuple[Dict, bool]: # type: ignore[override] """Operator evaluator implementation.""" - self.validate(**params) + self._validate(params) # For name consistency. reference_data = params - evaluation_result, evaluation_bool = operator_evaluator(reference_data["params"], value_to_compare) - return evaluation_result, not evaluation_bool + evaluation_result = operator_evaluator(reference_data["params"], value_to_compare) + return self.result(evaluation_result) + + def result(self, evaluation_result): + """ + Operator result method overwrite. + + This is required as Opertor return its own boolean within result. + """ + return evaluation_result[0], not evaluation_result[1] diff --git a/netcompare/evaluators.py b/netcompare/evaluators.py index ace9e09..702474f 100644 --- a/netcompare/evaluators.py +++ b/netcompare/evaluators.py @@ -104,10 +104,10 @@ def regex_evaluator(values: Mapping, regex_expression: str, mode: str) -> Dict: return result -def operator_evaluator(referance_data: Mapping, value_to_compare: Mapping) -> Tuple[Dict, bool]: +def operator_evaluator(reference_data: Mapping, value_to_compare: Mapping) -> Tuple[Dict, bool]: """Operator evaluator call.""" - # referance_data + # reference_data # {'mode': 'all-same', 'operator_data': True} - operator_mode = referance_data["mode"].replace("-", "_") - operator = Operator(referance_data["operator_data"], value_to_compare) + operator_mode = reference_data["mode"].replace("-", "_") + operator = Operator(reference_data["operator_data"], value_to_compare) return getattr(operator, operator_mode)() diff --git a/netcompare/operator.py b/netcompare/operator.py index e57f4c5..e344305 100644 --- a/netcompare/operator.py +++ b/netcompare/operator.py @@ -6,17 +6,49 @@ class Operator: """Operator class implementation.""" - def __init__(self, referance_data: Any, value_to_compare: Any) -> None: - """__init__ method.""" + def __init__(self, reference_data: Any, value_to_compare: Any) -> None: + """__init__ method for Operator class.""" # [{'7.7.7.7': {'peerGroup': 'EVPN-OVERLAY-SPINE', 'vrf': 'default', 'state': 'Idle'}}, # {'10.1.0.0': {'peerGroup': 'IPv4-UNDERLAY-SPINE', 'vrf': 'default', 'state': 'Idle'}}, # {'10.2.0.0': {'peerGroup': 'IPv4-UNDERLAY-SPINE', 'vrf': 'default', 'state': 'Idle'}}, # {'10.64.207.255': {'peerGroup': 'IPv4-UNDERLAY-MLAG-PEER', 'vrf': 'default', 'state': 'Idle'}}] - self.referance_data = referance_data + self.reference_data = reference_data self.value_to_compare = value_to_compare - def _loop_through_wrapper(self, call_ops: str) -> Tuple[bool, List]: - """Wrapper method for operator evaluation.""" + def _loop_through_wrapper(self, call_ops: str) -> Tuple[List, bool]: + """Private wrapper method for operator evaluation based on 'operator' lib. + + Based on value passed to the method, the appropriate operator logic is triggered. + + Args: + call_ops: operator type parameter. + + Returns: + dict: result evaluation based on operator type. + """ + + def call_evaluation_logic(): + """Operator valuation logic wrapper.""" + # reverse operands: https://docs.python.org/3.8/library/operator.html#operator.contains + if call_ops == "is_in": + if ops[call_ops](self.reference_data, evaluated_value): + result.append(item) + elif call_ops == "not_contains": + if not ops[call_ops](evaluated_value, self.reference_data): + result.append(item) + elif call_ops == "not_in": + if not ops[call_ops](self.reference_data, evaluated_value): + result.append(item) + elif call_ops == "in_range": + if self.reference_data[0] < evaluated_value < self.reference_data[1]: + result.append(item) + elif call_ops == "not_range": + if not self.reference_data[0] < evaluated_value < self.reference_data[1]: + result.append(item) + # "<", ">", "contains" + elif ops[call_ops](evaluated_value, self.reference_data): + result.append(item) + ops = { ">": operator.gt, "<": operator.lt, @@ -26,85 +58,65 @@ def _loop_through_wrapper(self, call_ops: str) -> Tuple[bool, List]: "not_contains": operator.contains, } - result = [] + result = [] # type: List for item in self.value_to_compare: for value in item.values(): for evaluated_value in value.values(): - # reverse operands (??? WHY ???) https://docs.python.org/3.8/library/operator.html#operator.contains - if call_ops == "is_in": - if ops[call_ops](self.referance_data, evaluated_value): - result.append(item) - elif call_ops == "not_contains": - if not ops[call_ops](evaluated_value, self.referance_data): - result.append(item) - elif call_ops == "not_in": - if not ops[call_ops](self.referance_data, evaluated_value): - result.append(item) - elif call_ops == "in_range": - if self.referance_data[0] < evaluated_value < self.referance_data[1]: - result.append(item) - elif call_ops == "not_range": - if not self.referance_data[0] < evaluated_value < self.referance_data[1]: - result.append(item) - # "<", ">", "contains" - elif ops[call_ops](evaluated_value, self.referance_data): - result.append(item) + call_evaluation_logic() if result: return (result, True) return (result, False) def all_same(self) -> Tuple[bool, Any]: - """All same operator implementation.""" + """All same operator type implementation.""" list_of_values = [] result = [] for item in self.value_to_compare: - for value in item.values(): - # Create a list for compare values. - list_of_values.append(value) - + # Create a list for compare values. + list_of_values.extend(iter(item.values())) for element in list_of_values: if element != list_of_values[0]: result.append(False) else: result.append(True) - if self.referance_data and not all(result): + if self.reference_data and not all(result): return (self.value_to_compare, False) - if self.referance_data: + if self.reference_data: return (self.value_to_compare, True) if not all(result): return (self.value_to_compare, True) return (self.value_to_compare, False) - def contains(self) -> Tuple[bool, List]: - """Contains operator implementation.""" + def contains(self) -> Tuple[List, bool]: + """Contains operator caller.""" return self._loop_through_wrapper("contains") - def not_contains(self) -> Tuple[bool, List]: - """Not contains operator implementation.""" + def not_contains(self) -> Tuple[List, bool]: + """Not contains operator caller.""" return self._loop_through_wrapper("not_contains") - def is_gt(self) -> Tuple[bool, List]: - """Is greather than operator implementation.""" + def is_gt(self) -> Tuple[List, bool]: + """Is greather than operator caller.""" return self._loop_through_wrapper(">") - def is_lt(self) -> Tuple[bool, List]: - """Is lower than operator implementation.""" + def is_lt(self) -> Tuple[List, bool]: + """Is lower than operator caller.""" return self._loop_through_wrapper("<") - def is_in(self) -> Tuple[bool, List]: - """Is in operator implementation.""" + def is_in(self) -> Tuple[List, bool]: + """Is in operator caller.""" return self._loop_through_wrapper("is_in") - def not_in(self) -> Tuple[bool, List]: - """Is not in operator implementation.""" + def not_in(self) -> Tuple[List, bool]: + """Is not in operator caller.""" return self._loop_through_wrapper("not_in") - def in_range(self) -> Tuple[bool, List]: - """Is in range operator implementation.""" + def in_range(self) -> Tuple[List, bool]: + """Is in range operator caller.""" return self._loop_through_wrapper("in_range") - def not_range(self) -> Tuple[bool, List]: - """Is not in range operator implementation.""" + def not_range(self) -> Tuple[List, bool]: + """Is not in range operator caller.""" return self._loop_through_wrapper("not_range") diff --git a/netcompare/utils/data_normalization.py b/netcompare/utils/data_normalization.py index 1fd2fea..8a5e053 100644 --- a/netcompare/utils/data_normalization.py +++ b/netcompare/utils/data_normalization.py @@ -1,11 +1,17 @@ """Data Normalization utilities.""" -from typing import List, Generator, Mapping +from typing import List, Generator, Union, Dict def flatten_list(my_list: List) -> List: """ Flatten a multi level nested list and returns a list of lists. + This normalization step is required since jmespath can return nested lists containing the + wanted value. This depends how much nested is the wanted value in the json output. + + Having a list of lists will help us to assert that we have the number of values we have, will + match the number of reference keys found in json object. + Args: my_list: nested list to be flattened. @@ -37,7 +43,7 @@ def is_flat_list(obj: List) -> bool: return list(iter_flatten_list(my_list)) -def exclude_filter(data: Mapping, exclude: List): +def exclude_filter(data: Union[Dict, List], exclude: List): """ Recusively look through all dict keys and pop out the one defined in "exclude". diff --git a/netcompare/utils/diff_helpers.py b/netcompare/utils/diff_helpers.py index 9faaefd..fd1b70f 100644 --- a/netcompare/utils/diff_helpers.py +++ b/netcompare/utils/diff_helpers.py @@ -2,12 +2,12 @@ import re from collections import defaultdict from functools import partial -from typing import Mapping, Dict, List +from typing import Mapping, Dict, List, DefaultDict REGEX_PATTERN_RELEVANT_KEYS = r"'([A-Za-z0-9_\./\\-]*)'" -def get_diff_iterables_items(diff_result: Mapping) -> Dict: +def get_diff_iterables_items(diff_result: Mapping) -> DefaultDict: """Helper function for diff_generator to postprocess changes reported by DeepDiff for iterables. DeepDiff iterable_items are returned when the source data is a list @@ -24,7 +24,7 @@ def get_diff_iterables_items(diff_result: Mapping) -> Dict: get_dict_keys = re.compile(r"^root((\['\w.*'\])+)\[\d+\]$") defaultdict_list = partial(defaultdict, list) - result = defaultdict(defaultdict_list) + result = defaultdict(defaultdict_list) # type: DefaultDict items_removed = diff_result.get("iterable_item_removed") if items_removed: @@ -59,7 +59,7 @@ def fix_deepdiff_key_names(obj: Mapping) -> Dict: Dict: aggregated output, for example: {'7.7.7.7': {'is_enabled': {'new_value': False, 'old_value': True}, 'is_up': {'new_value': False, 'old_value': True}}} """ - result = {} + result = {} # type: Dict for key, value in obj.items(): key_parts = re.findall(REGEX_PATTERN_RELEVANT_KEYS, key) if not key_parts: # If key parts can't be find, keep original key so data is not lost. @@ -69,7 +69,6 @@ def fix_deepdiff_key_names(obj: Mapping) -> Dict: return result -# TODO: Add testing def group_value(tree_list: List, value: Dict) -> Dict: """Function to create a nested Dict by recursively use the tree_list as nested keys.""" if tree_list: @@ -77,7 +76,6 @@ def group_value(tree_list: List, value: Dict) -> Dict: return value -# TODO: Add testing def dict_merger(original_dict: Dict, dict_to_merge: Dict): """Function to merge a dictionary (dict_to_merge) recursively into the original_dict.""" for key in dict_to_merge.keys(): diff --git a/netcompare/utils/jmespath_parsers.py b/netcompare/utils/jmespath_parsers.py index 44e7e72..64ea838 100644 --- a/netcompare/utils/jmespath_parsers.py +++ b/netcompare/utils/jmespath_parsers.py @@ -1,11 +1,20 @@ -"""jmespath expression parsers and related utilities.""" +""" +jmespath expression parsers and related utilities. + +This utility interfaces the custom netcompare jmespath expression with the jmespath library. +From one expression defined in netcompare, we will derive two expressions: one expression that traverse the json output and get the +evaluated bit of it, the second will target the reference key relative to the value to evaluate. More on README.md +""" import re from typing import Mapping, List, Union def jmespath_value_parser(path: str): """ - Get the jmespath value path from 'path'. + Extract the jmespath value path from 'path' argument. + + This is required as we use custom anchors ($$) to identify the reference key. + So the expression must be parsed and stripped of the reference key anchor. More info on README.md Two combinations are possible based on where reference key is defined. See example below. @@ -37,7 +46,10 @@ def jmespath_value_parser(path: str): def jmespath_refkey_parser(path: str): """ - Get the jmespath reference key path from 'path'. + Get the jmespath reference key path from 'path' argument. + + Reference key is define within $$ in 'path' and will be associated to the value/s to be evaluated. + More on README.md. Args: path: "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesReceived]" @@ -59,7 +71,7 @@ def jmespath_refkey_parser(path: str): def associate_key_of_my_value(paths: str, wanted_value: List) -> List: - """Associate each key defined in path to every value found in output.""" + """Associate each reference key (from: jmespath_refkey_parser) to every value found in output (from: jmespath_value_parser).""" # global.peers.*.[is_enabled,is_up] / result.[*].state find_the_key_of_my_values = paths.split(".")[-1] @@ -85,7 +97,7 @@ def associate_key_of_my_value(paths: str, wanted_value: List) -> List: def keys_cleaner(wanted_reference_keys: Union[Mapping, List]) -> List: - """Get every required reference key from output.""" + """Get every required reference key from output and build a dictionary from it.""" if isinstance(wanted_reference_keys, list): final_result = wanted_reference_keys elif isinstance(wanted_reference_keys, dict): diff --git a/poetry.lock b/poetry.lock index 8606d44..e0afc00 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,6 +1,6 @@ [[package]] name = "astroid" -version = "2.11.2" +version = "2.11.6" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false @@ -36,11 +36,11 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (> [[package]] name = "bandit" -version = "1.7.1" +version = "1.7.4" description = "Security oriented static analyser for python code." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} @@ -48,6 +48,11 @@ GitPython = ">=1.0.1" PyYAML = ">=5.3.1" stevedore = ">=1.20.0" +[package.extras] +test = ["coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml", "beautifulsoup4 (>=4.8.0)", "pylint (==1.9.4)"] +toml = ["toml"] +yaml = ["pyyaml"] + [[package]] name = "black" version = "22.3.0" @@ -58,7 +63,6 @@ python-versions = ">=3.6.2" [package.dependencies] click = ">=8.0.0" -dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0" platformdirs = ">=2" @@ -74,11 +78,11 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2021.10.8" +version = "2022.5.18.1" description = "Python package for providing Mozilla's CA Bundle." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "charset-normalizer" @@ -93,11 +97,11 @@ unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "8.0.4" +version = "8.1.3" description = "Composable command line interface toolkit" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -111,52 +115,44 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[[package]] -name = "dataclasses" -version = "0.8" -description = "A backport of the dataclasses module for Python 3.6" -category = "dev" -optional = false -python-versions = ">=3.6, <3.7" - [[package]] name = "deepdiff" -version = "5.7.0" +version = "5.8.1" description = "Deep Difference and Search of any Python object/data." category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -ordered-set = "4.0.2" +ordered-set = ">=4.1.0,<4.2.0" [package.extras] cli = ["click (==8.0.3)", "pyyaml (==5.4.1)", "toml (==0.10.2)", "clevercsv (==0.7.1)"] [[package]] name = "dill" -version = "0.3.4" +version = "0.3.5.1" description = "serialize all of python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" [package.extras] graph = ["objgraph (>=1.7.2)"] [[package]] name = "flake8" -version = "3.9.2" +version = "4.0.1" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.7.0,<2.8.0" -pyflakes = ">=2.3.0,<2.4.0" +pycodestyle = ">=2.8.0,<2.9.0" +pyflakes = ">=2.4.0,<2.5.0" [[package]] name = "gitdb" @@ -171,15 +167,15 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.20" -description = "Python Git Library" +version = "3.1.27" +description = "GitPython is a python library used to interact with Git repositories" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} +typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} [[package]] name = "idna" @@ -191,7 +187,7 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.8.3" +version = "4.2.0" description = "Read metadata from Python packages" category = "dev" optional = false @@ -203,8 +199,7 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -216,7 +211,7 @@ python-versions = "*" [[package]] name = "invoke" -version = "1.7.0" +version = "1.7.1" description = "Pythonic task execution" category = "dev" optional = false @@ -260,6 +255,25 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "mypy" +version = "0.961" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + [[package]] name = "mypy-extensions" version = "0.4.3" @@ -270,11 +284,14 @@ python-versions = "*" [[package]] name = "ordered-set" -version = "4.0.2" -description = "A set that remembers its order, and allows looking up its items by their index in that order." +version = "4.1.0" +description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" + +[package.extras] +dev = ["pytest", "black", "mypy"] [[package]] name = "packaging" @@ -297,7 +314,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "pbr" -version = "5.8.1" +version = "5.9.0" description = "Python Build Reasonableness" category = "dev" optional = false @@ -305,15 +322,15 @@ python-versions = ">=2.6" [[package]] name = "platformdirs" -version = "2.4.0" +version = "2.5.2" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] +test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] [[package]] name = "pluggy" @@ -340,11 +357,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pycodestyle" -version = "2.7.0" +version = "2.8.0" description = "Python style guide checker" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pydocstyle" @@ -362,7 +379,7 @@ toml = ["toml"] [[package]] name = "pyflakes" -version = "2.3.1" +version = "2.4.0" description = "passive checker of Python programs" category = "dev" optional = false @@ -370,14 +387,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pylint" -version = "2.13.3" +version = "2.13.9" description = "python code static checker" category = "dev" optional = false python-versions = ">=3.6.2" [package.dependencies] -astroid = ">=2.11.2,<=2.12.0-dev0" +astroid = ">=2.11.5,<=2.12.0-dev0" colorama = {version = "*", markers = "sys_platform == \"win32\""} dill = ">=0.2" isort = ">=4.2.5,<6" @@ -391,22 +408,22 @@ testutil = ["gitpython (>3)"] [[package]] name = "pyparsing" -version = "3.0.7" -description = "Python parsing module" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.8" [package.extras] -diagrams = ["jinja2", "railroad-diagrams"] +diagrams = ["railroad-diagrams", "jinja2"] [[package]] name = "pytest" -version = "7.0.1" +version = "7.1.2" description = "pytest: simple powerful testing with Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} @@ -432,20 +449,20 @@ python-versions = ">=3.6" [[package]] name = "requests" -version = "2.27.1" +version = "2.28.0" description = "Python HTTP for Humans." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +charset-normalizer = ">=2.0.0,<2.1.0" +idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" [package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] @@ -510,15 +527,15 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tomli" -version = "1.2.3" +version = "2.0.1" description = "A lil' TOML parser" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "typed-ast" -version = "1.5.2" +version = "1.5.4" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false @@ -526,11 +543,11 @@ python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "4.1.1" -description = "Backported and Experimental Type Hints for Python 3.6+" +version = "4.2.0" +description = "Backported and Experimental Type Hints for Python 3.7+" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "urllib3" @@ -547,7 +564,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "wrapt" -version = "1.14.0" +version = "1.14.1" description = "Module for decorators, wrappers and monkey patching." category = "dev" optional = false @@ -567,25 +584,25 @@ pyyaml = "*" [[package]] name = "zipp" -version = "3.6.0" +version = "3.8.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" -python-versions = "^3.6.2" -content-hash = "9e8a9a3f7538a3546faae1b4f35e1113f166c2a34822e9f2e8746b61d55bbce8" +python-versions = "^3.7.0" +content-hash = "6137c91b7e7b479aa34d3fbc74a92ea0fc193a390f3bc251f2b0513c0d960978" [metadata.files] astroid = [ - {file = "astroid-2.11.2-py3-none-any.whl", hash = "sha256:cc8cc0d2d916c42d0a7c476c57550a4557a083081976bf42a73414322a6411d9"}, - {file = "astroid-2.11.2.tar.gz", hash = "sha256:8d0a30fe6481ce919f56690076eafbb2fb649142a89dc874f1ec0e7a011492d0"}, + {file = "astroid-2.11.6-py3-none-any.whl", hash = "sha256:ba33a82a9a9c06a5ceed98180c5aab16e29c285b828d94696bf32d6015ea82a9"}, + {file = "astroid-2.11.6.tar.gz", hash = "sha256:4f933d0bf5e408b03a6feb5d23793740c27e07340605f236496cd6ce552043d6"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, @@ -596,8 +613,8 @@ attrs = [ {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, ] bandit = [ - {file = "bandit-1.7.1-py3-none-any.whl", hash = "sha256:f5acd838e59c038a159b5c621cf0f8270b279e884eadd7b782d7491c02add0d4"}, - {file = "bandit-1.7.1.tar.gz", hash = "sha256:a81b00b5436e6880fa8ad6799bc830e02032047713cbb143a12939ac67eb756c"}, + {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, + {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, ] black = [ {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, @@ -625,60 +642,56 @@ black = [ {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, ] certifi = [ - {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, - {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, + {file = "certifi-2022.5.18.1-py3-none-any.whl", hash = "sha256:f1d53542ee8cbedbe2118b5686372fb33c297fcd6379b050cca0ef13a597382a"}, + {file = "certifi-2022.5.18.1.tar.gz", hash = "sha256:9c5705e395cd70084351dd8ad5c41e65655e08ce46f2ec9cf6c2c08390f71eb7"}, ] charset-normalizer = [ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, ] click = [ - {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, - {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] -dataclasses = [ - {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, - {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, -] deepdiff = [ - {file = "deepdiff-5.7.0-py3-none-any.whl", hash = "sha256:1ffb38c3b5d9174eb2df95850c93aee55ec00e19396925036a2e680f725079e0"}, - {file = "deepdiff-5.7.0.tar.gz", hash = "sha256:838766484e323dcd9dec6955926a893a83767dc3f3f94542773e6aa096efe5d4"}, + {file = "deepdiff-5.8.1-py3-none-any.whl", hash = "sha256:e9aea49733f34fab9a0897038d8f26f9d94a97db1790f1b814cced89e9e0d2b7"}, + {file = "deepdiff-5.8.1.tar.gz", hash = "sha256:8d4eb2c4e6cbc80b811266419cb71dd95a157094a3947ccf937a94d44943c7b8"}, ] dill = [ - {file = "dill-0.3.4-py2.py3-none-any.whl", hash = "sha256:7e40e4a70304fd9ceab3535d36e58791d9c4a776b38ec7f7ec9afc8d3dca4d4f"}, - {file = "dill-0.3.4.zip", hash = "sha256:9f9734205146b2b353ab3fec9af0070237b6ddae78452af83d2fca84d739e675"}, + {file = "dill-0.3.5.1-py2.py3-none-any.whl", hash = "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302"}, + {file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"}, ] flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, + {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, + {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, ] gitdb = [ {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] gitpython = [ - {file = "GitPython-3.1.20-py3-none-any.whl", hash = "sha256:b1e1c269deab1b08ce65403cf14e10d2ef1f6c89e33ea7c5e5bb0222ea593b8a"}, - {file = "GitPython-3.1.20.tar.gz", hash = "sha256:df0e072a200703a65387b0cfdf0466e3bab729c0458cf6b7349d0e9877636519"}, + {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, + {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, ] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"}, - {file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"}, + {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, + {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] invoke = [ - {file = "invoke-1.7.0-py3-none-any.whl", hash = "sha256:a5159fc63dba6ca2a87a1e33d282b99cea69711b03c64a35bb4e1c53c6c4afa0"}, - {file = "invoke-1.7.0.tar.gz", hash = "sha256:e332e49de40463f2016315f51df42313855772be86435686156bc18f45b5cc6c"}, + {file = "invoke-1.7.1-py3-none-any.whl", hash = "sha256:2dc975b4f92be0c0a174ad2d063010c8a1fdb5e9389d69871001118b4fcac4fb"}, + {file = "invoke-1.7.1.tar.gz", hash = "sha256:7b6deaf585eee0a848205d0b8c0014b9bf6f287a8eb798818a642dff1df14b19"}, ] isort = [ {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, @@ -731,12 +744,38 @@ mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] +mypy = [ + {file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"}, + {file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"}, + {file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"}, + {file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"}, + {file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"}, + {file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"}, + {file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"}, + {file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"}, + {file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"}, + {file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"}, + {file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"}, + {file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"}, + {file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"}, + {file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"}, + {file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"}, + {file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"}, + {file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"}, + {file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"}, + {file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"}, + {file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"}, + {file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"}, + {file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"}, + {file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"}, +] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] ordered-set = [ - {file = "ordered-set-4.0.2.tar.gz", hash = "sha256:ba93b2df055bca202116ec44b9bead3df33ea63a7d5827ff8e16738b97f33a95"}, + {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, + {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, ] packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, @@ -747,12 +786,12 @@ pathspec = [ {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] pbr = [ - {file = "pbr-5.8.1-py2.py3-none-any.whl", hash = "sha256:27108648368782d07bbf1cb468ad2e2eeef29086affd14087a6d04b7de8af4ec"}, - {file = "pbr-5.8.1.tar.gz", hash = "sha256:66bc5a34912f408bb3925bf21231cb6f59206267b7f63f3503ef865c1a292e25"}, + {file = "pbr-5.9.0-py2.py3-none-any.whl", hash = "sha256:e547125940bcc052856ded43be8e101f63828c2d94239ffbe2b327ba3d5ccf0a"}, + {file = "pbr-5.9.0.tar.gz", hash = "sha256:e8dca2f4b43560edef58813969f52a56cef023146cbb8931626db80e6c1c4308"}, ] platformdirs = [ - {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, - {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, @@ -763,28 +802,28 @@ py = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pycodestyle = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, + {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, + {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, ] pydocstyle = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, ] pyflakes = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, + {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, + {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, ] pylint = [ - {file = "pylint-2.13.3-py3-none-any.whl", hash = "sha256:c8837b6ec6440e3490ab8f066054b0645a516a29ca51ce442f16f7004f711a70"}, - {file = "pylint-2.13.3.tar.gz", hash = "sha256:12ed2520510c40db647e4ec7f747b07e0d669b33ab41479c2a07bb89b92877db"}, + {file = "pylint-2.13.9-py3-none-any.whl", hash = "sha256:705c620d388035bdd9ff8b44c5bcdd235bfb49d276d488dd2c8ff1736aa42526"}, + {file = "pylint-2.13.9.tar.gz", hash = "sha256:095567c96e19e6f57b5b907e67d265ff535e588fe26b12b5ebe1fc5645b2c731"}, ] pyparsing = [ - {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, - {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] pytest = [ - {file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"}, - {file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"}, + {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, + {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, ] pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, @@ -822,8 +861,8 @@ pyyaml = [ {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] requests = [ - {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, - {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, + {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"}, + {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"}, ] requests-mock = [ {file = "requests-mock-1.9.3.tar.gz", hash = "sha256:8d72abe54546c1fc9696fa1516672f1031d72a55a1d66c85184f972a24ba0eba"}, @@ -850,113 +889,113 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tomli = [ - {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, - {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] typed-ast = [ - {file = "typed_ast-1.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:183b183b7771a508395d2cbffd6db67d6ad52958a5fdc99f450d954003900266"}, - {file = "typed_ast-1.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:676d051b1da67a852c0447621fdd11c4e104827417bf216092ec3e286f7da596"}, - {file = "typed_ast-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc2542e83ac8399752bc16e0b35e038bdb659ba237f4222616b4e83fb9654985"}, - {file = "typed_ast-1.5.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74cac86cc586db8dfda0ce65d8bcd2bf17b58668dfcc3652762f3ef0e6677e76"}, - {file = "typed_ast-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:18fe320f354d6f9ad3147859b6e16649a0781425268c4dde596093177660e71a"}, - {file = "typed_ast-1.5.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:31d8c6b2df19a777bc8826770b872a45a1f30cfefcfd729491baa5237faae837"}, - {file = "typed_ast-1.5.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:963a0ccc9a4188524e6e6d39b12c9ca24cc2d45a71cfdd04a26d883c922b4b78"}, - {file = "typed_ast-1.5.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0eb77764ea470f14fcbb89d51bc6bbf5e7623446ac4ed06cbd9ca9495b62e36e"}, - {file = "typed_ast-1.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:294a6903a4d087db805a7656989f613371915fc45c8cc0ddc5c5a0a8ad9bea4d"}, - {file = "typed_ast-1.5.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:26a432dc219c6b6f38be20a958cbe1abffcc5492821d7e27f08606ef99e0dffd"}, - {file = "typed_ast-1.5.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7407cfcad702f0b6c0e0f3e7ab876cd1d2c13b14ce770e412c0c4b9728a0f88"}, - {file = "typed_ast-1.5.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f30ddd110634c2d7534b2d4e0e22967e88366b0d356b24de87419cc4410c41b7"}, - {file = "typed_ast-1.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8c08d6625bb258179b6e512f55ad20f9dfef019bbfbe3095247401e053a3ea30"}, - {file = "typed_ast-1.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:90904d889ab8e81a956f2c0935a523cc4e077c7847a836abee832f868d5c26a4"}, - {file = "typed_ast-1.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bbebc31bf11762b63bf61aaae232becb41c5bf6b3461b80a4df7e791fabb3aca"}, - {file = "typed_ast-1.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c29dd9a3a9d259c9fa19d19738d021632d673f6ed9b35a739f48e5f807f264fb"}, - {file = "typed_ast-1.5.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:58ae097a325e9bb7a684572d20eb3e1809802c5c9ec7108e85da1eb6c1a3331b"}, - {file = "typed_ast-1.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:da0a98d458010bf4fe535f2d1e367a2e2060e105978873c04c04212fb20543f7"}, - {file = "typed_ast-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:33b4a19ddc9fc551ebabca9765d54d04600c4a50eda13893dadf67ed81d9a098"}, - {file = "typed_ast-1.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1098df9a0592dd4c8c0ccfc2e98931278a6c6c53cb3a3e2cf7e9ee3b06153344"}, - {file = "typed_ast-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c47c3b43fe3a39ddf8de1d40dbbfca60ac8530a36c9b198ea5b9efac75c09e"}, - {file = "typed_ast-1.5.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f290617f74a610849bd8f5514e34ae3d09eafd521dceaa6cf68b3f4414266d4e"}, - {file = "typed_ast-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:df05aa5b241e2e8045f5f4367a9f6187b09c4cdf8578bb219861c4e27c443db5"}, - {file = "typed_ast-1.5.2.tar.gz", hash = "sha256:525a2d4088e70a9f75b08b3f87a51acc9cde640e19cc523c7e41aa355564ae27"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] typing-extensions = [ - {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, - {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, + {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, + {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, ] urllib3 = [ {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, ] wrapt = [ - {file = "wrapt-1.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:5a9a1889cc01ed2ed5f34574c90745fab1dd06ec2eee663e8ebeefe363e8efd7"}, - {file = "wrapt-1.14.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:9a3ff5fb015f6feb78340143584d9f8a0b91b6293d6b5cf4295b3e95d179b88c"}, - {file = "wrapt-1.14.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4b847029e2d5e11fd536c9ac3136ddc3f54bc9488a75ef7d040a3900406a91eb"}, - {file = "wrapt-1.14.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:9a5a544861b21e0e7575b6023adebe7a8c6321127bb1d238eb40d99803a0e8bd"}, - {file = "wrapt-1.14.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:88236b90dda77f0394f878324cfbae05ae6fde8a84d548cfe73a75278d760291"}, - {file = "wrapt-1.14.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f0408e2dbad9e82b4c960274214af533f856a199c9274bd4aff55d4634dedc33"}, - {file = "wrapt-1.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9d8c68c4145041b4eeae96239802cfdfd9ef927754a5be3f50505f09f309d8c6"}, - {file = "wrapt-1.14.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:22626dca56fd7f55a0733e604f1027277eb0f4f3d95ff28f15d27ac25a45f71b"}, - {file = "wrapt-1.14.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:65bf3eb34721bf18b5a021a1ad7aa05947a1767d1aa272b725728014475ea7d5"}, - {file = "wrapt-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09d16ae7a13cff43660155383a2372b4aa09109c7127aa3f24c3cf99b891c330"}, - {file = "wrapt-1.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:debaf04f813ada978d7d16c7dfa16f3c9c2ec9adf4656efdc4defdf841fc2f0c"}, - {file = "wrapt-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748df39ed634851350efa87690c2237a678ed794fe9ede3f0d79f071ee042561"}, - {file = "wrapt-1.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1807054aa7b61ad8d8103b3b30c9764de2e9d0c0978e9d3fc337e4e74bf25faa"}, - {file = "wrapt-1.14.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:763a73ab377390e2af26042f685a26787c402390f682443727b847e9496e4a2a"}, - {file = "wrapt-1.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8529b07b49b2d89d6917cfa157d3ea1dfb4d319d51e23030664a827fe5fd2131"}, - {file = "wrapt-1.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:68aeefac31c1f73949662ba8affaf9950b9938b712fb9d428fa2a07e40ee57f8"}, - {file = "wrapt-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59d7d92cee84a547d91267f0fea381c363121d70fe90b12cd88241bd9b0e1763"}, - {file = "wrapt-1.14.0-cp310-cp310-win32.whl", hash = "sha256:3a88254881e8a8c4784ecc9cb2249ff757fd94b911d5df9a5984961b96113fff"}, - {file = "wrapt-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:9a242871b3d8eecc56d350e5e03ea1854de47b17f040446da0e47dc3e0b9ad4d"}, - {file = "wrapt-1.14.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a65bffd24409454b889af33b6c49d0d9bcd1a219b972fba975ac935f17bdf627"}, - {file = "wrapt-1.14.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9d9fcd06c952efa4b6b95f3d788a819b7f33d11bea377be6b8980c95e7d10775"}, - {file = "wrapt-1.14.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:db6a0ddc1282ceb9032e41853e659c9b638789be38e5b8ad7498caac00231c23"}, - {file = "wrapt-1.14.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:14e7e2c5f5fca67e9a6d5f753d21f138398cad2b1159913ec9e9a67745f09ba3"}, - {file = "wrapt-1.14.0-cp35-cp35m-win32.whl", hash = "sha256:6d9810d4f697d58fd66039ab959e6d37e63ab377008ef1d63904df25956c7db0"}, - {file = "wrapt-1.14.0-cp35-cp35m-win_amd64.whl", hash = "sha256:d808a5a5411982a09fef6b49aac62986274ab050e9d3e9817ad65b2791ed1425"}, - {file = "wrapt-1.14.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b77159d9862374da213f741af0c361720200ab7ad21b9f12556e0eb95912cd48"}, - {file = "wrapt-1.14.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36a76a7527df8583112b24adc01748cd51a2d14e905b337a6fefa8b96fc708fb"}, - {file = "wrapt-1.14.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0057b5435a65b933cbf5d859cd4956624df37b8bf0917c71756e4b3d9958b9e"}, - {file = "wrapt-1.14.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a0a4ca02752ced5f37498827e49c414d694ad7cf451ee850e3ff160f2bee9d3"}, - {file = "wrapt-1.14.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8c6be72eac3c14baa473620e04f74186c5d8f45d80f8f2b4eda6e1d18af808e8"}, - {file = "wrapt-1.14.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:21b1106bff6ece8cb203ef45b4f5778d7226c941c83aaaa1e1f0f4f32cc148cd"}, - {file = "wrapt-1.14.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:493da1f8b1bb8a623c16552fb4a1e164c0200447eb83d3f68b44315ead3f9036"}, - {file = "wrapt-1.14.0-cp36-cp36m-win32.whl", hash = "sha256:89ba3d548ee1e6291a20f3c7380c92f71e358ce8b9e48161401e087e0bc740f8"}, - {file = "wrapt-1.14.0-cp36-cp36m-win_amd64.whl", hash = "sha256:729d5e96566f44fccac6c4447ec2332636b4fe273f03da128fff8d5559782b06"}, - {file = "wrapt-1.14.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:891c353e95bb11abb548ca95c8b98050f3620a7378332eb90d6acdef35b401d4"}, - {file = "wrapt-1.14.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23f96134a3aa24cc50614920cc087e22f87439053d886e474638c68c8d15dc80"}, - {file = "wrapt-1.14.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6807bcee549a8cb2f38f73f469703a1d8d5d990815c3004f21ddb68a567385ce"}, - {file = "wrapt-1.14.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6915682f9a9bc4cf2908e83caf5895a685da1fbd20b6d485dafb8e218a338279"}, - {file = "wrapt-1.14.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f2f3bc7cd9c9fcd39143f11342eb5963317bd54ecc98e3650ca22704b69d9653"}, - {file = "wrapt-1.14.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3a71dbd792cc7a3d772ef8cd08d3048593f13d6f40a11f3427c000cf0a5b36a0"}, - {file = "wrapt-1.14.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5a0898a640559dec00f3614ffb11d97a2666ee9a2a6bad1259c9facd01a1d4d9"}, - {file = "wrapt-1.14.0-cp37-cp37m-win32.whl", hash = "sha256:167e4793dc987f77fd476862d32fa404d42b71f6a85d3b38cbce711dba5e6b68"}, - {file = "wrapt-1.14.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d066ffc5ed0be00cd0352c95800a519cf9e4b5dd34a028d301bdc7177c72daf3"}, - {file = "wrapt-1.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d9bdfa74d369256e4218000a629978590fd7cb6cf6893251dad13d051090436d"}, - {file = "wrapt-1.14.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2498762814dd7dd2a1d0248eda2afbc3dd9c11537bc8200a4b21789b6df6cd38"}, - {file = "wrapt-1.14.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f24ca7953f2643d59a9c87d6e272d8adddd4a53bb62b9208f36db408d7aafc7"}, - {file = "wrapt-1.14.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b835b86bd5a1bdbe257d610eecab07bf685b1af2a7563093e0e69180c1d4af1"}, - {file = "wrapt-1.14.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b21650fa6907e523869e0396c5bd591cc326e5c1dd594dcdccac089561cacfb8"}, - {file = "wrapt-1.14.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:354d9fc6b1e44750e2a67b4b108841f5f5ea08853453ecbf44c81fdc2e0d50bd"}, - {file = "wrapt-1.14.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f83e9c21cd5275991076b2ba1cd35418af3504667affb4745b48937e214bafe"}, - {file = "wrapt-1.14.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:61e1a064906ccba038aa3c4a5a82f6199749efbbb3cef0804ae5c37f550eded0"}, - {file = "wrapt-1.14.0-cp38-cp38-win32.whl", hash = "sha256:28c659878f684365d53cf59dc9a1929ea2eecd7ac65da762be8b1ba193f7e84f"}, - {file = "wrapt-1.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:b0ed6ad6c9640671689c2dbe6244680fe8b897c08fd1fab2228429b66c518e5e"}, - {file = "wrapt-1.14.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3f7e671fb19734c872566e57ce7fc235fa953d7c181bb4ef138e17d607dc8a1"}, - {file = "wrapt-1.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87fa943e8bbe40c8c1ba4086971a6fefbf75e9991217c55ed1bcb2f1985bd3d4"}, - {file = "wrapt-1.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4775a574e9d84e0212f5b18886cace049a42e13e12009bb0491562a48bb2b758"}, - {file = "wrapt-1.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d57677238a0c5411c76097b8b93bdebb02eb845814c90f0b01727527a179e4d"}, - {file = "wrapt-1.14.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00108411e0f34c52ce16f81f1d308a571df7784932cc7491d1e94be2ee93374b"}, - {file = "wrapt-1.14.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d332eecf307fca852d02b63f35a7872de32d5ba8b4ec32da82f45df986b39ff6"}, - {file = "wrapt-1.14.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:01f799def9b96a8ec1ef6b9c1bbaf2bbc859b87545efbecc4a78faea13d0e3a0"}, - {file = "wrapt-1.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47045ed35481e857918ae78b54891fac0c1d197f22c95778e66302668309336c"}, - {file = "wrapt-1.14.0-cp39-cp39-win32.whl", hash = "sha256:2eca15d6b947cfff51ed76b2d60fd172c6ecd418ddab1c5126032d27f74bc350"}, - {file = "wrapt-1.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:bb36fbb48b22985d13a6b496ea5fb9bb2a076fea943831643836c9f6febbcfdc"}, - {file = "wrapt-1.14.0.tar.gz", hash = "sha256:8323a43bd9c91f62bb7d4be74cc9ff10090e7ef820e27bfe8815c57e68261311"}, + {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, + {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, + {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, + {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, + {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, + {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, + {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, + {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, + {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, + {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, + {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, + {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, + {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, + {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, + {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, + {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, + {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, + {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, + {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, + {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, + {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, + {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] yamllint = [ {file = "yamllint-1.26.3.tar.gz", hash = "sha256:3934dcde484374596d6b52d8db412929a169f6d9e52e20f9ade5bf3523d9b96e"}, ] zipp = [ - {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, - {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, + {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, + {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, ] diff --git a/pyproject.toml b/pyproject.toml index 10b9139..400c9e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,6 @@ readme = "README.md" keywords = ["json", "diff", "network"] classifiers = [ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", @@ -23,7 +22,7 @@ include = [ ] [tool.poetry.dependencies] -python = "^3.6.2" +python = "^3.7.0" deepdiff = "^5.5.0" jmespath = "^0.10.0" @@ -39,7 +38,7 @@ bandit = "*" invoke = "*" toml = "*" flake8 = "*" - +mypy = "*" [tool.black] line-length = 120 @@ -71,12 +70,13 @@ no-docstring-rgx="^(_|test_|Meta$)" [tool.pylint.messages_control] # Line length is enforced by Black, so pylint doesn't need to check it. # Pylint and Black disagree about how to format multi-line arrays; Black wins. +# too-many-branches disabled to supported nested logic in Operator. +# protected-access disabled as we want test the method disable = """, line-too-long, bad-continuation, - E5110, - R0912, - R0801 + too-many-branches, + protected-access """ [tool.pylint.miscellaneous] @@ -107,3 +107,19 @@ testpaths = [ "tests" ] addopts = "-vv --doctest-modules" + +[tool.mypy] +exclude = [ + '^tasks\.py', +] + +[[tool.mypy.overrides]] +module = [ + "deepdiff", + "jmespath", +] +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "tests.*" +ignore_errors = true diff --git a/tasks.py b/tasks.py index 6b20a26..aa89212 100644 --- a/tasks.py +++ b/tasks.py @@ -30,7 +30,7 @@ def is_truthy(arg): TOOL_CONFIG = PYPROJECT_CONFIG["tool"]["poetry"] # Can be set to a separate Python version to be used for launching or building image -PYTHON_VER = os.getenv("PYTHON_VER", "3.6") +PYTHON_VER = os.getenv("PYTHON_VER", "3.7") # Name of the docker image/image IMAGE_NAME = os.getenv("IMAGE_NAME", TOOL_CONFIG["name"]) # Tag for the image @@ -148,6 +148,13 @@ def bandit(context, path=".", local=INVOKE_LOCAL): run_cmd(context, exec_cmd, local) +@task(help={"local": "Run locally or within the Docker container"}) +def mypy(context, path=".", local=INVOKE_LOCAL): + """Run mypy to validate type hinting.""" + exec_cmd = f"mypy {path}" + run_cmd(context, exec_cmd, local) + + @task def cli(context): """Enter the image to perform troubleshooting or dev work.""" @@ -164,6 +171,6 @@ def tests(context, path=".", local=INVOKE_LOCAL): yamllint(context, path, local) pydocstyle(context, path, local) bandit(context, path, local) + mypy(context, path, local) pytest(context, local) - print("All tests have passed!") diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_data_normalization.py b/tests/test_data_normalization.py index 6d5760e..bc28661 100644 --- a/tests/test_data_normalization.py +++ b/tests/test_data_normalization.py @@ -16,5 +16,6 @@ @pytest.mark.parametrize("data, expected_output", flatten_list_tests) def test_value_parser(data, expected_output): + """Assert that flatten_list function flat a multiple nested list into a list of lists.""" output = flatten_list(data) assert expected_output == output, ASSERT_FAIL_MESSAGE.format(output=output, expected_output=expected_output) diff --git a/tests/test_diff_generator.py b/tests/test_diff_generator.py index 03b1a20..baaedc3 100644 --- a/tests/test_diff_generator.py +++ b/tests/test_diff_generator.py @@ -5,7 +5,8 @@ from .utility import load_mocks, ASSERT_FAIL_MESSAGE -exact_match_of_global_peers_via_napalm_getter = ( +# Diff test case 1. +global_peers = ( "napalm_getter_peer_state_change", "global.$peers$.*.[is_enabled,is_up]", [], @@ -21,21 +22,24 @@ }, ) -exact_match_of_bgp_peer_caps_via_api = ( +# Diff test case 2. +bgp_peer_caps = ( "api", "result[0].vrfs.default.peerList[*].[$peerAddress$,state,bgpPeerCaps]", [], {"7.7.7.7": {"state": {"new_value": "Connected", "old_value": "Idle"}}}, ) -exact_match_of_bgp_neigh_via_textfsm = ( +# Diff test case 3. +bgp_neigh = ( "textfsm", "result[*].[$bgp_neigh$,state]", [], {"10.17.254.2": {"state": {"new_value": "Up", "old_value": "Idle"}}}, ) -raw_diff_of_interface_ma1_via_api_value_exclude = ( +# Diff test case 4. Exclude filter applied. +raw_diff_of_interface_ma1_value_exclude = ( "raw_value_exclude", "result[*]", ["interfaceStatistics", "interfaceCounters"], @@ -49,7 +53,8 @@ }, ) -raw_diff_of_interface_ma1_via_api_novalue_exclude = ( +# Diff test case 5. Raw diff (no jmspath expression defined) and exclude filter applied. +raw_diff_of_interface_ma1_novalue_exclude = ( "raw_novalue_exclude", None, ["interfaceStatistics", "interfaceCounters"], @@ -68,7 +73,8 @@ }, ) -raw_diff_of_interface_ma1_via_api_novalue_noexclude = ( +# Diff test case 5. Raw diff (no jmspath expression defined) and no exclude filter applied. +raw_diff_of_interface_ma1_novalue_noexclude = ( "raw_novalue_noexclude", None, [], @@ -101,28 +107,32 @@ }, ) -exact_match_missing_item = ( +# Diff test case 6. Missing item in post. +missing_item = ( "napalm_getter_missing_peer", None, [], {"global": {"peers": {"7.7.7.7": "missing"}}}, ) -exact_match_additional_item = ( +# Diff test case 7. Extra item in post. +additional_item = ( "napalm_getter_additional_peer", None, [], {"global": {"peers": {"8.8.8.8": "new"}}}, ) -exact_match_changed_item = ( +# Diff test case 8. Item changed in post. +changed_item = ( "napalm_getter_changed_peer", None, [], {"global": {"peers": {"7.7.7.7": "missing", "8.8.8.8": "new"}}}, ) -exact_match_multi_nested_list = ( +# Diff test case 9. Value in multiple nested lists. +multi_nested_list = ( "exact_match_nested", "global.$peers$.*.*.ipv4.[accepted_prefixes,received_prefixes]", [], @@ -132,14 +142,16 @@ }, ) -exact_match_textfsm_ospf_int_br_raw = ( +# Diff test case 10. JMSPath empty string. +ospf_int_br_raw = ( "textfsm_ospf_int_br", "", [], {"state": {"new_value": "DOWN", "old_value": "P2P", "new_value_dup!": "DR", "old_value_dup!": "BDR"}}, ) -exact_match_textfsm_ospf_int_br_normalized = ( +# Diff test case 11. Extensive nu,ber of values. +ospf_int_br_normalized = ( "textfsm_ospf_int_br", "[*].[$interface$,area,ip_address_mask,cost,state,neighbors_fc]", [], @@ -149,14 +161,16 @@ }, ) -exact_match_test_issue_44_case_1 = ( +# Test GitLab Issues +# Test issue #44: https://github.com/networktocode-llc/netcompare/issues/44 +test_issue_44_case_1 = ( "raw_novalue_exclude", "result[*].interfaces.*.[$name$,interfaceStatus]", [], {"Management1": {"interfaceStatus": {"new_value": "connected", "old_value": "down"}}}, ) -exact_match_test_issue_44_case_2 = ( +test_issue_44_case_2 = ( "raw_novalue_exclude", "result[0].interfaces.*.[$name$,interfaceStatus]", [], @@ -165,20 +179,20 @@ eval_tests = [ - exact_match_of_global_peers_via_napalm_getter, - exact_match_of_bgp_peer_caps_via_api, - exact_match_of_bgp_neigh_via_textfsm, - raw_diff_of_interface_ma1_via_api_value_exclude, - raw_diff_of_interface_ma1_via_api_novalue_exclude, - raw_diff_of_interface_ma1_via_api_novalue_noexclude, - exact_match_missing_item, - exact_match_additional_item, - exact_match_changed_item, - exact_match_multi_nested_list, - exact_match_textfsm_ospf_int_br_raw, - exact_match_textfsm_ospf_int_br_normalized, - exact_match_test_issue_44_case_1, - exact_match_test_issue_44_case_2, + global_peers, + bgp_peer_caps, + bgp_neigh, + raw_diff_of_interface_ma1_value_exclude, + raw_diff_of_interface_ma1_novalue_exclude, + raw_diff_of_interface_ma1_novalue_noexclude, + missing_item, + additional_item, + changed_item, + multi_nested_list, + ospf_int_br_raw, + ospf_int_br_normalized, + test_issue_44_case_1, + test_issue_44_case_2, ] diff --git a/tests/test_diff_helpers.py b/tests/test_diff_helpers.py new file mode 100644 index 0000000..be7eb03 --- /dev/null +++ b/tests/test_diff_helpers.py @@ -0,0 +1,46 @@ +"""DIff helpers unit tests.""" +from netcompare.utils.diff_helpers import dict_merger, group_value, fix_deepdiff_key_names, get_diff_iterables_items + + +def test_dict_merger(): + """Tests that dict is merged as expected and duplicates identified.""" + original_dict = dict(key_1="my_key_1", key_5="my_key_5") + dict_to_merge = dict(key_1="my_key_1", key_2="my_key_2", key_3="my_key_3") + dict_merger(original_dict, dict_to_merge) + + assert original_dict == { + "key_1": "my_key_1", + "key_1_dup!": "my_key_1", + "key_2": "my_key_2", + "key_3": "my_key_3", + "key_5": "my_key_5", + } + + +def test_group_value(): + """Tests that nested dict is recursively created.""" + tree_list = ["10.1.0.0", "is_enabled"] + value = {"new_value": False, "old_value": True} + assert group_value(tree_list, value) == {"10.1.0.0": {"is_enabled": {"new_value": False, "old_value": True}}} + + +def test_fix_deepdiff_key_names(): + """Tests that deepdiff return is parsed properly.""" + deepdiff_object = {"root[0]['10.1.0.0']['is_enabled']": {"new_value": False, "old_value": True}} + assert fix_deepdiff_key_names(deepdiff_object) == { + "10.1.0.0": {"is_enabled": {"new_value": False, "old_value": True}} + } + + +def test_get_diff_iterables_items(): + """Tests that deepdiff return is parsed properly.""" + diff_result = { + "values_changed": {"root['Ethernet1'][0]['port']": {"new_value": "518", "old_value": "519"}}, + "iterable_item_added": { + "root['Ethernet3'][1]": {"hostname": "ios-xrv-unittest", "port": "Gi0/0/0/0"}, + }, + } + result = get_diff_iterables_items(diff_result) + + assert list(dict(result).keys())[0] == "['Ethernet3']" + assert list(list(dict(result).values())[0].values())[0] == [{"hostname": "ios-xrv-unittest", "port": "Gi0/0/0/0"}] diff --git a/tests/test_filter_parsers.py b/tests/test_filter_parsers.py index d98137f..a80f4a3 100644 --- a/tests/test_filter_parsers.py +++ b/tests/test_filter_parsers.py @@ -4,7 +4,7 @@ from .utility import ASSERT_FAIL_MESSAGE -exclude_filter_case_1 = ( +exclude_filter_test_case_1 = ( ["interfaceStatistics"], { "interfaces": { @@ -30,11 +30,12 @@ ) exclude_filter_tests = [ - exclude_filter_case_1, + exclude_filter_test_case_1, ] @pytest.mark.parametrize("exclude, data, expected_output", exclude_filter_tests) def test_exclude_filter(exclude, data, expected_output): + """Test exclude filter functionality.""" exclude_filter(data, exclude) assert expected_output == data, ASSERT_FAIL_MESSAGE.format(output=data, expected_output=expected_output) diff --git a/tests/test_sw_upgrade_device_state.py b/tests/test_sw_upgrade_device_state.py index 18f42fb..683b74d 100644 --- a/tests/test_sw_upgrade_device_state.py +++ b/tests/test_sw_upgrade_device_state.py @@ -6,7 +6,7 @@ @pytest.mark.parametrize( - "platform, command, jpath, expected_parameter, test_is_passed", + "platform, command, jpath, expected_parameter, check_should_pass", [ ("arista_eos", "show_version", "[*].[$image$,image]", {"image": "4.14.7M"}, True), ("arista_eos", "show_version", "[*].[$image$,image]", {"image": "no-match"}, False), @@ -16,7 +16,7 @@ ("cisco_nxos", "show_version", "[*].[$os$,os]", {"os": "no-match"}, False), ], ) -def test_show_version(platform, command, jpath, expected_parameter, test_is_passed): +def test_show_version(platform, command, jpath, expected_parameter, check_should_pass): """Test expected version with parameter_match.""" filename = f"{platform}_{command}.json" command = load_json_file("sw_upgrade", filename) @@ -24,11 +24,11 @@ def test_show_version(platform, command, jpath, expected_parameter, test_is_pass check = CheckType.init("parameter_match") value = check.get_value(command, jpath) eval_results, passed = check.evaluate(value, expected_parameter, "match") # pylint: disable=E1121 - assert passed is test_is_passed, f"FAILED, eval_result: {eval_results}" + assert passed is check_should_pass, f"FAILED, eval_result: {eval_results}" @pytest.mark.parametrize( - "platform, command, jpath, test_is_passed", + "platform, command, jpath, check_should_pass", [ ("arista_eos", "show_interface", "[*].[$interface$,link_status,protocol_status]", True), ("cisco_ios", "show_interface", "[*].[$interface$,link_status,protocol_status]", True), @@ -38,12 +38,12 @@ def test_show_version(platform, command, jpath, expected_parameter, test_is_pass ("cisco_nxos", "show_interface", "[*].[$interface$,link_status,admin_state]", False), ], ) -def test_show_interfaces_state(platform, command, jpath, test_is_passed): +def test_show_interfaces_state(platform, command, jpath, check_should_pass): """Test the interface status with exact_match.""" command_pre = load_json_file("sw_upgrade", f"{platform}_{command}.json") command_post = deepcopy(command_pre) - if test_is_passed is False: + if check_should_pass is False: if platform == "cisco_nxos": command_post[1]["admin_state"] = "down" else: @@ -54,7 +54,7 @@ def test_show_interfaces_state(platform, command, jpath, test_is_passed): pre_value = CheckType.get_value(command_pre, jpath) post_value = CheckType.get_value(command_post, jpath) eval_results, passed = check.evaluate(post_value, pre_value) - assert passed is test_is_passed, f"FAILED, eval_result: {eval_results}" + assert passed is check_should_pass, f"FAILED, eval_result: {eval_results}" @pytest.mark.parametrize( @@ -95,7 +95,7 @@ def test_show_ip_route_missing_and_additional_routes(platform, command): @pytest.mark.parametrize( - "platform, command, jpath, test_is_passed", + "platform, command, jpath, check_should_pass", [ ("arista_eos", "show_ip_bgp_summary", "[*].[$bgp_neigh$,state]", True), ("arista_eos", "show_ip_bgp_summary", "[*].[$bgp_neigh$,state]", False), @@ -105,12 +105,12 @@ def test_show_ip_route_missing_and_additional_routes(platform, command): ("cisco_nxos", "show_ip_bgp_neighbors", "[*].[$neighbor$,bgp_state]", False), ], ) -def test_bgp_neighbor_state(platform, command, jpath, test_is_passed): +def test_bgp_neighbor_state(platform, command, jpath, check_should_pass): """Test bgp neighbors state with exact_match.""" command_pre = load_json_file("sw_upgrade", f"{platform}_{command}.json") command_post = deepcopy(command_pre) - if test_is_passed is False: + if check_should_pass is False: state_key = "state" if "arista" in platform else "bgp_state" command_post[0][state_key] = "Idle" @@ -118,11 +118,11 @@ def test_bgp_neighbor_state(platform, command, jpath, test_is_passed): pre_value = CheckType.get_value(command_pre, jpath) post_value = CheckType.get_value(command_post, jpath) eval_results, passed = check.evaluate(post_value, pre_value) - assert passed is test_is_passed, f"FAILED, eval_result: {eval_results}" + assert passed is check_should_pass, f"FAILED, eval_result: {eval_results}" @pytest.mark.parametrize( - "platform, command, prfx_post_value, tolerance, test_is_passed", + "platform, command, prfx_post_value, tolerance, check_should_pass", [ ("cisco_ios", "show_ip_bgp_summary", "5457", 10, True), ("cisco_ios", "show_ip_bgp_summary", "5456", 10, False), @@ -132,7 +132,7 @@ def test_bgp_neighbor_state(platform, command, jpath, test_is_passed): ("cisco_nxos", "show_ip_bgp_summary", "Idle", 10, False), ], ) -def test_bgp_prefix_tolerance(platform, command, prfx_post_value, tolerance, test_is_passed): +def test_bgp_prefix_tolerance(platform, command, prfx_post_value, tolerance, check_should_pass): """Test bgp peer prefix count with tolerance.""" command_pre = load_json_file("sw_upgrade", f"{platform}_{command}.json") command_post = deepcopy(command_pre) @@ -145,11 +145,11 @@ def test_bgp_prefix_tolerance(platform, command, prfx_post_value, tolerance, tes post_value = CheckType.get_value(command_post, jpath) eval_results, passed = check.evaluate(post_value, pre_value, tolerance) # pylint: disable=E1121 - assert passed is test_is_passed, f"FAILED, eval_result: {eval_results}" + assert passed is check_should_pass, f"FAILED, eval_result: {eval_results}" @pytest.mark.parametrize( - "platform, command, jpath, test_is_passed", + "platform, command, jpath, check_should_pass", [ ("arista_eos", "show_ip_ospf_neighbors", "[*].[$neighbor_id$,state]", True), ("arista_eos", "show_ip_ospf_neighbors", "[*].[$neighbor_id$,state]", False), @@ -159,12 +159,12 @@ def test_bgp_prefix_tolerance(platform, command, prfx_post_value, tolerance, tes ("cisco_nxos", "show_ip_ospf_neighbors", "[*].[$neighbor_ipaddr$,state]", False), ], ) -def test_ospf_neighbor_state(platform, command, jpath, test_is_passed): +def test_ospf_neighbor_state(platform, command, jpath, check_should_pass): """Test ospf neighbors with exact_match.""" command_pre = load_json_file("sw_upgrade", f"{platform}_{command}.json") command_post = deepcopy(command_pre) - if test_is_passed is False: + if check_should_pass is False: command_post[0]["state"] = "2WAY" command_post = command_post[:1] @@ -172,7 +172,7 @@ def test_ospf_neighbor_state(platform, command, jpath, test_is_passed): pre_value = CheckType.get_value(command_pre, jpath) post_value = CheckType.get_value(command_post, jpath) eval_results, passed = check.evaluate(post_value, pre_value) - assert passed is test_is_passed, f"FAILED, eval_result: {eval_results}" + assert passed is check_should_pass, f"FAILED, eval_result: {eval_results}" @pytest.mark.skip(reason="Command output not available") @@ -181,7 +181,7 @@ def test_pim_neighbors(): @pytest.mark.parametrize( - "platform, command, test_is_passed", + "platform, command, check_should_pass", [ ("arista_eos", "show_lldp_neighbors", True), ("arista_eos", "show_lldp_neighbors", False), @@ -191,14 +191,14 @@ def test_pim_neighbors(): ("cisco_nxos", "show_lldp_neighbors", False), ], ) -def test_lldp_neighbor_state(platform, command, test_is_passed): +def test_lldp_neighbor_state(platform, command, check_should_pass): """Test LLDP neighbors with exact match.""" command_pre = load_json_file("sw_upgrade", f"{platform}_{command}.json") command_post = deepcopy(command_pre) - if test_is_passed is False: + if check_should_pass is False: command_post = command_post[:2] check = CheckType.init("exact_match") eval_results, passed = check.evaluate(command_post, command_pre) - assert passed is test_is_passed, f"FAILED, eval_result: {eval_results}" + assert passed is check_should_pass, f"FAILED, eval_result: {eval_results}" diff --git a/tests/test_type_checks.py b/tests/test_type_checks.py index a2a30ce..99ac67a 100644 --- a/tests/test_type_checks.py +++ b/tests/test_type_checks.py @@ -15,7 +15,7 @@ class CheckTypeChild(CheckType): assert ( "Can't instantiate abstract class CheckTypeChild" - " with abstract methods evaluate, validate" in error.value.__str__() + " with abstract methods _validate, evaluate" in error.value.__str__() ) @@ -26,14 +26,14 @@ class CheckTypeChild(CheckType): """Test Class.""" @staticmethod - def validate(**kwargs): + def _validate(*args): return None def evaluate(self, *args, **kwargs): return {}, True check = CheckTypeChild() - assert isinstance(check, CheckTypeChild) and check.validate() is None and check.evaluate() == ({}, True) + assert isinstance(check, CheckTypeChild) and check._validate() is None and check.evaluate() == ({}, True) @pytest.mark.parametrize( diff --git a/tests/test_validates.py b/tests/test_validates.py index 90507f1..bd7d2db 100644 --- a/tests/test_validates.py +++ b/tests/test_validates.py @@ -10,7 +10,7 @@ tolerance_wrong_value = ( "tolerance", {"tolerance": "10"}, - "Tolerance argument's value must be an integer. You have: .", + "Tolerance argument's value must be a number. You have: .", ) parameter_no_params = ( "parameter_match", @@ -122,11 +122,18 @@ @pytest.mark.parametrize("check_type_str, evaluate_args, expected_results", all_tests) -def test_tolerance_key_name(check_type_str, evaluate_args, expected_results): +def test_validate_arguments(check_type_str, evaluate_args, expected_results): """Test CheckType validate method for each check-type.""" check = CheckType.init(check_type_str) with pytest.raises(ValueError) as exc_info: - check.validate(**evaluate_args) + if check_type_str == "tolerance": + check._validate(evaluate_args.get("tolerance")) + elif check_type_str == "parameter_match": + check._validate(params=evaluate_args.get("params"), mode=evaluate_args.get("mode")) + elif check_type_str == "regex": + check._validate(regex=evaluate_args.get("regex"), mode=evaluate_args.get("mode")) + elif check_type_str == "operator": + check._validate(evaluate_args) assert exc_info.type is ValueError and exc_info.value.args[0] == expected_results diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py deleted file mode 100644 index c8f7298..0000000 --- a/tests/unit/conftest.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Used to setup fixtures to be used through tests""" -import pytest - - -@pytest.fixture() -def give_me_success(): - """ - Provides True to make tests pass - - Returns: - (bool): Returns True - """ - return True diff --git a/tests/unit/test_initial_pass.py b/tests/unit/test_initial_pass.py deleted file mode 100644 index 694c27e..0000000 --- a/tests/unit/test_initial_pass.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Example Test using Fixtures.""" - - -def test_initial_success(give_me_success): - "Assert give_me_success is true" - assert give_me_success