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