diff --git a/CHANGELOG.md b/CHANGELOG.md index 4525516..53d622f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,20 @@ # Changelog +## v0.0.3 +- Update documentation +- Fix evaluation logic in operator check +- Fix operator checks to follow other check_type logic #85 +- Add support for multiple ref key #92 +- Update gt and lt logic in operator check #64 +- Fix ref key in dict of dicts data type #91 +- Support single value in expression #94 +- Minor fixes ## v0.0.2 - - Update operator logic for returned result - Update docs ## v0.0.1 - - Initial release ## v0.0.1-beta.1 - - First beta release diff --git a/docs/usage.md b/docs/usage.md index f5e1df1..ce7692a 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -251,7 +251,7 @@ Let's have a look at a couple of examples: ``` We will define a JMESPath expression for the values we want to test and extract from the reference and comparison objects. ```python ->>> my_jmspath = "global.$peers$.*.*.ipv4.[accepted_prefixes,received_prefixes,sent_prefixes]" +>>> my_jmspath = "global.peers.$*$.*.ipv4.[accepted_prefixes,received_prefixes,sent_prefixes]" >>> reference_value = extract_data_from_json(reference_data, my_jmspath) >>> reference_value [{'10.1.0.0': {'accepted_prefixes': 900, @@ -478,7 +478,12 @@ The `operator` check is a collection of more specific checks divided into catego 2. `is-lt`: Check if the value of a specified element is lesser than a given numeric value. - `is-lt: 55`: checks if value is lower than 55 or not. +3. `is-ge`: Check if the value of a specified element is greater than or equal to a given numeric value. + - `is-ge: 2`: checks if value should be greater or equal than 2. +4. `is-le`: Check if the value of a specified element is lesser than or equal a given numeric value. + - `is-le: 55`: checks if value is lower or equal than 55 or not. + Examples: ```python diff --git a/jdiff/check_types.py b/jdiff/check_types.py index 9bf3e3d..8b1bd0d 100644 --- a/jdiff/check_types.py +++ b/jdiff/check_types.py @@ -1,5 +1,5 @@ """CheckType Implementation.""" -from typing import Mapping, Tuple, Dict, Any, Union +from typing import List, Tuple, Dict, Any, Union from abc import ABC, abstractmethod from .evaluators import diff_generator, parameter_evaluator, regex_evaluator, operator_evaluator @@ -136,7 +136,7 @@ def _validate(params, mode) -> None: # type: ignore[override] f"'mode' argument should be one of the following: {', '.join(mode_options)}. You have: {mode}" ) - def evaluate(self, params: Dict, value_to_compare: Mapping, mode: str) -> Tuple[Dict, bool]: # type: ignore[override] + def evaluate(self, params: Dict, value_to_compare: List[Dict], mode: str) -> Tuple[Dict, bool]: # type: ignore[override] """Parameter Match evaluator implementation.""" self._validate(params=params, mode=mode) # TODO: we don't use the mode? @@ -161,7 +161,7 @@ def _validate(regex, mode) -> None: # type: ignore[override] if mode not in mode_options: raise ValueError(f"'mode' argument should be {mode_options}. You have: {mode}") - def evaluate(self, regex: str, value_to_compare: Mapping, mode: str) -> Tuple[Dict, bool]: # type: ignore[override] + def evaluate(self, regex: str, value_to_compare: List[Dict[Any, Dict]], mode: str) -> Tuple[Dict, bool]: # type: ignore[override] """Regex Match evaluator implementation.""" self._validate(regex=regex, mode=mode) evaluation_result = regex_evaluator(value_to_compare, regex, mode) @@ -176,7 +176,7 @@ def _validate(params) -> None: # type: ignore[override] """Validate operator parameters.""" in_operators = ("is-in", "not-in", "in-range", "not-in-range") bool_operators = ("all-same",) - number_operators = ("is-gt", "is-lt") + number_operators = ("is-gt", "is-lt", "is-ge", "is-le") string_operators = ("contains", "not-contains") valid_options = ( in_operators, diff --git a/jdiff/evaluators.py b/jdiff/evaluators.py index 702474f..6b9474a 100644 --- a/jdiff/evaluators.py +++ b/jdiff/evaluators.py @@ -1,6 +1,6 @@ """Evaluators.""" import re -from typing import Any, Mapping, Dict, Tuple +from typing import Any, Mapping, Dict, Tuple, List from deepdiff import DeepDiff from .utils.diff_helpers import get_diff_iterables_items, fix_deepdiff_key_names from .operator import Operator @@ -36,7 +36,7 @@ def diff_generator(pre_result: Any, post_result: Any) -> Dict: return fix_deepdiff_key_names(result) -def parameter_evaluator(values: Mapping, parameters: Mapping, mode: str) -> Dict: +def parameter_evaluator(values: List[Dict], parameters: Mapping, mode: str) -> Dict: """Parameter Match evaluator engine. Args: @@ -51,30 +51,30 @@ def parameter_evaluator(values: Mapping, parameters: Mapping, mode: str) -> Dict Dictionary with all the items that have some value not matching the expectations from parameters """ if not isinstance(values, list): - raise TypeError("Something went wrong during jmespath parsing. 'values' must be of type List.") + raise TypeError("'values' must be of type List.") result = {} - for value in values: + for index, value in enumerate(values): # value: {'7.7.7.7': {'peerAddress': '7.7.7.7', 'localAsn': '65130.1101', 'linkType': 'externals if not isinstance(value, dict): - raise TypeError( - "Something went wrong during jmespath parsing. ", - f"'value' ({value}) must be of type Dict, and it's {type(value)}", - ) + raise TypeError(f"'value' ({value}) must be of type Dict, and it's {type(value)}") result_item = {} - # TODO: Why the 'value' dict has always ONE single element? we have to explain - # inner_key: '7.7.7.7' - inner_key = list(value.keys())[0] - # inner_value: [{'peerAddress': '7.7.7.7', 'localAsn': '65130.1101', 'linkType': 'externals'}] - inner_value = list(value.values())[0] + # When data has been normalized with $key$, get inner key and value + if len(value) == 1: + # inner_key: '7.7.7.7' + inner_key = list(value.keys())[0] + # inner_value: [{'peerAddress': '7.7.7.7', 'localAsn': '65130.1101', 'linkType': 'externals'}] + value = list(value.values())[0] + else: + inner_key = index for parameter_key, parameter_value in parameters.items(): - if mode == "match" and inner_value[parameter_key] != parameter_value: - result_item[parameter_key] = inner_value[parameter_key] - elif mode == "no-match" and inner_value[parameter_key] == parameter_value: - result_item[parameter_key] = inner_value[parameter_key] + if mode == "match" and value[parameter_key] != parameter_value: + result_item[parameter_key] = value[parameter_key] + elif mode == "no-match" and value[parameter_key] == parameter_value: + result_item[parameter_key] = value[parameter_key] if result_item: result[inner_key] = result_item @@ -82,10 +82,10 @@ def parameter_evaluator(values: Mapping, parameters: Mapping, mode: str) -> Dict return result -def regex_evaluator(values: Mapping, regex_expression: str, mode: str) -> Dict: +def regex_evaluator(values: List[Dict[Any, Dict]], regex_expression: str, mode: str) -> Dict: """Regex Match evaluator engine.""" # values: [{'7.7.7.7': {'peerGroup': 'EVPN-OVERLAY-SPINE'}}] - # parameter: {'regex': '.*UNDERLAY.*', 'mode': 'include'} + # parameter: {'regex': '.*UNDERLAY.*', 'mode': 'match'} result = {} if not isinstance(values, list): raise TypeError("Something went wrong during JMSPath parsing. 'values' must be of type List.") @@ -94,10 +94,10 @@ def regex_evaluator(values: Mapping, regex_expression: str, mode: str) -> Dict: for founded_value in item.values(): for value in founded_value.values(): match_result = re.search(regex_expression, value) - # Fail if there is not regex match + # Fail if there is no regex match for "match" mode if mode == "match" and not match_result: result.update(item) - # Fail if there is regex match + # Fail if there is regex match for "no-match" mode. elif mode == "no-match" and match_result: result.update(item) diff --git a/jdiff/extract_data.py b/jdiff/extract_data.py index 10ba001..f2b951a 100644 --- a/jdiff/extract_data.py +++ b/jdiff/extract_data.py @@ -1,7 +1,7 @@ """Extract data from JSON. Based on custom JMSPath implementation.""" import re import warnings -from typing import Mapping, List, Dict, Any, Union +from typing import Mapping, List, Dict, Any, Union, Optional import jmespath from .utils.data_normalization import exclude_filter, flatten_list from .utils.jmespath_parsers import ( @@ -9,10 +9,11 @@ jmespath_refkey_parser, associate_key_of_my_value, keys_values_zipper, + multi_reference_keys, ) -def extract_data_from_json(data: Union[Mapping, List], path: str = "*", exclude: List = None) -> Any: +def extract_data_from_json(data: Union[Mapping, List], path: str = "*", exclude: Optional[List] = None) -> Any: """Return wanted data from outpdevice data based on the check path. See unit test for complete example. Get the wanted values to be evaluated if JMESPath expression is defined, @@ -43,33 +44,36 @@ def extract_data_from_json(data: Union[Mapping, List], path: str = "*", exclude: # return if path is not specified return data + # Multi ref_key + if len(re.findall(r"\$.*?\$", path)) > 1: + clean_path = path.replace("$", "") + values = jmespath.search(f"{clean_path}{' | []' * (path.count('*') - 1)}", data) + return keys_values_zipper(multi_reference_keys(path, data), associate_key_of_my_value(clean_path, values)) + values = jmespath.search(jmespath_value_parser(path), data) if values is None: raise TypeError("JMSPath returned 'None'. Please, verify your JMSPath regex.") - # check for multi-nested lists if not found return here - if not any(isinstance(i, list) for i in values): - return values - - # process elements to check if lists should be flattened - for element in values: - for item in element: - # 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]]. You have "{values}".' - ) - if isinstance(item, list): - values = flatten_list(values) # flatten list and rewrite values - break # items are the same, need to check only first to see if this is a nested list - - paired_key_value = associate_key_of_my_value(jmespath_value_parser(path), values) + # check for multi-nested lists + if any(isinstance(i, list) for i in values): + # process elements to check if lists should be flattened + for element in values: + for item in element: + # 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]]. You have "{values}".' + ) + if isinstance(item, list): + values = flatten_list(values) # flatten list and rewrite values + break # items are the same, need to check only first to see if this is a nested list # We need to get a list of reference keys - list of strings. # Based on the expression or data we might have different data types # therefore we need to normalize. if re.search(r"\$.*\$", path): + paired_key_value = associate_key_of_my_value(jmespath_value_parser(path), values) wanted_reference_keys = jmespath.search(jmespath_refkey_parser(path), data) if isinstance(wanted_reference_keys, dict): # when wanted_reference_keys is dict() type diff --git a/jdiff/operator.py b/jdiff/operator.py index a7c7a0a..6c07000 100644 --- a/jdiff/operator.py +++ b/jdiff/operator.py @@ -51,7 +51,9 @@ def call_evaluation_logic(): ops = { ">": operator.gt, + ">=": operator.ge, "<": operator.lt, + "<=": operator.le, "is_in": operator.contains, "not_in": operator.contains, "contains": operator.contains, @@ -99,10 +101,18 @@ def is_gt(self) -> Tuple[List, bool]: """Is greather than operator caller.""" return self._loop_through_wrapper(">") + def is_ge(self) -> Tuple[List, bool]: + """Is greather or equal than operator caller.""" + return self._loop_through_wrapper(">=") + def is_lt(self) -> Tuple[List, bool]: """Is lower than operator caller.""" return self._loop_through_wrapper("<") + def is_le(self) -> Tuple[List, bool]: + """Is lower or equal than operator caller.""" + return self._loop_through_wrapper("<=") + def is_in(self) -> Tuple[List, bool]: """Is in operator caller.""" return self._loop_through_wrapper("is_in") diff --git a/jdiff/utils/jmespath_parsers.py b/jdiff/utils/jmespath_parsers.py index 5503eb7..ee1e669 100644 --- a/jdiff/utils/jmespath_parsers.py +++ b/jdiff/utils/jmespath_parsers.py @@ -8,6 +8,8 @@ import re from typing import Mapping, List, Union +import jmespath + def jmespath_value_parser(path: str): """ @@ -31,15 +33,15 @@ def jmespath_value_parser(path: str): path_suffix = path.split(".")[-1] if regex_match_ref_key: + reference_key = regex_match_ref_key.group() if regex_ref_key.search(path_suffix): # [$peerAddress$,prefixesReceived] --> [prefixesReceived] - reference_key = regex_match_ref_key.group() return path.replace(reference_key, "") # result[0].$vrfs$.default... --> result[0].vrfs.default.... - regex_normalized_value = re.search(r"\$.*\$", regex_match_ref_key.group()) + regex_normalized_value = re.search(r"\$.*\$", reference_key) if regex_normalized_value: - normalized_value = regex_match_ref_key.group().split("$")[1] + normalized_value = reference_key.split("$")[1] return path.replace(regex_normalized_value.group(), normalized_value) return path @@ -65,9 +67,9 @@ def jmespath_refkey_parser(path: str): splitted_jmespath[number] = regex_match_anchor.group().replace("$", "") if regex_match_anchor and not element.startswith("[") and not element.endswith("]"): - splitted_jmespath = splitted_jmespath[: number + 1] + splitted_jmespath = splitted_jmespath[:number] - return ".".join(splitted_jmespath) + return ".".join(splitted_jmespath) or "@" def associate_key_of_my_value(paths: str, wanted_value: List) -> List: @@ -85,13 +87,19 @@ def associate_key_of_my_value(paths: str, wanted_value: List) -> List: final_list = [] - for items in wanted_value: - if len(items) != len(my_key_value_list): - raise ValueError("Key's value len != from value len") + if not all(isinstance(item, list) for item in wanted_value) and len(my_key_value_list) == 1: + for item in wanted_value: + temp_dict = {my_key_value_list[0]: item} + final_list.append(temp_dict) + + else: + for items in wanted_value: + if len(items) != len(my_key_value_list): + raise ValueError("Key's value len != from value len") - temp_dict = {my_key_value_list[my_index]: my_value for my_index, my_value in enumerate(items)} + temp_dict = {my_key_value_list[my_index]: my_value for my_index, my_value in enumerate(items)} - final_list.append(temp_dict) + final_list.append(temp_dict) return final_list @@ -120,3 +128,40 @@ def keys_values_zipper(list_of_reference_keys: List, wanted_value_with_key: List final_result.append({my_key: wanted_value_with_key[my_index]}) return final_result + + +def multi_reference_keys(jmspath, data): + """Build a list of concatenated reference keys. + + Args: + jmspath: "$*$.peers.$*$.*.ipv4.[accepted_prefixes]" + data: tests/mock/napalm_get_bgp_neighbors/multi_vrf.json + + Returns: + ["global.10.1.0.0", "global.10.2.0.0", "global.10.64.207.255", "global.7.7.7.7", "vpn.10.1.0.0", "vpn.10.2.0.0"] + """ + ref_key_regex = re.compile(r"\$.*?\$") + mapping = [] + split_path = jmspath.split(".") + + ref_key_index = -1 # -1 as the starting value, so it will match split path list indexes + for index, element in enumerate(split_path): + if ref_key_regex.search(element): + ref_key_index += 1 + key_path = ( + ".".join(split_path[:index]).replace("$", "") or "@" + ) # @ is for top keys, as they are stripped with "*" + flat_path = f"{key_path}{' | []' * key_path.count('*')}" # | [] to flatten the data, nesting level is eq to "*" count + sub_data = jmespath.search(flat_path, data) # extract sub-data with up to the ref key + if isinstance(sub_data, dict): + keys = list(sub_data.keys()) + elif isinstance(sub_data, list): + keys = [] + for parent, children in zip( + mapping[ref_key_index - 1], sub_data + ): # refer to previous keys as they are already present in mapping + keys.extend(f"{parent}.{child}" for child in children.keys()) # concatenate keys + else: + raise ValueError("Ref key anchor must return either a dict or a list.") + mapping.append(keys) + return mapping[-1] # return last element as it has all previous ref_keys concatenated. diff --git a/poetry.lock b/poetry.lock index 2caaf47..6cb48ed 100644 --- a/poetry.lock +++ b/poetry.lock @@ -12,23 +12,9 @@ typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpyth typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} wrapt = ">=1.11,<2" -[[package]] -name = "attrs" -version = "22.1.0" -description = "Classes Without Boilerplate" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] - [[package]] name = "bandit" -version = "1.7.4" +version = "1.7.5" description = "Security oriented static analyser for python code." category = "dev" optional = false @@ -38,27 +24,29 @@ python-versions = ">=3.7" colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} GitPython = ">=1.0.1" PyYAML = ">=5.3.1" +rich = "*" 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"] +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)", "beautifulsoup4 (>=4.8.0)", "pylint (==1.9.4)", "tomli (>=1.1.0)"] +toml = ["tomli (>=1.1.0)"] yaml = ["pyyaml"] [[package]] name = "black" -version = "22.8.0" +version = "23.3.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" +packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} @@ -70,7 +58,7 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2022.9.14" +version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." category = "dev" optional = false @@ -78,14 +66,11 @@ python-versions = ">=3.6" [[package]] name = "charset-normalizer" -version = "2.1.1" +version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "dev" optional = false -python-versions = ">=3.6.0" - -[package.extras] -unicode_backport = ["unicodedata2"] +python-versions = ">=3.7.0" [[package]] name = "click" @@ -101,11 +86,11 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" -version = "0.4.5" +version = "0.4.6" description = "Cross-platform colored terminal text." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" [[package]] name = "deepdiff" @@ -123,15 +108,26 @@ cli = ["click (==8.0.3)", "pyyaml (==5.4.1)", "toml (==0.10.2)", "clevercsv (==0 [[package]] name = "dill" -version = "0.3.5.1" +version = "0.3.6" description = "serialize all of python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +python-versions = ">=3.7" [package.extras] graph = ["objgraph (>=1.7.2)"] +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "flake8" version = "3.9.2" @@ -162,19 +158,19 @@ dev = ["twine", "markdown", "flake8", "wheel"] [[package]] name = "gitdb" -version = "4.0.9" +version = "4.0.10" description = "Git Object Database" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.27" -description = "GitPython is a python library used to interact with Git repositories" +version = "3.1.31" +description = "GitPython is a Python library used to interact with Git repositories" category = "dev" optional = false python-versions = ">=3.7" @@ -193,7 +189,7 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.12.0" +version = "4.13.0" description = "Read metadata from Python packages" category = "dev" optional = false @@ -204,37 +200,37 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "jaraco.tidelift (>=1.4)"] perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" [[package]] name = "invoke" -version = "1.7.1" +version = "2.0.0" description = "Pythonic task execution" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "isort" -version = "5.10.1" +version = "5.11.5" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6.1,<4.0" +python-versions = ">=3.7.0" [package.extras] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] -requirements_deprecated_finder = ["pipreqs", "pip-api"] +pipfile-deprecated-finder = ["pipreqs", "requirementslib", "pip-shims (>=0.5.2)"] +requirements-deprecated-finder = ["pipreqs", "pip-api"] colors = ["colorama (>=0.4.3,<0.5.0)"] plugins = ["setuptools"] @@ -262,11 +258,11 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "lazy-object-proxy" -version = "1.7.1" +version = "1.9.0" description = "A fast and thorough lazy object proxy." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "markdown" @@ -282,9 +278,31 @@ importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} [package.extras] testing = ["coverage", "pyyaml"] +[[package]] +name = "markdown-it-py" +version = "2.2.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +mdurl = ">=0.1,<1.0" +typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code_style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" -version = "2.1.1" +version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." category = "dev" optional = false @@ -298,6 +316,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "mergedeep" version = "1.3.4" @@ -308,65 +334,69 @@ python-versions = ">=3.6" [[package]] name = "mkdocs" -version = "1.3.1" +version = "1.4.2" description = "Project documentation with Markdown." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -click = ">=3.3" +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} ghp-import = ">=1.0" -importlib-metadata = ">=4.3" -Jinja2 = ">=2.10.2" -Markdown = ">=3.2.1,<3.4" +importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +jinja2 = ">=2.11.1" +markdown = ">=3.2.1,<3.4" mergedeep = ">=1.3.4" packaging = ">=20.5" -PyYAML = ">=3.10" +pyyaml = ">=5.1" pyyaml-env-tag = ">=0.1" +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.8\""} watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pyyaml-env-tag (==0.1)", "pyyaml (==5.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] [[package]] name = "mkdocs-include-markdown-plugin" -version = "3.8.1" +version = "3.9.1" description = "Mkdocs Markdown includer plugin." category = "dev" optional = false python-versions = ">=3.6" [package.extras] -dev = ["bump2version (==1.0.1)", "flake8 (==3.9.2)", "flake8-implicit-str-concat (==0.2.0)", "flake8-print (==4.0.0)", "isort (==5.9.1)", "mdpo (==0.3.61)", "mkdocs (==1.3.1)", "pre-commit (==2.13.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "pyupgrade (==2.19.4)", "yamllint (==1.26.1)"] -test = ["mkdocs (==1.3.1)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)"] +dev = ["bump2version (==1.0.1)", "mkdocs (==1.4.0)", "pre-commit", "pytest (==7.1.3)", "pytest-cov (==3.0.0)", "tox"] +test = ["mkdocs (==1.4.0)", "pytest (==7.1.3)", "pytest-cov (==3.0.0)"] [[package]] name = "mypy" -version = "0.971" +version = "1.2.0" description = "Optional static typing for Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -mypy-extensions = ">=0.4.3" +mypy-extensions = ">=1.0.0" 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)"] +install-types = ["pip"] python2 = ["typed-ast (>=1.4.0,<2)"] reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" [[package]] name = "ordered-set" @@ -381,18 +411,15 @@ dev = ["pytest", "black", "mypy"] [[package]] name = "packaging" -version = "21.3" +version = "23.1" description = "Core utilities for Python packages" category = "dev" optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +python-versions = ">=3.7" [[package]] name = "pathspec" -version = "0.10.1" +version = "0.11.1" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false @@ -400,7 +427,7 @@ python-versions = ">=3.7" [[package]] name = "pbr" -version = "5.10.0" +version = "5.11.1" description = "Python Build Reasonableness" category = "dev" optional = false @@ -408,15 +435,18 @@ python-versions = ">=2.6" [[package]] name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "3.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" +[package.dependencies] +typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} + [package.extras] -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)"] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)", "sphinx (>=6.1.3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest (>=7.2.2)"] [[package]] name = "pluggy" @@ -433,14 +463,6 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - [[package]] name = "pycodestyle" version = "2.7.0" @@ -451,17 +473,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pydocstyle" -version = "6.1.1" +version = "6.3.0" description = "Python docstring style checker" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -snowballstemmer = "*" +importlib-metadata = {version = ">=2.0.0,<5.0.0", markers = "python_version < \"3.8\""} +snowballstemmer = ">=2.2.0" [package.extras] -toml = ["toml"] +toml = ["tomli (>=1.2.3)"] [[package]] name = "pyflakes" @@ -471,6 +494,17 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pygments" +version = "2.15.1" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +plugins = ["importlib-metadata"] + [[package]] name = "pylint" version = "2.13.9" @@ -492,37 +526,25 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\"" [package.extras] testutil = ["gitpython (>3)"] -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "dev" -optional = false -python-versions = ">=3.6.8" - -[package.extras] -diagrams = ["railroad-diagrams", "jinja2"] - [[package]] name = "pytest" -version = "7.1.3" +version = "7.3.1" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" [package.dependencies] -attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -tomli = ">=1.0.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] [[package]] name = "python-dateutil" @@ -556,7 +578,7 @@ pyyaml = "*" [[package]] name = "requests" -version = "2.28.1" +version = "2.28.2" description = "Python HTTP for Humans." category = "dev" optional = false @@ -564,7 +586,7 @@ python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" +charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" @@ -588,6 +610,22 @@ six = "*" fixture = ["fixtures"] test = ["fixtures", "mock", "purl", "pytest", "sphinx", "testrepository (>=0.0.18)", "testtools", "requests-futures"] +[[package]] +name = "rich" +version = "13.3.4" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "dev" +optional = false +python-versions = ">=3.7.0" + +[package.dependencies] +markdown-it-py = ">=2.2.0,<3.0.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "six" version = "1.16.0" @@ -614,7 +652,7 @@ python-versions = "*" [[package]] name = "stevedore" -version = "3.5.0" +version = "3.5.2" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false @@ -650,7 +688,7 @@ python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "4.3.0" +version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "dev" optional = false @@ -658,11 +696,11 @@ python-versions = ">=3.7" [[package]] name = "urllib3" -version = "1.26.12" +version = "1.26.15" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.extras] brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] @@ -671,18 +709,18 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "watchdog" -version = "2.1.9" +version = "3.0.0" description = "Filesystem events monitoring" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "wrapt" -version = "1.14.1" +version = "1.15.0" description = "Module for decorators, wrappers and monkey patching." category = "dev" optional = false @@ -690,11 +728,11 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "yamllint" -version = "1.28.0" +version = "1.30.0" description = "A linter for YAML files." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] pathspec = ">=0.5.3" @@ -702,28 +740,24 @@ pyyaml = "*" [[package]] name = "zipp" -version = "3.8.1" +version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "sphinx-lint", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "jaraco.functools", "more-itertools", "big-o", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8"] [metadata] lock-version = "1.1" python-versions = "^3.7.0" -content-hash = "2be5d1d803d9c1d44b21902842a6c839586c8f9e95b056dada941b1a363971fc" +content-hash = "4a5e951b189b9a59af85323d6b4422b9861808a113265e1ce9258d71f6d4761e" [metadata.files] astroid = [] -attrs = [] -bandit = [ - {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, - {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, -] +bandit = [] black = [] certifi = [] charset-normalizer = [] @@ -731,15 +765,13 @@ click = [ {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.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] -deepdiff = [] -dill = [ - {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"}, +colorama = [] +deepdiff = [ + {file = "deepdiff-5.8.1-py3-none-any.whl", hash = "sha256:e9aea49733f34fab9a0897038d8f26f9d94a97db1790f1b814cced89e9e0d2b7"}, + {file = "deepdiff-5.8.1.tar.gz", hash = "sha256:8d4eb2c4e6cbc80b811266419cb71dd95a157094a3947ccf937a94d44943c7b8"}, ] +dill = [] +exceptiongroup = [] flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, @@ -748,116 +780,33 @@ ghp-import = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, ] -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.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, - {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, -] +gitdb = [] +gitpython = [] idna = [] importlib-metadata = [] -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.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"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, -] -jinja2 = [] -jmespath = [] -lazy-object-proxy = [ - {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, - {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, -] +iniconfig = [] +invoke = [] +isort = [] +jinja2 = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] +jmespath = [ + {file = "jmespath-0.10.0-py2.py3-none-any.whl", hash = "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"}, + {file = "jmespath-0.10.0.tar.gz", hash = "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9"}, +] +lazy-object-proxy = [] markdown = [ {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, ] -markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +markdown-it-py = [] +markupsafe = [] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] -mccabe = [] +mdurl = [] mergedeep = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, @@ -865,37 +814,33 @@ mergedeep = [ mkdocs = [] mkdocs-include-markdown-plugin = [] mypy = [] -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 = [] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +mypy-extensions = [] +ordered-set = [ + {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 = [] pathspec = [] pbr = [] -platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +platformdirs = [] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -pluggy = [] -py = [] -pycodestyle = [] -pydocstyle = [ - {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, - {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, +pycodestyle = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] -pyflakes = [] +pydocstyle = [] +pyflakes = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] +pygments = [] pylint = [ {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.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] pytest = [] python-dateutil = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, @@ -940,11 +885,9 @@ pyyaml-env-tag = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] -requests = [ - {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, - {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, -] +requests = [] requests-mock = [] +rich = [] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -957,11 +900,11 @@ snowballstemmer = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] -stevedore = [ - {file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"}, - {file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"}, +stevedore = [] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -toml = [] tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -992,103 +935,9 @@ typed-ast = [ {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.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, -] +typing-extensions = [] urllib3 = [] -watchdog = [ - {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330"}, - {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d"}, - {file = "watchdog-2.1.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658"}, - {file = "watchdog-2.1.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591"}, - {file = "watchdog-2.1.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33"}, - {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846"}, - {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3"}, - {file = "watchdog-2.1.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654"}, - {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39"}, - {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7"}, - {file = "watchdog-2.1.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd"}, - {file = "watchdog-2.1.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3"}, - {file = "watchdog-2.1.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d"}, - {file = "watchdog-2.1.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_armv7l.whl", hash = "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_i686.whl", hash = "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64.whl", hash = "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_s390x.whl", hash = "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6"}, - {file = "watchdog-2.1.9-py3-none-win32.whl", hash = "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1"}, - {file = "watchdog-2.1.9-py3-none-win_amd64.whl", hash = "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c"}, - {file = "watchdog-2.1.9-py3-none-win_ia64.whl", hash = "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428"}, - {file = "watchdog-2.1.9.tar.gz", hash = "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609"}, -] -wrapt = [ - {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"}, -] +watchdog = [] +wrapt = [] yamllint = [] zipp = [] diff --git a/pyproject.toml b/pyproject.toml index f13706c..5f3b7c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "jdiff" -version = "0.0.2" +version = "0.0.3" description = "A light-weight library to compare structured output from network devices show commands." authors = ["Network to Code, LLC "] license = "Apache-2.0" @@ -41,6 +41,7 @@ flake8 = "*" mypy = "*" mkdocs = "^1.3.1" mkdocs-include-markdown-plugin = "^3.6.1" +importlib-metadata = "<5.0.0" # https://importlib-metadata.readthedocs.io/en/latest/history.html#v5-0-0 [tool.black] line-length = 120 diff --git a/tests/mock/napalm_get_bgp_neighbors/multi_vrf.json b/tests/mock/napalm_get_bgp_neighbors/multi_vrf.json new file mode 100644 index 0000000..0064472 --- /dev/null +++ b/tests/mock/napalm_get_bgp_neighbors/multi_vrf.json @@ -0,0 +1,138 @@ +{ + "global": { + "peers": { + "10.1.0.0": { + "address_family": { + "ipv4": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + }, + "ipv6": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + } + }, + "description": "", + "is_enabled": true, + "is_up": true, + "local_as": 4268360780, + "remote_as": 67890, + "remote_id": "0.0.0.0", + "uptime": 1783 + }, + "10.2.0.0": { + "address_family": { + "ipv4": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + }, + "ipv6": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + } + }, + "description": "", + "is_enabled": true, + "is_up": false, + "local_as": 4268360780, + "remote_as": 67890, + "remote_id": "0.0.0.0", + "uptime": 1782 + }, + "10.64.207.255": { + "address_family": { + "ipv4": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + }, + "ipv6": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + } + }, + "description": "", + "is_enabled": false, + "is_up": true, + "local_as": 4268360780, + "remote_as": 12345, + "remote_id": "0.0.0.0", + "uptime": 1782 + }, + "7.7.7.7": { + "address_family": { + "ipv4": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + }, + "ipv6": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + } + }, + "description": "", + "is_enabled": true, + "is_up": true, + "local_as": 4268360780, + "remote_as": 67890, + "remote_id": "0.0.0.0", + "uptime": 0 + } + }, + "router_id": "1.1.0.1" + }, + "vpn": { + "peers": { + "10.1.0.0": { + "address_family": { + "ipv4": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + }, + "ipv6": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + } + }, + "description": "", + "is_enabled": true, + "is_up": true, + "local_as": 4268360780, + "remote_as": 67890, + "remote_id": "0.0.0.0", + "uptime": 1783 + }, + "10.2.0.0": { + "address_family": { + "ipv4": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + }, + "ipv6": { + "accepted_prefixes": 1000, + "received_prefixes": 1000, + "sent_prefixes": 1000 + } + }, + "description": "", + "is_enabled": true, + "is_up": false, + "local_as": 4268360780, + "remote_as": 67890, + "remote_id": "0.0.0.0", + "uptime": 1782 + } + }, + "router_id": "10.1.0.1" + } +} diff --git a/tests/mock/parameter_match/napalm_facts.json b/tests/mock/parameter_match/napalm_facts.json new file mode 100644 index 0000000..fed110a --- /dev/null +++ b/tests/mock/parameter_match/napalm_facts.json @@ -0,0 +1,12 @@ +[ + { + "uptime": 5100, + "vendor": "Juniper", + "hostname": "vsrx", + "fqdn": "vsrx", + "os_version": "12.1X47-D20.7", + "serial_number": "beb914a9cca3", + "model": "FIREFLY-PERIMETER", + "interface_list": ["lo0"] + } +] diff --git a/tests/test_diff_generator.py b/tests/test_diff_generator.py index fcf5ee8..9c0892d 100644 --- a/tests/test_diff_generator.py +++ b/tests/test_diff_generator.py @@ -8,7 +8,7 @@ # Diff test case 1. global_peers = ( "napalm_getter_peer_state_change", - "global.$peers$.*.[is_enabled,is_up]", + "global.peers.$*$.[is_enabled,is_up]", [], { "10.1.0.0": { @@ -134,7 +134,7 @@ # Diff test case 9. Value in multiple nested lists. multi_nested_list = ( "exact_match_nested", - "global.$peers$.*.*.ipv4.[accepted_prefixes,received_prefixes]", + "global.peers.$*$.*.ipv4.[accepted_prefixes,received_prefixes]", [], { "10.1.0.0": {"accepted_prefixes": {"new_value": -1, "old_value": -9}}, diff --git a/tests/test_get_value.py b/tests/test_get_value.py index 737a12a..6288386 100644 --- a/tests/test_get_value.py +++ b/tests/test_get_value.py @@ -1,16 +1,139 @@ -"""Test GitHub issues.""" +"""Test extract_data_from_json.""" import pytest from jdiff import extract_data_from_json +from .utility import load_json_file, ASSERT_FAIL_MESSAGE -my_data = [{"global": {"peers": {"10.1.0.0": "peer1", "10.2.0.0": "peer2"}}}] +test_cases_extract_data_none = [ + "global[*]", + 'global.peers."1.1.1.1"', +] -@pytest.mark.parametrize("data", my_data) -def test_jmspath_return_none(data): - """Handle exception when JMSPath retunr None.""" - my_jmspath = "global[*]" +@pytest.mark.parametrize("jmspath", test_cases_extract_data_none) +def test_jmspath_return_none(jmspath): + """Handle exception when JMSPath returns None.""" + data = {"global": {"peers": {"10.1.0.0": "peer1", "10.2.0.0": "peer2"}}} with pytest.raises(TypeError) as error: - extract_data_from_json(data=data, path=my_jmspath)() # pylint: disable=E0110 + extract_data_from_json(data=data, path=jmspath) assert "JMSPath returned 'None'. Please, verify your JMSPath regex." in error.value.__str__() + + +test_cases_extract_data_no_ref_key = [ + ("global.peers.*.*.ipv6.[accepted_prefixes]", [[1000], [1000], [1000], [1000]]), + ("vpn.peers.*.*.ipv6.[accepted_prefixes]", [[1000], [1000]]), + ( + "*.peers.*.*.*.[accepted_prefixes]", + [[1000], [1000], [1000], [1000], [1000], [1000], [1000], [1000], [1000], [1000], [1000], [1000]], + ), +] + + +test_cases_extract_data_with_ref_key = [ + ( + "global.peers.$*$.*.ipv6.[accepted_prefixes]", + [ + {"10.1.0.0": {"accepted_prefixes": 1000}}, + {"10.2.0.0": {"accepted_prefixes": 1000}}, + {"10.64.207.255": {"accepted_prefixes": 1000}}, + {"7.7.7.7": {"accepted_prefixes": 1000}}, + ], + ), + ( + "vpn.peers.$*$.*.ipv6.[accepted_prefixes]", + [{"10.1.0.0": {"accepted_prefixes": 1000}}, {"10.2.0.0": {"accepted_prefixes": 1000}}], + ), + ( + "$*$.peers.$*$.*.ipv4.[accepted_prefixes,received_prefixes,sent_prefixes]", + [ + {"global.10.1.0.0": {"accepted_prefixes": 1000, "received_prefixes": 1000, "sent_prefixes": 1000}}, + {"global.10.2.0.0": {"accepted_prefixes": 1000, "received_prefixes": 1000, "sent_prefixes": 1000}}, + {"global.10.64.207.255": {"accepted_prefixes": 1000, "received_prefixes": 1000, "sent_prefixes": 1000}}, + {"global.7.7.7.7": {"accepted_prefixes": 1000, "received_prefixes": 1000, "sent_prefixes": 1000}}, + {"vpn.10.1.0.0": {"accepted_prefixes": 1000, "received_prefixes": 1000, "sent_prefixes": 1000}}, + {"vpn.10.2.0.0": {"accepted_prefixes": 1000, "received_prefixes": 1000, "sent_prefixes": 1000}}, + ], + ), + ( + "$*$.peers.$*$.*.ipv6.[received_prefixes,sent_prefixes]", + [ + {"global.10.1.0.0": {"received_prefixes": 1000, "sent_prefixes": 1000}}, + {"global.10.2.0.0": {"received_prefixes": 1000, "sent_prefixes": 1000}}, + {"global.10.64.207.255": {"received_prefixes": 1000, "sent_prefixes": 1000}}, + {"global.7.7.7.7": {"received_prefixes": 1000, "sent_prefixes": 1000}}, + {"vpn.10.1.0.0": {"received_prefixes": 1000, "sent_prefixes": 1000}}, + {"vpn.10.2.0.0": {"received_prefixes": 1000, "sent_prefixes": 1000}}, + ], + ), + pytest.param( + "$*$.peers.$*$.address_family.$*$.[accepted_prefixes]", + "", + marks=pytest.mark.xfail(reason="Jmespath issue - path returns empty list."), + ), + ("global.peers.*.is_enabled", [True, True, False, True]), + ( + "global.peers.$*$.is_enabled", + [ + {"10.1.0.0": {"is_enabled": True}}, + {"10.2.0.0": {"is_enabled": True}}, + {"10.64.207.255": {"is_enabled": False}}, + {"7.7.7.7": {"is_enabled": True}}, + ], + ), + ( + "global.peers.$*$.[is_enabled]", + [ + {"10.1.0.0": {"is_enabled": True}}, + {"10.2.0.0": {"is_enabled": True}}, + {"10.64.207.255": {"is_enabled": False}}, + {"7.7.7.7": {"is_enabled": True}}, + ], + ), +] + + +@pytest.mark.parametrize( + "jmspath, expected_value", test_cases_extract_data_no_ref_key + test_cases_extract_data_with_ref_key +) +def test_extract_data_from_json(jmspath, expected_value): + """Test JMSPath return value.""" + data = load_json_file("napalm_get_bgp_neighbors", "multi_vrf.json") + value = extract_data_from_json(data=data, path=jmspath) + + assert value == expected_value, ASSERT_FAIL_MESSAGE.format(output=value, expected_output=expected_value) + + +test_cases_top_key_anchor = [ + ("$*$.is_enabled", [{".local.": {"is_enabled": True}}, {".local..0": {"is_enabled": True}}]), + ("$*$.is_up", [{".local.": {"is_up": True}}, {".local..0": {"is_up": True}}]), +] + + +@pytest.mark.parametrize("jmspath, expected_value", test_cases_top_key_anchor) +def test_top_key_anchor(jmspath, expected_value): + """Test JMSPath return value for anchoring the top key.""" + data = { + ".local.": { + "description": "", + "is_enabled": True, + "is_up": True, + "last_flapped": -1, + "mac_address": "Unspecified", + "mtu": 0, + "speed": -1, + }, + ".local..0": { + "description": "", + "is_enabled": True, + "is_up": True, + "last_flapped": -1, + "mac_address": "Unspecified", + "mtu": 0, + "speed": -1, + }, + } + + value = extract_data_from_json(data=data, path=jmspath) + + assert value == expected_value, ASSERT_FAIL_MESSAGE.format(output=value, expected_output=expected_value) diff --git a/tests/test_jmespath_parsers.py b/tests/test_jmespath_parsers.py index 3e37728..972acc1 100644 --- a/tests/test_jmespath_parsers.py +++ b/tests/test_jmespath_parsers.py @@ -5,8 +5,9 @@ jmespath_refkey_parser, keys_values_zipper, associate_key_of_my_value, + multi_reference_keys, ) -from .utility import ASSERT_FAIL_MESSAGE +from .utility import load_json_file, ASSERT_FAIL_MESSAGE value_parser_case_1 = ( @@ -26,6 +27,21 @@ "result[0].vrfs.default.peerList[*].[peerAddress,prefixesReceived]", ) + +value_parser_tests = [ + value_parser_case_1, + value_parser_case_2, + value_parser_case_3, + value_parser_case_4, +] + + +@pytest.mark.parametrize("path, expected_output", value_parser_tests) +def test_value_parser(path, expected_output): + output = jmespath_value_parser(path) + assert expected_output == output, ASSERT_FAIL_MESSAGE.format(output=output, expected_output=expected_output) + + keyref_parser_case_1 = ( "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesReceived]", "result[0].vrfs.default.peerList[*].peerAddress", @@ -40,31 +56,23 @@ ) keyref_parser_case_4 = ( "result[0].$vrfs$.default.peerList[*].[peerAddress,prefixesReceived]", - "result[0].vrfs", + "result[0]", +) +keyref_parser_case_5 = ( + "global.peers.$*$.*.ipv4.[accepted_prefixes,received_prefixes,sent_prefixes]", + "global.peers", ) -value_parser_tests = [ - value_parser_case_1, - value_parser_case_2, - value_parser_case_3, - value_parser_case_4, -] - keyref_parser_tests = [ keyref_parser_case_1, keyref_parser_case_2, keyref_parser_case_3, keyref_parser_case_4, + keyref_parser_case_5, ] -@pytest.mark.parametrize("path, expected_output", value_parser_tests) -def test_value_parser(path, expected_output): - output = jmespath_value_parser(path) - assert expected_output == output, ASSERT_FAIL_MESSAGE.format(output=output, expected_output=expected_output) - - @pytest.mark.parametrize("path, expected_output", keyref_parser_tests) def test_keyref_parser(path, expected_output): output = jmespath_refkey_parser(path) @@ -77,21 +85,11 @@ def test_keyref_parser(path, expected_output): [{"10.1.0.0": {"is_enabled": False, "is_up": False}}, {"10.2.0.0": {"is_enabled": True, "is_up": True}}], ) -keys_association_case_1 = ( - "global.peers.*.[is_enabled,is_up]", - [[True, False], [True, False]], - [{"is_enabled": True, "is_up": False}, {"is_enabled": True, "is_up": False}], -) - keys_zipper_tests = [ keys_zipper_case_1, ] -keys_association_test = [ - keys_association_case_1, -] - @pytest.mark.parametrize("ref_keys, wanted_values, expected_output", keys_zipper_tests) def test_keys_zipper(ref_keys, wanted_values, expected_output): @@ -99,7 +97,57 @@ def test_keys_zipper(ref_keys, wanted_values, expected_output): assert expected_output == output, ASSERT_FAIL_MESSAGE.format(output=output, expected_output=expected_output) +keys_association_case_1 = ( + "global.peers.*.[is_enabled,is_up]", + [[True, False], [True, False]], + [{"is_enabled": True, "is_up": False}, {"is_enabled": True, "is_up": False}], +) + + +keys_association_test = [ + keys_association_case_1, +] + + @pytest.mark.parametrize("path, wanted_values, expected_output", keys_association_test) def test_keys_association(path, wanted_values, expected_output): output = associate_key_of_my_value(path, wanted_values) assert expected_output == output, ASSERT_FAIL_MESSAGE.format(output=output, expected_output=expected_output) + + +multi_ref_key_case_1 = ( + "$*$.peers.$*$.*.ipv4.[accepted_prefixes]", + ["global.10.1.0.0", "global.10.2.0.0", "global.10.64.207.255", "global.7.7.7.7", "vpn.10.1.0.0", "vpn.10.2.0.0"], +) + + +multi_ref_key_case_2 = ( + "$*$.peers.$*$.address_family.$*$.[accepted_prefixes]", + [ + "global.10.1.0.0.ipv4", + "global.10.1.0.0.ipv6", + "global.10.2.0.0.ipv4", + "global.10.2.0.0.ipv6", + "global.10.64.207.255.ipv4", + "global.10.64.207.255.ipv6", + "global.7.7.7.7.ipv4", + "global.7.7.7.7.ipv6", + "vpn.10.1.0.0.ipv4", + "vpn.10.1.0.0.ipv6", + "vpn.10.2.0.0.ipv4", + "vpn.10.2.0.0.ipv6", + ], +) + + +multi_ref_key_test_cases = [ + multi_ref_key_case_1, + multi_ref_key_case_2, +] + + +@pytest.mark.parametrize("path, expected_output", multi_ref_key_test_cases) +def test_multi_ref_key(path, expected_output): + data = load_json_file("napalm_get_bgp_neighbors", "multi_vrf.json") + output = multi_reference_keys(path, data) + assert expected_output == output, ASSERT_FAIL_MESSAGE.format(output=output, expected_output=expected_output) diff --git a/tests/test_operators.py b/tests/test_operators.py index 968e6ef..a67f8a8 100644 --- a/tests/test_operators.py +++ b/tests/test_operators.py @@ -75,6 +75,20 @@ "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesSent]", ([], True), ) +operator_is_ge_equal = ( + "pre.json", + "operator", + {"params": {"mode": "is-ge", "operator_data": 50}}, + "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesSent]", + ([], True), +) +operator_is_ge_greater = ( + "pre.json", + "operator", + {"params": {"mode": "is-ge", "operator_data": 20}}, + "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesSent]", + ([], True), +) operator_is_lt = ( "pre.json", "operator", @@ -82,6 +96,20 @@ "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesSent]", ([], True), ) +operator_is_le_equal = ( + "pre.json", + "operator", + {"params": {"mode": "is-le", "operator_data": 50}}, + "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesSent]", + ([], True), +) +operator_is_le_lower = ( + "pre.json", + "operator", + {"params": {"mode": "is-le", "operator_data": 60}}, + "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesSent]", + ([], True), +) operator_is_in = ( "pre.json", "operator", @@ -132,7 +160,11 @@ operator_contains, operator_not_contains, operator_is_gt, + operator_is_ge_equal, + operator_is_ge_greater, operator_is_lt, + operator_is_le_equal, + operator_is_le_lower, operator_is_in, operator_not_in, operator_in_range, diff --git a/tests/test_sw_upgrade_device_state.py b/tests/test_sw_upgrade_device_state.py index 0c4ec1b..8dfde0c 100644 --- a/tests/test_sw_upgrade_device_state.py +++ b/tests/test_sw_upgrade_device_state.py @@ -6,25 +6,23 @@ @pytest.mark.parametrize( - "platform, command, jpath, expected_parameter, check_should_pass", + "platform, command, expected_parameter, check_should_pass, fail_details", [ - ("arista_eos", "show_version", "[*].[$image$,image]", {"image": "4.14.7M"}, True), - ("arista_eos", "show_version", "[*].[$image$,image]", {"image": "no-match"}, False), - ("cisco_ios", "show_version", "[*].[$version$,version]", {"version": "12.2(54)SG1"}, True), - ("cisco_ios", "show_version", "[*].[$version$,version]", {"version": "no-match"}, False), - ("cisco_nxos", "show_version", "[*].[$os$,os]", {"os": "6.1(2)I3(1)"}, True), - ("cisco_nxos", "show_version", "[*].[$os$,os]", {"os": "no-match"}, False), + ("arista_eos", "show_version", {"image": "4.14.7M"}, True, {}), + ("arista_eos", "show_version", {"image": "no-match"}, False, {0: {"image": "4.14.7M"}}), + ("cisco_ios", "show_version", {"version": "12.2(54)SG1"}, True, {}), + ("cisco_ios", "show_version", {"version": "no-match"}, False, {0: {"version": "12.2(54)SG1"}}), + ("cisco_nxos", "show_version", {"os": "6.1(2)I3(1)"}, True, {}), + ("cisco_nxos", "show_version", {"os": "no-match"}, False, {0: {"os": "6.1(2)I3(1)"}}), ], ) -def test_show_version(platform, command, jpath, expected_parameter, check_should_pass): +def test_show_version(platform, command, expected_parameter, check_should_pass, fail_details): """Test expected version with parameter_match.""" filename = f"{platform}_{command}.json" command = load_json_file("sw_upgrade", filename) - check = CheckType.create("parameter_match") - value = extract_data_from_json(command, jpath) - eval_results, passed = check.evaluate(expected_parameter, value, "match") # pylint: disable=E1121 - assert passed is check_should_pass, f"FAILED, eval_result: {eval_results}" + eval_results, passed = check.evaluate(expected_parameter, command, "match") # pylint: disable=E1121 + assert passed is check_should_pass and eval_results == fail_details, f"FAILED, eval_result: {eval_results}" @pytest.mark.parametrize( @@ -86,7 +84,6 @@ def test_show_ip_route_missing_and_additional_routes(platform, command): """Test missing or additional routes fail the test with exact_match.""" command_pre = command_post = load_json_file("sw_upgrade", f"{platform}_{command}.json") check = CheckType.create("exact_match") - print(len(command_pre)) eval_results_missing, passed_missing = check.evaluate(command_post[:30], command_pre) eval_results_additional, passed_additional = check.evaluate(command_post, command_pre[:30]) assert ( diff --git a/tests/test_type_checks.py b/tests/test_type_checks.py index b7112a5..e141a52 100644 --- a/tests/test_type_checks.py +++ b/tests/test_type_checks.py @@ -145,7 +145,7 @@ def test_check_type_results(check_type_str, evaluate_args, folder_name, path, ex "napalm_get_bgp_neighbors", "exact_match", {}, - "global.$peers$.*.[is_enabled,is_up]", + "global.peers.$*$.[is_enabled,is_up]", ( { "7.7.7.7": { @@ -160,14 +160,14 @@ def test_check_type_results(check_type_str, evaluate_args, folder_name, path, ex "napalm_get_bgp_neighbors", "tolerance", {"tolerance": 10}, - "global.$peers$.*.*.ipv4.[accepted_prefixes,received_prefixes,sent_prefixes]", + "global.peers.$*$.*.ipv4.[accepted_prefixes,received_prefixes,sent_prefixes]", ({"10.1.0.0": {"accepted_prefixes": {"new_value": 900, "old_value": 1000}}}, False), ) napalm_bgp_neighbor_prefixes_ipv6 = ( "napalm_get_bgp_neighbors", "tolerance", {"tolerance": 10}, - "global.$peers$.*.*.ipv6.[accepted_prefixes,received_prefixes,sent_prefixes]", + "global.peers.$*$.*.ipv6.[accepted_prefixes,received_prefixes,sent_prefixes]", ({"10.64.207.255": {"received_prefixes": {"new_value": 1100, "old_value": 1000}}}, False), ) napalm_get_lldp_neighbors_exact_raw = ( @@ -284,10 +284,18 @@ def test_checks(folder_name, check_type_str, evaluate_args, path, expected_resul False, ), ) +parameter_match_napalm_facts = ( + "napalm_facts.json", + "parameter_match", + {"mode": "match", "params": {"os_version": "12.1X47-D20.7"}}, + "*", + ({}, True), +) @pytest.mark.parametrize( - "filename, check_type_str, evaluate_args, path, expected_result", [parameter_match_api, parameter_no_match_api] + "filename, check_type_str, evaluate_args, path, expected_result", + [parameter_match_api, parameter_no_match_api, parameter_match_napalm_facts], ) def test_param_match(filename, check_type_str, evaluate_args, path, expected_result): """Validate parameter_match check type.""" diff --git a/tests/test_validates.py b/tests/test_validates.py index 5d049ca..c10a8ff 100644 --- a/tests/test_validates.py +++ b/tests/test_validates.py @@ -65,7 +65,7 @@ operator_params_wrong_operator = ( "operator", {"params": {"mode": "random", "operator_data": [20, 40, 60]}}, - "'params' value must be one of the following: ['is-in', 'not-in', 'in-range', 'not-in-range', 'all-same', 'is-gt', 'is-lt', 'contains', 'not-contains']. You have: random", + "'params' value must be one of the following: ['is-in', 'not-in', 'in-range', 'not-in-range', 'all-same', 'is-gt', 'is-lt', 'is-ge', 'is-le', 'contains', 'not-contains']. You have: random", ) operator_params_in = ( "operator", @@ -85,7 +85,7 @@ operator_params_number = ( "operator", {"params": {"mode": "is-gt", "operator_data": "1"}}, - "check options ('is-gt', 'is-lt') must have value of type float or int. You have: 1 of type ", + "check options ('is-gt', 'is-lt', 'is-ge', 'is-le') must have value of type float or int. You have: 1 of type ", ) operator_params_contains = ( "operator",