From f93ce38e280c09a8ec84f36ee98e246b8ac88aa8 Mon Sep 17 00:00:00 2001 From: Jack De Winter Date: Sun, 23 Apr 2023 08:09:22 -0700 Subject: [PATCH 1/4] https://github.com/jackdewinter/application_properties/issues/3 - Adding support for config files (ini) --- Pipfile | 4 +- Pipfile.lock | 76 +- .../application_properties.py | 18 +- .../application_properties_config_loader.py | 213 +++++ .../application_properties_json_loader.py | 39 +- .../application_properties_loader_helper.py | 34 + .../application_properties_toml_loader.py | 51 +- changelog.md | 16 +- install-requirements.txt | 1 + publish/coverage.json | 8 +- publish/test-results.json | 12 +- setup.cfg | 2 +- ...st_application_properties_config_loader.py | 743 ++++++++++++++++++ ...test_application_properties_json_loader.py | 135 +++- ...test_application_properties_toml_loader.py | 326 +++++--- test/test_helpers.py | 52 ++ 16 files changed, 1481 insertions(+), 249 deletions(-) create mode 100644 application_properties/application_properties_config_loader.py create mode 100644 application_properties/application_properties_loader_helper.py create mode 100644 test/test_application_properties_config_loader.py create mode 100644 test/test_helpers.py diff --git a/Pipfile b/Pipfile index 630d843..026bd0d 100644 --- a/Pipfile +++ b/Pipfile @@ -8,7 +8,6 @@ black = "==23.1.0" isort = "==5.12.0" flake8 = "==6.0.0" flake8-bugbear = "==23.3.23" -flake8-bandit = "==4.1.1" pre-commit = "==3.2.2" project-summarizer = "==0.5.0" pylint = "==2.17.2" @@ -27,7 +26,8 @@ mypy = "==1.1.1" colorama = "==0.4.5" sourcery = "==1.1.0" coverage = "==7.2.1" -bandit = "*" +bandit = "==1.7.5" +tomli = "==2.0.1" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 976bc37..642ec30 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3d4bdd90c34b9678ec34742f9287a02414c6d8656ef0ff4a204fc3c36ec8b78b" + "sha256": "6706beeeaf219d45fece6701d94fd9e5add2f16ffd61dcc256f55f75bf602705" }, "pipfile-spec": 6, "requires": { @@ -18,19 +18,19 @@ "default": { "astroid": { "hashes": [ - "sha256:6e61b85c891ec53b07471aec5878f4ac6446a41e590ede0f2ce095f39f7d49dd", - "sha256:dea89d9f99f491c66ac9c04ebddf91e4acf8bd711722175fe6245c0725cc19bb" + "sha256:44224ad27c54d770233751315fa7f74c46fa3ee0fab7beef1065f99f09897efe", + "sha256:f11e74658da0f2a14a8d19776a8647900870a63de71db83713a8e77a6af52662" ], "markers": "python_full_version >= '3.7.2'", - "version": "==2.15.2" + "version": "==2.15.3" }, "attrs": { "hashes": [ - "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", - "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" + "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", + "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" ], - "markers": "python_version >= '3.6'", - "version": "==22.2.0" + "markers": "python_version >= '3.7'", + "version": "==23.1.0" }, "bandit": { "hashes": [ @@ -316,11 +316,11 @@ }, "filelock": { "hashes": [ - "sha256:3618c0da67adcc0506b015fd11ef7faf1b493f0b40d87728e19986b536890c37", - "sha256:f08a52314748335c6460fc8fe40cd5638b85001225db78c2aa01c8c0db83b318" + "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9", + "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718" ], "markers": "python_version >= '3.7'", - "version": "==3.11.0" + "version": "==3.12.0" }, "flake8": { "hashes": [ @@ -330,14 +330,6 @@ "index": "pypi", "version": "==6.0.0" }, - "flake8-bandit": { - "hashes": [ - "sha256:068e09287189cbfd7f986e92605adea2067630b75380c6b5733dab7d87f9a84e", - "sha256:4c8a53eb48f23d4ef1e59293657181a3c989d0077c9952717e98a0eace43e06d" - ], - "index": "pypi", - "version": "==4.1.1" - }, "flake8-bugbear": { "hashes": [ "sha256:8a218d13abd6904800970381057ce8e72cde8eea743240c4ef3ede4dd0bc9cfb", @@ -380,11 +372,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:5a66966b39ff1c14ef5b2d60c1d842b0141fefff0f4cc6365b4bc9446c652807", - "sha256:f65e478a7c2177bd19517a3a15dac094d253446d8690c5f3e71e735a04312374" + "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", + "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" ], "markers": "python_version < '3.12'", - "version": "==6.2.1" + "version": "==6.6.0" }, "importlib-resources": { "hashes": [ @@ -550,11 +542,11 @@ }, "packaging": { "hashes": [ - "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2", - "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97" + "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" ], "markers": "python_version >= '3.7'", - "version": "==23.0" + "version": "==23.1" }, "pathspec": { "hashes": [ @@ -638,11 +630,11 @@ }, "pygments": { "hashes": [ - "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297", - "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717" + "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c", + "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1" ], - "markers": "python_version >= '3.6'", - "version": "==2.14.0" + "markers": "python_version >= '3.7'", + "version": "==2.15.1" }, "pylint": { "hashes": [ @@ -713,7 +705,7 @@ "sha256:acb739f89fabb3d798c099e9e0c035003062367a441910aaaf2281bc1972ee14", "sha256:fcc653f65fe3035b478820b5284fbf0f52803622ee3f60a2faed7a7d3ba1f41e" ], - "markers": "python_version >= '3.7' and python_version < '4'", + "markers": "python_version >= '3.7' and python_version < '4.0'", "version": "==2.0.4" }, "pytest-timeout": { @@ -799,7 +791,7 @@ "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" ], - "markers": "python_version >= '3.7' and python_version < '4'", + "markers": "python_version >= '3.7' and python_version < '4.0'", "version": "==2.28.2" }, "requests-toolbelt": { @@ -820,11 +812,11 @@ }, "rich": { "hashes": [ - "sha256:540c7d6d26a1178e8e8b37e9ba44573a3cd1464ff6348b99ee7061b95d1c6333", - "sha256:dc84400a9d842b3a9c5ff74addd8eb798d155f36c1c91303888e0a66850d2a15" + "sha256:22b74cae0278fd5086ff44144d3813be1cedc9115bdfabbfefd86400cb88b20a", + "sha256:b5d573e13605423ec80bdd0cd5f8541f7844a0e71a13f74cf454ccb2f490708b" ], "markers": "python_version >= '3.7'", - "version": "==13.3.3" + "version": "==13.3.4" }, "six": { "hashes": [ @@ -856,7 +848,7 @@ "sha256:2c428d2338976279e8eb2196f7a94910960d9f7ba2f41f3988511e95ca447021", "sha256:bd5a71ff5e5e5f5ea983880e4a1dd1bb47f8feebbb3d95b592398e2f02194771" ], - "markers": "python_version >= '3.8'", + "markers": "python_full_version >= '3.8.0'", "version": "==5.0.0" }, "tomli": { @@ -864,7 +856,7 @@ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], - "markers": "python_version >= '3.7'", + "index": "pypi", "version": "==2.0.1" }, "tomlkit": { @@ -885,10 +877,10 @@ }, "trove-classifiers": { "hashes": [ - "sha256:06fd10c95d285e7ddebd59e6a4ba299f03d7417d38d369248a4a40c9754a68fa", - "sha256:ee42f2f8c1d4bcfe35f746e472f07633570d485fab45407effc0379270a3bb03" + "sha256:65e8cb3929e3788141db90a0776b76fcafbeb548a4fbe6aee4bfd9656e899939", + "sha256:f8823bc4b002fb60ad102f1a97fbb0086efe149123f5701bcbae93d330321cca" ], - "version": "==2023.3.9" + "version": "==2023.4.22" }, "twine": { "hashes": [ @@ -916,11 +908,11 @@ }, "virtualenv": { "hashes": [ - "sha256:31712f8f2a17bd06234fa97fdf19609e789dd4e3e4bf108c3da71d710651adbc", - "sha256:f50e3e60f990a0757c9b68333c9fdaa72d7188caa417f96af9e52407831a3b68" + "sha256:278753c47aaef1a0f14e6db8a4c5e1e040e90aea654d0fc1dc7e0d8a42616cc3", + "sha256:48fd3b907b5149c5aab7c23d9790bea4cac6bc6b150af8635febc4cfeab1275a" ], "markers": "python_version >= '3.7'", - "version": "==20.21.0" + "version": "==20.22.0" }, "wcwidth": { "hashes": [ diff --git a/application_properties/application_properties.py b/application_properties/application_properties.py index f9fedd4..7d8755a 100644 --- a/application_properties/application_properties.py +++ b/application_properties/application_properties.py @@ -87,6 +87,12 @@ def enable_convert_untyped_if_possible(self) -> None: """ self.__convert_untyped_if_possible = True + def clear(self) -> None: + """ + Clear the configuration map. + """ + self.__flat_property_map.clear() + def load_from_dict( self, config_map: Dict[Any, Any], clear_map: bool = True ) -> None: @@ -99,7 +105,7 @@ def load_from_dict( LOGGER.debug("Loading from dictionary: {%s}", str(config_map)) if clear_map: - self.__flat_property_map.clear() + self.clear() self.__scan_map(config_map, "") @staticmethod @@ -125,16 +131,20 @@ def verify_full_part_form(property_key: str) -> str: return property_key @staticmethod - def verify_full_key_form(property_key: str) -> str: + def verify_full_key_form( + property_key: str, alternate_name: Optional[str] = None + ) -> str: """ Given a full key, verify that it is composed properly. """ + key_name = alternate_name or "Full property key" + if property_key.startswith( ApplicationProperties.__separator ) or property_key.endswith(ApplicationProperties.__separator): raise ValueError( - f"Full property key must not start or end with the '{ApplicationProperties.__separator}' character." + f"{key_name} must not start or end with the '{ApplicationProperties.__separator}' character." ) doubles = ( f"{ApplicationProperties.__separator}{ApplicationProperties.__separator}" @@ -142,7 +152,7 @@ def verify_full_key_form(property_key: str) -> str: doubles_index = property_key.find(doubles) if doubles_index != -1: raise ValueError( - "Full property key cannot contain multiples of " + f"{key_name} cannot contain multiples of " + f"the {ApplicationProperties.__separator} without any text between them." ) split_key = property_key.split(ApplicationProperties.__separator) diff --git a/application_properties/application_properties_config_loader.py b/application_properties/application_properties_config_loader.py new file mode 100644 index 0000000..bc89796 --- /dev/null +++ b/application_properties/application_properties_config_loader.py @@ -0,0 +1,213 @@ +""" +Module to provide for a manner to load an ApplicationProperties object from a ini-type config file. +""" +import configparser +import os +from typing import Callable, Optional, Set, Tuple + +from application_properties.application_properties import ApplicationProperties +from application_properties.application_properties_loader_helper import ( + ApplicationPropertiesLoaderHelper, +) + + +# pylint: disable=too-few-public-methods +class ApplicationPropertiesConfigLoader: + """ + Class to provide for a manner to load an ApplicationProperties object from a ini-type config file. + """ + + # pylint: disable=too-many-arguments + @staticmethod + def load_and_set( + properties_object: ApplicationProperties, + configuration_file: str, + section_header: Optional[str] = None, + handle_error_fn: Optional[Callable[[str, Optional[Exception]], None]] = None, + clear_property_map: bool = True, + check_for_file_presence: bool = True, + ) -> Tuple[bool, bool]: + """ + Load the specified file and set it into the given properties object. + """ + handle_error_fn = ( + ApplicationPropertiesLoaderHelper.set_error_handler_if_not_set( + handle_error_fn + ) + ) + ( + did_succeed, + did_have_one_error, + ) = ApplicationPropertiesConfigLoader.__check_for_file( + configuration_file, check_for_file_presence, handle_error_fn + ) + if not did_succeed: + return did_succeed, did_have_one_error + + if clear_property_map: + properties_object.clear() + + config_parser = ApplicationPropertiesConfigLoader.__read_configuration( + configuration_file, handle_error_fn + ) + if not config_parser: + return False, True + + did_apply_one = False + set_property_names: Set[str] = set() + for next_section_name in config_parser.sections(): + ( + did_apply_one, + did_have_one_error, + ) = ApplicationPropertiesConfigLoader.__next_section( + properties_object, + next_section_name, + configuration_file, + handle_error_fn, + section_header, + config_parser, + set_property_names, + did_have_one_error, + did_apply_one, + ) + return did_apply_one and not did_have_one_error, did_have_one_error + + # pylint: enable=too-many-arguments + + # pylint: disable=too-many-arguments + @staticmethod + def __next_section( + properties_object: ApplicationProperties, + next_section_name: str, + configuration_file: str, + handle_error_fn: Callable[[str, Optional[Exception]], None], + section_header: Optional[str], + config_parser: configparser.ConfigParser, + set_property_names: Set[str], + did_have_one_error: bool, + did_apply_one: bool, + ) -> Tuple[bool, bool]: + if not ApplicationPropertiesConfigLoader.__verify_section_name( + properties_object, next_section_name, configuration_file, handle_error_fn + ): + did_have_one_error = True + elif not (section_header and next_section_name != section_header): + for item_pair in config_parser.items(next_section_name): + if ApplicationPropertiesConfigLoader.__set_item( + properties_object, + set_property_names, + item_pair, + configuration_file, + handle_error_fn, + section_header, + next_section_name, + ): + did_apply_one = True + else: + did_have_one_error = True + break + return did_apply_one, did_have_one_error + + # pylint: enable=too-many-arguments + + # pylint: disable=too-many-arguments + @staticmethod + def __set_item( + properties_object: ApplicationProperties, + set_property_names: Set[str], + item_pair: Tuple[str, str], + configuration_file: str, + handle_error_fn: Callable[[str, Optional[Exception]], None], + section_header: Optional[str], + next_section_name: str, + ) -> bool: + item_name = item_pair[0] + item_value = item_pair[1] + try: + properties_object.verify_full_key_form(item_name, "Configuration item name") + except ValueError as this_exception: + formatted_error = ( + f"Configuration item name '{item_name}' in file '{configuration_file}' " + + f"is not a valid section name: {str(this_exception)}" + ) + handle_error_fn(formatted_error, this_exception) + return False + + full_property_name = ( + f"{item_name}" if section_header else f"{next_section_name}.{item_name}" + ) + if not item_value.strip(): + formatted_error = f"Full configuration item name '{full_property_name}' in file '{configuration_file}' does not have a value assigned to it." + handle_error_fn(formatted_error, None) + return False + + if full_property_name in set_property_names: + formatted_error = f"Full configuration item name '{full_property_name}' in file '{configuration_file}' occurs multiple times using different formats." + handle_error_fn(formatted_error, None) + return False + + full_property = f"{full_property_name}={item_value}" + properties_object.set_manual_property(full_property) + set_property_names.add(full_property_name) + return True + + # pylint: enable=too-many-arguments + + @staticmethod + def __read_configuration( + configuration_file: str, + handle_error_fn: Callable[[str, Optional[Exception]], None], + ) -> Optional[configparser.ConfigParser]: + config_parser = configparser.ConfigParser(allow_no_value=False) + try: + config_parser.read(configuration_file) + return config_parser + except configparser.Error as this_exception: + formatted_error = ( + f"Specified configuration file '{configuration_file}' " + + f"is not a valid config file: {str(this_exception)}." + ) + handle_error_fn(formatted_error, this_exception) + return None + + @staticmethod + def __check_for_file( + configuration_file: str, + check_for_file_presence: bool, + handle_error_fn: Callable[[str, Optional[Exception]], None], + ) -> Tuple[bool, bool]: + if not os.path.exists(configuration_file) or not os.path.isfile( + configuration_file + ): + if check_for_file_presence: + return False, False + + formatted_error = ( + f"Specified configuration file '{configuration_file}' does not exist." + ) + handle_error_fn(formatted_error, None) + return False, True + return True, False + + @staticmethod + def __verify_section_name( + properties_object: ApplicationProperties, + next_section_name: str, + configuration_file: str, + handle_error_fn: Callable[[str, Optional[Exception]], None], + ) -> bool: + try: + properties_object.verify_full_key_form( + next_section_name, "Configuration section name" + ) + except ValueError as this_exception: + formatted_error = ( + f"Configuration section name '{next_section_name}' in file '{configuration_file}' " + + f"is not a valid section name: {str(this_exception)}" + ) + handle_error_fn(formatted_error, this_exception) + return False + return True + + +# pylint: enable=too-few-public-methods diff --git a/application_properties/application_properties_json_loader.py b/application_properties/application_properties_json_loader.py index 8ce3097..f8780a8 100644 --- a/application_properties/application_properties_json_loader.py +++ b/application_properties/application_properties_json_loader.py @@ -2,9 +2,13 @@ Module to provide for a manner to load an ApplicationProperties object from a JSON file. """ import json -from typing import Any, Callable, Dict, Optional +import os +from typing import Any, Callable, Dict, Optional, Tuple from application_properties.application_properties import ApplicationProperties +from application_properties.application_properties_loader_helper import ( + ApplicationPropertiesLoaderHelper, +) # pylint: disable=too-few-public-methods @@ -17,24 +21,26 @@ class ApplicationPropertiesJsonLoader: def load_and_set( properties_object: ApplicationProperties, configuration_file: str, - handle_error_fn: Optional[Callable[[str, Exception], None]] = None, + handle_error_fn: Optional[Callable[[str, Optional[Exception]], None]] = None, clear_property_map: bool = True, - ) -> bool: + check_for_file_presence: bool = True, + ) -> Tuple[bool, bool]: """ Load the specified file and set it into the given properties object. """ - - if not handle_error_fn: - - def print_error_to_stdout( - formatted_error: str, thrown_exception: Exception - ) -> None: - _ = thrown_exception - print(formatted_error) - - handle_error_fn = print_error_to_stdout + if check_for_file_presence and ( + not os.path.exists(configuration_file) + or not os.path.isfile(configuration_file) + ): + return False, False configuration_map: Dict[Any, Any] = {} + handle_error_fn = ( + ApplicationPropertiesLoaderHelper.set_error_handler_if_not_set( + handle_error_fn + ) + ) + did_have_one_error = False try: with open(configuration_file, encoding="utf-8") as infile: configuration_map = json.load(infile) @@ -43,16 +49,18 @@ def print_error_to_stdout( f"Specified configuration file '{configuration_file}' " + f"is not a valid JSON file: {str(this_exception)}." ) + did_have_one_error = True handle_error_fn(formatted_error, this_exception) except IOError as this_exception: formatted_error = ( f"Specified configuration file '{configuration_file}' " + f"was not loaded: {str(this_exception)}." ) + did_have_one_error = True handle_error_fn(formatted_error, this_exception) did_apply_map = False - if configuration_map: + if not did_have_one_error and configuration_map: try: properties_object.load_from_dict( configuration_map, clear_map=clear_property_map @@ -63,8 +71,9 @@ def print_error_to_stdout( f"Specified configuration file '{configuration_file}' " + f"is not valid: {str(this_exception)}" ) + did_have_one_error = True handle_error_fn(formatted_error, this_exception) - return did_apply_map + return did_apply_map and not did_have_one_error, did_have_one_error # pylint: enable=too-few-public-methods diff --git a/application_properties/application_properties_loader_helper.py b/application_properties/application_properties_loader_helper.py new file mode 100644 index 0000000..892894a --- /dev/null +++ b/application_properties/application_properties_loader_helper.py @@ -0,0 +1,34 @@ +""" +Module to provide helpers for the loaders to share. +""" + + +# pylint: disable=too-few-public-methods +from typing import Callable, Optional + + +class ApplicationPropertiesLoaderHelper: + """ + Class to provide helpers for the loaders to share. + """ + + @staticmethod + def set_error_handler_if_not_set( + handle_error_fn: Optional[Callable[[str, Optional[Exception]], None]] + ) -> Callable[[str, Optional[Exception]], None]: + """ + If the error_handler is not set, make sure to set it to print to output. + """ + if not handle_error_fn: + + def print_error_to_stdout( + formatted_error: str, thrown_exception: Optional[Exception] + ) -> None: + _ = thrown_exception + print(formatted_error) + + handle_error_fn = print_error_to_stdout + return handle_error_fn + + +# pylint: enable=too-few-public-methods diff --git a/application_properties/application_properties_toml_loader.py b/application_properties/application_properties_toml_loader.py index efb6e65..d3563ed 100644 --- a/application_properties/application_properties_toml_loader.py +++ b/application_properties/application_properties_toml_loader.py @@ -2,11 +2,14 @@ Module to provide for a manner to load an ApplicationProperties object from a TOML file. """ import os -from typing import Any, Callable, Dict, Optional +from typing import Any, Callable, Dict, Optional, Tuple import tomli from application_properties.application_properties import ApplicationProperties +from application_properties.application_properties_loader_helper import ( + ApplicationPropertiesLoaderHelper, +) # pylint: disable=too-few-public-methods @@ -21,28 +24,25 @@ def load_and_set( properties_object: ApplicationProperties, configuration_file: str, section_header: Optional[str] = None, - handle_error_fn: Optional[Callable[[str, Exception], None]] = None, + handle_error_fn: Optional[Callable[[str, Optional[Exception]], None]] = None, clear_property_map: bool = True, check_for_file_presence: bool = True, - ) -> bool: + ) -> Tuple[bool, bool]: """ Load the specified file and set it into the given properties object. """ if check_for_file_presence and not ( os.path.exists(configuration_file) and os.path.isfile(configuration_file) ): - return False + return False, False - if not handle_error_fn: - - def print_error_to_stdout( - formatted_error: str, thrown_exception: Exception - ) -> None: - _ = thrown_exception - print(formatted_error) - - handle_error_fn = print_error_to_stdout + handle_error_fn = ( + ApplicationPropertiesLoaderHelper.set_error_handler_if_not_set( + handle_error_fn + ) + ) + did_have_one_error = False configuration_map: Optional[Dict[str, Any]] = {} try: with open(configuration_file, "rb") as infile: @@ -52,25 +52,30 @@ def print_error_to_stdout( f"Specified configuration file '{configuration_file}' " + f"is not a valid TOML file: {str(this_exception)}." ) + did_have_one_error = True handle_error_fn(formatted_error, this_exception) except IOError as this_exception: formatted_error = ( f"Specified configuration file '{configuration_file}' " + f"was not loaded: {str(this_exception)}." ) + did_have_one_error = True handle_error_fn(formatted_error, this_exception) did_apply_map = False - if configuration_map and section_header: - configuration_map = ApplicationPropertiesTomlLoader.__apply_section_header( - configuration_map, section_header - ) - if configuration_map: - properties_object.load_from_dict( - configuration_map, clear_map=clear_property_map - ) - did_apply_map = True - return did_apply_map + if not did_have_one_error: + if configuration_map and section_header: + configuration_map = ( + ApplicationPropertiesTomlLoader.__apply_section_header( + configuration_map, section_header + ) + ) + if configuration_map: + properties_object.load_from_dict( + configuration_map, clear_map=clear_property_map + ) + did_apply_map = True + return did_apply_map and not did_have_one_error, did_have_one_error # pylint: enable=too-many-arguments diff --git a/changelog.md b/changelog.md index c402ad3..18ed044 100644 --- a/changelog.md +++ b/changelog.md @@ -2,9 +2,21 @@ ## Unversioned - In Main, Not Released -### Fixed and Added +## Version 0.7.0 - Date: 2023-04-22 + +- [Issue 137](https://github.com/jackdewinter/application_properties/issues/137) + - to support multiple configurations, added clear_map flag to Json loader + - refactoring to clean things up for upcoming changes +- [Issue 2](https://github.com/jackdewinter/application_properties/issues/2) + - added ability to translate from string to other forms + - controlled by flag, and only occurs on pure strings, not any strings with any typing + - i.e. only manual set and "config" files are untyped, so only those are affected +- [Issue 145](https://github.com/jackdewinter/application_properties/issues/145) + - added TOML configuration file support as a typed configuration file + +## Version 0.6.0 - Date: xxxx-xx-xx -- None +- quick release to help PyMarkdown project ## Version 0.5.2 - Date: 2022-04-01 diff --git a/install-requirements.txt b/install-requirements.txt index e69de29..1d0cc14 100644 --- a/install-requirements.txt +++ b/install-requirements.txt @@ -0,0 +1 @@ +tomli==2.0.1 diff --git a/publish/coverage.json b/publish/coverage.json index 9712c10..20f8d47 100644 --- a/publish/coverage.json +++ b/publish/coverage.json @@ -2,12 +2,12 @@ "projectName": "application_properties", "reportSource": "pytest", "branchLevel": { - "totalMeasured": 98, - "totalCovered": 98 + "totalMeasured": 124, + "totalCovered": 124 }, "lineLevel": { - "totalMeasured": 272, - "totalCovered": 272 + "totalMeasured": 374, + "totalCovered": 374 } } diff --git a/publish/test-results.json b/publish/test-results.json index a212db9..da4e0f1 100644 --- a/publish/test-results.json +++ b/publish/test-results.json @@ -10,6 +10,14 @@ "skippedTests": 0, "elapsedTimeInMilliseconds": 0 }, + { + "name": "test.test_application_properties_config_loader", + "totalTests": 17, + "failedTests": 0, + "errorTests": 0, + "skippedTests": 0, + "elapsedTimeInMilliseconds": 0 + }, { "name": "test.test_application_properties_facade", "totalTests": 12, @@ -20,7 +28,7 @@ }, { "name": "test.test_application_properties_json_loader", - "totalTests": 8, + "totalTests": 9, "failedTests": 0, "errorTests": 0, "skippedTests": 0, @@ -28,7 +36,7 @@ }, { "name": "test.test_application_properties_toml_loader", - "totalTests": 15, + "totalTests": 17, "failedTests": 0, "errorTests": 0, "skippedTests": 0, diff --git a/setup.cfg b/setup.cfg index ab14897..96422b4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,7 +13,7 @@ select = B,C,E,F,W,T4 ## Run with: pylint --rcfile=setup.cfg somepackage [pylint] -disable = C0301, W0511, duplicate-code +disable = C0301 extension-pkg-whitelist=win32api #ignore = migrations #ignore-docstrings = yes diff --git a/test/test_application_properties_config_loader.py b/test/test_application_properties_config_loader.py new file mode 100644 index 0000000..88e618b --- /dev/null +++ b/test/test_application_properties_config_loader.py @@ -0,0 +1,743 @@ +""" +Tests for the ApplicationProperties class +""" +import io +import os +import sys +from test.test_helpers import ErrorResults, TestHelpers + +from application_properties import ApplicationProperties +from application_properties.application_properties_config_loader import ( + ApplicationPropertiesConfigLoader, +) + + +def test_config_loader_config_not_present_check_enabled(): + """ + Test to make sure that we do not try and load a configuration file that is not present. + """ + + # Arrange + configuration_file = "does-not-exist" + configuration_file = os.path.abspath(configuration_file) + assert not os.path.exists(configuration_file) + application_properties = ApplicationProperties() + + expected_did_apply = False + expected_did_error = False + expected_value = -1 + + # Act + actual_did_apply, actual_did_error = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, configuration_file, None, None, True, True + ) + actual_value = application_properties.get_integer_property( + "plugins.md999.test_value", -1 + ) + + # Assert + assert expected_value == actual_value + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + + +def test_config_loader_config_not_present_check_disabled(): + """ + Test to make sure that we do not try and load a configuration file that is not present. + """ + + # Arrange + configuration_file = "does-not-exist" + configuration_file = os.path.abspath(configuration_file) + assert not os.path.exists(configuration_file) + application_properties = ApplicationProperties() + + expected_did_apply = False + expected_did_error = True + expected_value = -1 + + # Act + actual_did_apply, actual_did_error = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, configuration_file, None, None, True, False + ) + actual_value = application_properties.get_integer_property( + "plugins.md999.test_value", -1 + ) + + # Assert + assert expected_value == actual_value + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + + +def test_config_loader_valid_config(): + """ + Test to make sure that we can load a valid config file. + """ + + # Arrange + supplied_configuration = """[plugins] +md999.test_value = 2 +""" + expected_value = "2" + expected_did_apply = True + expected_did_error = False + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + with open(configuration_file, "r", encoding="utf-8") as file: + settings = file.read() + print(settings) + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, configuration_file, None, None, True, True + ) + actual_value = application_properties.get_string_property( + "plugins.md999.test_value", "bob" + ) + + # Assert + assert expected_value == actual_value + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_valid_config_but_wrong_get_property_type(): + """ + Test to make sure that we can load a valid config file, even if the property + we are looking for is of the wrong type. The load should succeed, even + if the get fails. + """ + + # Arrange + supplied_configuration = """[plugins] +md999.test_value = 2 +""" + expected_error = ( + "The value for property 'plugins.md999.test_value' must be of type 'int'." + ) + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + captured_exception = None + ApplicationPropertiesConfigLoader.load_and_set( + application_properties, configuration_file + ) + try: + application_properties.get_integer_property( + "plugins.md999.test_value", -1, None, strict_mode=True + ) + except ValueError as this_exception: + captured_exception = this_exception + + # Assert + assert not application_properties.convert_untyped_if_possible + assert captured_exception is not None + assert str(captured_exception) == expected_error + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_not_present_with_check(): + """ + Test to make sure that we cannot load a config file that is not there, + and explicitly have something in place to check for that. + """ + + # Arrange + supplied_configuration = """[plugins] +md999.test_value = 2 +""" + expected_did_apply = False + expected_did_error = False + + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + os.remove(configuration_file) + application_properties = ApplicationProperties() + + # Act + actual_did_apply, actual_did_error = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, configuration_file, None, None, True, True + ) + + # Assert + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + + +def test_config_loader_config_file_not_present_without_check(): + """ + Test to make sure that we cannot load a config file that is not there, + and explicitly do not have something in place to check for that. + """ + + # Arrange + supplied_configuration = """[plugins] +md999.test_value = 2 +""" + expected_did_apply = False + expected_did_error = True + + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + os.remove(configuration_file) + application_properties = ApplicationProperties() + + # Act + old_stdout = sys.stdout + old_stderr = sys.stderr + std_output = io.StringIO() + std_error = io.StringIO() + try: + sys.stdout = std_output + sys.stderr = std_error + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, configuration_file, None, None, True, False + ) + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr + + # Assert + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert std_output is not None + assert ( + std_output.getvalue() + == f"Specified configuration file '{configuration_file}' does not exist.\n" + ) + assert std_error is not None + assert std_error.getvalue() == "" + + +def test_config_loader_config_file_not_present_without_check_and_error_function(): + """ + Test to make sure that we cannot load a config file that is not there, + and explicitly do not have something in place to check for that, but have + all errors getting captured. + """ + + # Arrange + supplied_configuration = """[plugins] +md999.test_value = 2 +""" + results = ErrorResults() + expected_did_apply = False + expected_did_error = True + + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + os.remove(configuration_file) + application_properties = ApplicationProperties() + + # Act + actual_did_apply, actual_did_error = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, + configuration_file, + None, + results.keep_error, + True, + False, + ) + + # Assert + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert results.reported_error is not None + assert ( + results.reported_error + == f"Specified configuration file '{configuration_file}' does not exist." + ) + + +def test_config_loader_config_file_not_valid(): + """ + Test to make sure that we error loading an invalid config file. + """ + + # Arrange + supplied_configuration = """[plugins] +md999.test_value +""" + expected_did_apply = False + expected_did_error = True + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + old_stdout = sys.stdout + std_output = io.StringIO() + try: + sys.stdout = std_output + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, configuration_file, None, None, True, False + ) + finally: + sys.stdout = old_stdout + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert std_output is not None + assert std_output.getvalue() is not None + configuration_file2 = configuration_file.replace("\\", "\\\\") + assert std_output.getvalue() == ( + f"Specified configuration file '{configuration_file}' is not a valid config file: Source contains parsing errors: '{configuration_file2}'\n\t[line 2]: 'md999.test_value\\n'.\n" + ) + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_valid_with_no_section_header(): + """ + Test to make sure that not having a section header implies that everything in the + file is part of the configuration. + """ + + # Arrange + supplied_configuration = """[plugins] +tools.bar = fred +""" + expected_did_apply = True + expected_did_error = False + expected_value = "fred" + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, configuration_file, None, None, False, False + ) + actual_value = application_properties.get_string_property( + "plugins.tools.bar", None, None + ) + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert expected_value == actual_value + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_valid_with_one_word_section_header(): + """ + Test to make sure that having a having a one word section header that is + present in the config file allows for anything under that entry to be + processed as configuration. + """ + + # Arrange + section_header = "plugins" + supplied_configuration = """[plugins] +tools.bar = fred + +[other-plugins] +tools.foo = fred +""" + expected_did_apply = True + expected_did_error = False + expected_value = "fred" + other_expected_value = None + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, + configuration_file, + section_header, + None, + True, + False, + ) + actual_value = application_properties.get_string_property( + "tools.bar", None, None + ) + other_actual_value = application_properties.get_string_property( + "tools.foo", None, None + ) + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert expected_value == actual_value + assert other_expected_value == other_actual_value + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_valid_with_one_word_bad_section_header(): + """ + Test to make sure that having a one word section header that is not + present in the config file effectively does not contribute to the + configuration. + """ + + # Arrange + section_header = "not-plugins" + supplied_configuration = """[plugins] +tools.bar = fred +""" + expected_did_apply = False + expected_did_error = False + expected_value = None + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, + configuration_file, + section_header, + None, + True, + False, + ) + actual_value = application_properties.get_string_property( + "tools.bar", None, None + ) + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert expected_value == actual_value + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_valid_with_multi_word_valid_section_header(): + """ + Test to make sure that having a having a multi word section header that points to + an existing section is recognized. + """ + + # Arrange + section_header = "tools.mytool" + supplied_configuration = """[tools.mytool] +tools.bar = fred +""" + expected_did_apply = True + expected_did_error = False + expected_value = "fred" + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, + configuration_file, + section_header, + None, + True, + False, + ) + actual_value = application_properties.get_string_property( + "tools.bar", None, None + ) + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert expected_value == actual_value + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_bad_config_format_with_repeated_section(): + """ + Test to make sure that a repeated section, in different forms, causes errors. + """ + + # Arrange + section_header = "plugins.tools.bar" + supplied_configuration = """[plugins.tools] +bar = fred + +[plugins.tools] +bar = barney +""" + results = ErrorResults() + expected_did_apply = False + expected_did_error = True + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, + configuration_file, + section_header, + results.keep_error, + True, + False, + ) + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + configuration_file2 = configuration_file.replace("\\", "\\\\") + assert ( + results.reported_error + == f"Specified configuration file '{configuration_file}' is not a valid config file: While reading from '{configuration_file2}' [line 4]: section 'plugins.tools' already exists." + ) + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_bad_config_format_with_header_with_leading_period(): + """ + Test to make sure that a header that starts with a period is an error. + """ + + # Arrange + section_header = "plugins.tools.bar" + supplied_configuration = """[.plugins] +tools.bar = "fred" +""" + results = ErrorResults() + expected_did_apply = False + expected_did_error = True + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, + configuration_file, + section_header, + results.keep_error, + True, + False, + ) + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert ( + results.reported_error + == f"Configuration section name '.plugins' in file '{configuration_file}' is not a valid section name: Configuration section name must not start or end with the '.' character." + ) + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_bad_config_format_with_item_with_leading_period(): + """ + Test to make sure that an item that starts with a period is an error. + """ + + # Arrange + section_header = None + supplied_configuration = """[plugins] +.tools.bar = fred +""" + results = ErrorResults() + expected_did_apply = False + expected_did_error = True + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, + configuration_file, + section_header, + results.keep_error, + True, + False, + ) + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert ( + results.reported_error + == f"Configuration item name '.tools.bar' in file '{configuration_file}' is not a valid section name: Configuration item name must not start or end with the '.' character." + ) + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_bad_config_format_with_double_items_through_different_paths(): + """ + Test to make sure that an item name that is doubled, but through different paths, is caught. + """ + + # Arrange + section_header = None + supplied_configuration = """[plugins] +tools.bar = fred + +[plugins.tools] +bar = fred +""" + results = ErrorResults() + expected_did_apply = False + expected_did_error = True + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, + configuration_file, + section_header, + results.keep_error, + True, + False, + ) + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert ( + results.reported_error + == f"Full configuration item name 'plugins.tools.bar' in file '{configuration_file}' occurs multiple times using different formats." + ) + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) + + +def test_config_loader_config_file_bad_config_format_with_no_item_value(): + """ + Test to make sure that an item name that is followed by a separator, but not value, is handled. + """ + + # Arrange + section_header = None + supplied_configuration = """[plugins] +tools.bar =\a\a\a\a +""".replace( + "\a", " " + ) + results = ErrorResults() + expected_did_apply = False + expected_did_error = True + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesConfigLoader.load_and_set( + application_properties, + configuration_file, + section_header, + results.keep_error, + True, + False, + ) + + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert ( + results.reported_error + == f"Full configuration item name 'plugins.tools.bar' in file '{configuration_file}' does not have a value assigned to it." + ) + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) diff --git a/test/test_application_properties_json_loader.py b/test/test_application_properties_json_loader.py index f7ea9f3..a63bc55 100644 --- a/test/test_application_properties_json_loader.py +++ b/test/test_application_properties_json_loader.py @@ -5,7 +5,7 @@ import json import os import sys -import tempfile +from test.test_helpers import TestHelpers from application_properties import ( ApplicationProperties, @@ -13,21 +13,33 @@ ) -def write_temporary_configuration(supplied_configuration): +def test_json_loader_config_not_present(): """ - Write the configuration as a temporary file that is kept around. + Test to make sure that we do not try and load a configuration file that is not present. """ - try: - with tempfile.NamedTemporaryFile("wt", delete=False) as outfile: - if isinstance(supplied_configuration, str): - outfile.write(supplied_configuration) - else: - json.dump(supplied_configuration, outfile) - return outfile.name - except IOError as ex: - raise AssertionError( - f"Test configuration file was not written ({str(ex)})." - ) from ex + + # Arrange + configuration_file = "does-not-exist" + configuration_file = os.path.abspath(configuration_file) + assert not os.path.exists(configuration_file) + application_properties = ApplicationProperties() + + expected_did_apply = False + expected_did_error = False + expected_value = -1 + + # Act + actual_did_apply, actual_did_error = ApplicationPropertiesJsonLoader.load_and_set( + application_properties, configuration_file, None, True, True + ) + actual_value = application_properties.get_integer_property( + "plugins.md999.test_value", -1 + ) + + # Assert + assert expected_value == actual_value + assert expected_did_error == actual_did_error + assert expected_did_apply == actual_did_apply def test_json_loader_valid_json(): @@ -38,14 +50,21 @@ def test_json_loader_valid_json(): # Arrange supplied_configuration = {"plugins": {"md999": {"test_value": 2}}} expected_value = 2 + expected_did_apply = True + expected_did_error = False configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - ApplicationPropertiesJsonLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesJsonLoader.load_and_set( application_properties, configuration_file ) actual_value = application_properties.get_integer_property( @@ -54,6 +73,8 @@ def test_json_loader_valid_json(): # Assert assert expected_value == actual_value + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error finally: if configuration_file and os.path.exists(configuration_file): os.remove(configuration_file) @@ -71,15 +92,22 @@ def test_json_loader_valid_json_but_wrong_get_property_type(): expected_error = ( "The value for property 'plugins.md999.test_value' must be of type 'int'." ) + expected_did_apply = True + expected_did_error = False configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act captured_exception = None - ApplicationPropertiesJsonLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesJsonLoader.load_and_set( application_properties, configuration_file ) try: @@ -90,6 +118,8 @@ def test_json_loader_valid_json_but_wrong_get_property_type(): captured_exception = this_exception # Assert + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert not application_properties.convert_untyped_if_possible assert captured_exception is not None assert str(captured_exception) == expected_error @@ -110,15 +140,22 @@ def test_json_loader_valid_json_but_wrong_get_property_type_with_untyped_convers expected_error = ( "The value for property 'plugins.md999.test_value' must be of type 'int'." ) + expected_did_apply = True + expected_did_error = False configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties(convert_untyped_if_possible=True) # Act captured_exception = None - ApplicationPropertiesJsonLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesJsonLoader.load_and_set( application_properties, configuration_file ) try: @@ -129,6 +166,8 @@ def test_json_loader_valid_json_but_wrong_get_property_type_with_untyped_convers captured_exception = this_exception # Assert + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert application_properties.convert_untyped_if_possible assert captured_exception is not None assert str(captured_exception) == expected_error @@ -144,6 +183,8 @@ def test_json_loader_invalid_json(): # Arrange supplied_configuration = "this is not a json file" + expected_did_apply = False + expected_did_error = True handled_error_parameters = [] @@ -153,15 +194,22 @@ def inner_func(formatted_error, this_exception): configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - ApplicationPropertiesJsonLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesJsonLoader.load_and_set( application_properties, configuration_file, handle_error_fn=inner_func ) # Assert + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert handled_error_parameters assert handled_error_parameters[0].startswith("Specified configuration file ") assert ( @@ -180,6 +228,8 @@ def test_json_loader_missing_file(): """ # Arrange + expected_did_apply = False + expected_did_error = True handled_error_parameters = [] def inner_func(formatted_error, this_exception): @@ -191,11 +241,16 @@ def inner_func(formatted_error, this_exception): application_properties = ApplicationProperties() # Act - ApplicationPropertiesJsonLoader.load_and_set( - application_properties, configuration_file, handle_error_fn=inner_func + actual_did_apply, actual_did_error = ApplicationPropertiesJsonLoader.load_and_set( + application_properties, + configuration_file, + handle_error_fn=inner_func, + check_for_file_presence=False, ) # Assert + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert handled_error_parameters assert handled_error_parameters[0].startswith( "Specified configuration file 'missing_file_name.other' was not loaded: " @@ -210,6 +265,8 @@ def test_json_loader_valid_json_but_invalid_key(): # Arrange supplied_configuration = {"plugins": {"md999": {"test.value": 2}}} + expected_did_apply = False + expected_did_error = True handled_error_parameters = [] @@ -219,15 +276,22 @@ def inner_func(formatted_error, this_exception): configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - ApplicationPropertiesJsonLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesJsonLoader.load_and_set( application_properties, configuration_file, inner_func ) # Assert + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert handled_error_parameters assert handled_error_parameters[0].startswith("Specified configuration file '") assert ( @@ -240,17 +304,21 @@ def inner_func(formatted_error, this_exception): os.remove(configuration_file) -def test_json_loader_valid_json_but_invalid_key_xx(): +def test_json_loader_valid_json_but_invalid_key_with_stdin_capture(): """ Test to make sure that we can load a valid Json file, but fail when there is an invalid key. """ # Arrange supplied_configuration = {"plugins": {"md999": {"test.value": 2}}} + expected_did_apply = False + expected_did_error = True configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act @@ -262,7 +330,10 @@ def test_json_loader_valid_json_but_invalid_key_xx(): sys.stdout = new_stdout sys.stderr = new_stderr - ApplicationPropertiesJsonLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesJsonLoader.load_and_set( application_properties, configuration_file ) finally: @@ -270,6 +341,8 @@ def test_json_loader_valid_json_but_invalid_key_xx(): sys.stderr = saved_stderr # Assert + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert new_stdout.getvalue().startswith("Specified configuration file '") assert ( "' is not valid: Keys strings cannot contain the separator character '.'." @@ -300,10 +373,10 @@ def test_json_loader_pair_valid_json(): configuration_file_first = None try: - configuration_file_first = write_temporary_configuration( + configuration_file_first = TestHelpers.write_temporary_configuration( supplied_configuration_first ) - configuration_file_second = write_temporary_configuration( + configuration_file_second = TestHelpers.write_temporary_configuration( supplied_configuration_second ) application_properties = ApplicationProperties() diff --git a/test/test_application_properties_toml_loader.py b/test/test_application_properties_toml_loader.py index ccbdf9c..7f1f667 100644 --- a/test/test_application_properties_toml_loader.py +++ b/test/test_application_properties_toml_loader.py @@ -2,10 +2,9 @@ Tests for the ApplicationProperties class """ import io -import json import os import sys -import tempfile +from test.test_helpers import ErrorResults, TestHelpers from application_properties import ApplicationProperties from application_properties.application_properties_toml_loader import ( @@ -13,21 +12,33 @@ ) -def write_temporary_configuration(supplied_configuration): +def test_toml_loader_config_not_present(): """ - Write the configuration as a temporary file that is kept around. + Test to make sure that we do not try and load a configuration file that is not present. """ - try: - with tempfile.NamedTemporaryFile("wt", delete=False) as outfile: - if isinstance(supplied_configuration, str): - outfile.write(supplied_configuration) - else: - json.dump(supplied_configuration, outfile) - return outfile.name - except IOError as ex: - raise AssertionError( - f"Test configuration file was not written ({str(ex)})." - ) from ex + + # Arrange + configuration_file = "does-not-exist" + configuration_file = os.path.abspath(configuration_file) + assert not os.path.exists(configuration_file) + application_properties = ApplicationProperties() + + expected_did_apply = False + expected_did_error = False + expected_value = -1 + + # Act + actual_did_apply, actual_did_error = ApplicationPropertiesTomlLoader.load_and_set( + application_properties, configuration_file, None, None, True, True + ) + actual_value = application_properties.get_integer_property( + "plugins.md999.test_value", -1 + ) + + # Assert + assert expected_value == actual_value + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error def test_toml_loader_valid_toml(): @@ -41,14 +52,20 @@ def test_toml_loader_valid_toml(): """ expected_value = 2 expected_did_apply = True + expected_did_error = False configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, None, None, True, True ) actual_value = application_properties.get_integer_property( @@ -58,6 +75,7 @@ def test_toml_loader_valid_toml(): # Assert assert expected_value == actual_value assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error finally: if configuration_file and os.path.exists(configuration_file): os.remove(configuration_file) @@ -80,7 +98,9 @@ def test_toml_loader_valid_toml_but_wrong_get_property_type(): configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act @@ -121,7 +141,9 @@ def test_toml_loader_valid_toml_but_wrong_get_property_type_with_untyped_convers configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() application_properties.enable_convert_untyped_if_possible() @@ -157,18 +179,22 @@ def test_toml_loader_toml_file_not_present_with_check(): md999.test_value = 2 """ expected_did_apply = False + expected_did_error = False - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) os.remove(configuration_file) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + actual_did_apply, actual_did_error = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, None, None, True, True ) # Assert assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error def test_toml_loader_toml_file_not_present_without_check(): @@ -182,8 +208,11 @@ def test_toml_loader_toml_file_not_present_without_check(): md999.test_value = 2 """ expected_did_apply = False + expected_did_error = True - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) os.remove(configuration_file) application_properties = ApplicationProperties() @@ -192,7 +221,10 @@ def test_toml_loader_toml_file_not_present_without_check(): std_output = io.StringIO() try: sys.stdout = std_output - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, None, None, True, False ) finally: @@ -200,6 +232,7 @@ def test_toml_loader_toml_file_not_present_without_check(): # Assert assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert std_output is not None assert std_output.getvalue() is not None assert std_output.getvalue().startswith( @@ -214,48 +247,35 @@ def test_toml_loader_toml_file_not_present_without_check_and_error_function(): all errors getting captured. """ - # pylint: disable=too-few-public-methods - class ErrorResults: - """ - Class to collect the error results. - """ - - reported_error = None - - @staticmethod - def keep_error(formatted_error: str, thrown_exception: Exception) -> None: - """ - Deal with the error by keeping a record of it to compare. - """ - _ = thrown_exception - ErrorResults.reported_error = formatted_error - - # pylint: enable=too-few-public-methods - # Arrange supplied_configuration = """[plugins] md999.test_value = 2 """ + results = ErrorResults() expected_did_apply = False + expected_did_error = True - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) os.remove(configuration_file) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + actual_did_apply, actual_did_error = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, None, - ErrorResults.keep_error, + results.keep_error, True, False, ) # Assert assert expected_did_apply == actual_did_apply - assert ErrorResults.reported_error is not None - assert ErrorResults.reported_error.startswith( + assert expected_did_error == actual_did_error + assert results.reported_error is not None + assert results.reported_error.startswith( f"Specified configuration file '{configuration_file}' was not loaded: " ) @@ -270,10 +290,13 @@ def test_toml_loader_toml_file_not_valid(): md999.test_value """ expected_did_apply = False + expected_did_error = True configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act @@ -281,13 +304,17 @@ def test_toml_loader_toml_file_not_valid(): std_output = io.StringIO() try: sys.stdout = std_output - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, None, None, True, False ) finally: sys.stdout = old_stdout assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert std_output is not None assert std_output.getvalue() is not None assert std_output.getvalue().startswith( @@ -309,15 +336,21 @@ def test_toml_loader_toml_file_valid_with_no_section_header(): tools.bar = "fred" """ expected_did_apply = True + expected_did_error = False expected_value = "fred" configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, None, None, True, False ) actual_value = application_properties.get_string_property( @@ -325,6 +358,7 @@ def test_toml_loader_toml_file_valid_with_no_section_header(): ) assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert expected_value == actual_value finally: if configuration_file and os.path.exists(configuration_file): @@ -344,15 +378,21 @@ def test_toml_loader_toml_file_valid_with_one_word_section_header(): tools.bar = "fred" """ expected_did_apply = True + expected_did_error = False expected_value = "fred" configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, section_header, @@ -365,6 +405,7 @@ def test_toml_loader_toml_file_valid_with_one_word_section_header(): ) assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert expected_value == actual_value finally: if configuration_file and os.path.exists(configuration_file): @@ -384,15 +425,21 @@ def test_toml_loader_toml_file_valid_with_one_word_bad_section_header(): tools.bar = "fred" """ expected_did_apply = False + expected_did_error = False expected_value = None configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, section_header, @@ -405,6 +452,7 @@ def test_toml_loader_toml_file_valid_with_one_word_bad_section_header(): ) assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert expected_value == actual_value finally: if configuration_file and os.path.exists(configuration_file): @@ -423,15 +471,21 @@ def test_toml_loader_toml_file_valid_with_multi_word_valid_section_header(): tools.bar = "fred" """ expected_did_apply = True + expected_did_error = False expected_value = "fred" configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, section_header, @@ -444,6 +498,7 @@ def test_toml_loader_toml_file_valid_with_multi_word_valid_section_header(): ) assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert expected_value == actual_value finally: if configuration_file and os.path.exists(configuration_file): @@ -462,15 +517,21 @@ def test_toml_loader_toml_file_valid_with_multi_word_section_header_that_points_ tools.bar = "fred" """ expected_did_apply = False + expected_did_error = False expected_value = None configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, section_header, @@ -483,6 +544,7 @@ def test_toml_loader_toml_file_valid_with_multi_word_section_header_that_points_ ) assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert expected_value == actual_value finally: if configuration_file and os.path.exists(configuration_file): @@ -494,24 +556,6 @@ def test_toml_loader_toml_file_bad_toml_format_with_repeated_section(): Test to make sure that a repeated section, in different forms, causes errors. """ - # pylint: disable=too-few-public-methods - class ErrorResults: - """ - Class to collect the error results. - """ - - reported_error = None - - @staticmethod - def keep_error(formatted_error: str, thrown_exception: Exception) -> None: - """ - Deal with the error by keeping a record of it to compare. - """ - _ = thrown_exception - ErrorResults.reported_error = formatted_error - - # pylint: enable=too-few-public-methods - # Arrange section_header = "plugins.tools.bar" supplied_configuration = """[plugins] @@ -520,26 +564,34 @@ def keep_error(formatted_error: str, thrown_exception: Exception) -> None: [plugins.tools] bar = "barney" """ + results = ErrorResults() expected_did_apply = False + expected_did_error = True configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, section_header, - ErrorResults.keep_error, + results.keep_error, True, False, ) assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert ( - ErrorResults.reported_error + results.reported_error == f"Specified configuration file '{configuration_file}' is not a valid TOML file: Cannot declare ('plugins', 'tools') twice (at line 4, column 15)." ) finally: @@ -552,49 +604,39 @@ def test_toml_loader_toml_file_bad_toml_format_with_header_with_leading_period() Test to make sure that a header that starts with a period is an error. """ - # pylint: disable=too-few-public-methods - class ErrorResults: - """ - Class to collect the error results. - """ - - reported_error = None - - @staticmethod - def keep_error(formatted_error: str, thrown_exception: Exception) -> None: - """ - Deal with the error by keeping a record of it to compare. - """ - _ = thrown_exception - ErrorResults.reported_error = formatted_error - - # pylint: enable=too-few-public-methods - # Arrange section_header = "plugins.tools.bar" supplied_configuration = """[.plugins] tools.bar = "fred" """ + results = ErrorResults() expected_did_apply = False + expected_did_error = True configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, section_header, - ErrorResults.keep_error, + results.keep_error, True, False, ) assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert ( - ErrorResults.reported_error + results.reported_error == f"Specified configuration file '{configuration_file}' is not a valid TOML file: Invalid initial character for a key part (at line 1, column 2)." ) finally: @@ -607,50 +649,88 @@ def test_toml_loader_toml_file_bad_toml_format_with_item_with_leading_period(): Test to make sure that an item that starts with a period is an error. """ - # pylint: disable=too-few-public-methods - class ErrorResults: - """ - Class to collect the error results. - """ + # Arrange + section_header = "plugins.tools.bar" + supplied_configuration = """[plugins] +.tools.bar = "fred" +""" + results = ErrorResults() + expected_did_apply = False + expected_did_error = True + + configuration_file = None + try: + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) + application_properties = ApplicationProperties() + + # Act + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( + application_properties, + configuration_file, + section_header, + results.keep_error, + True, + False, + ) - reported_error = None + assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error + assert ( + results.reported_error + == f"Specified configuration file '{configuration_file}' is not a valid TOML file: Invalid statement (at line 2, column 1)." + ) + finally: + if configuration_file and os.path.exists(configuration_file): + os.remove(configuration_file) - @staticmethod - def keep_error(formatted_error: str, thrown_exception: Exception) -> None: - """ - Deal with the error by keeping a record of it to compare. - """ - _ = thrown_exception - ErrorResults.reported_error = formatted_error - # pylint: enable=too-few-public-methods +def test_toml_loader_toml_file_bad_toml_format_with_double_items_through_different_paths(): + """ + Test to make sure that an item name that is doubled, but through different paths, is caught. + """ # Arrange - section_header = "plugins.tools.bar" + section_header = None supplied_configuration = """[plugins] -.tools.bar = "fred" +tools.bar = "fred" + +[plugins.tools] +bar = "fred" """ + results = ErrorResults() expected_did_apply = False + expected_did_error = True configuration_file = None try: - configuration_file = write_temporary_configuration(supplied_configuration) + configuration_file = TestHelpers.write_temporary_configuration( + supplied_configuration + ) application_properties = ApplicationProperties() # Act - actual_did_apply = ApplicationPropertiesTomlLoader.load_and_set( + ( + actual_did_apply, + actual_did_error, + ) = ApplicationPropertiesTomlLoader.load_and_set( application_properties, configuration_file, section_header, - ErrorResults.keep_error, + results.keep_error, True, False, ) assert expected_did_apply == actual_did_apply + assert expected_did_error == actual_did_error assert ( - ErrorResults.reported_error - == f"Specified configuration file '{configuration_file}' is not a valid TOML file: Invalid statement (at line 2, column 1)." + results.reported_error + == f"Specified configuration file '{configuration_file}' is not a valid TOML file: Cannot declare ('plugins', 'tools') twice (at line 4, column 15)." ) finally: if configuration_file and os.path.exists(configuration_file): diff --git a/test/test_helpers.py b/test/test_helpers.py new file mode 100644 index 0000000..1da4baa --- /dev/null +++ b/test/test_helpers.py @@ -0,0 +1,52 @@ +""" +Module to provide helper methods for tests. +""" +import json +import tempfile + + +# pylint: disable=too-few-public-methods +class TestHelpers: + """ + Class to provide helper methods for tests. + """ + + @staticmethod + def write_temporary_configuration(supplied_configuration): + """ + Write the configuration as a temporary file that is kept around. + """ + try: + with tempfile.NamedTemporaryFile("wt", delete=False) as outfile: + if isinstance(supplied_configuration, str): + outfile.write(supplied_configuration) + else: + json.dump(supplied_configuration, outfile) + return outfile.name + except IOError as ex: + raise AssertionError( + f"Test configuration file was not written ({str(ex)})." + ) from ex + + +# pylint: enable=too-few-public-methods + + +# pylint: disable=too-few-public-methods +class ErrorResults: + """ + Class to collect the error results. + """ + + def __init__(self): + self.reported_error = None + + def keep_error(self, formatted_error: str, thrown_exception: Exception) -> None: + """ + Deal with the error by keeping a record of it to compare. + """ + _ = thrown_exception + self.reported_error = formatted_error + + +# pylint: enable=too-few-public-methods From e2c6d612e18a9d69b6453a8a19d417872af1430c Mon Sep 17 00:00:00 2001 From: Jack De Winter Date: Sun, 23 Apr 2023 08:13:36 -0700 Subject: [PATCH 2/4] reversing config change --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 96422b4..ab14897 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,7 +13,7 @@ select = B,C,E,F,W,T4 ## Run with: pylint --rcfile=setup.cfg somepackage [pylint] -disable = C0301 +disable = C0301, W0511, duplicate-code extension-pkg-whitelist=win32api #ignore = migrations #ignore-docstrings = yes From dfdaad52d6ae1916a3f63e35d7e575ab11c81154 Mon Sep 17 00:00:00 2001 From: Jack De Winter Date: Sun, 23 Apr 2023 08:39:57 -0700 Subject: [PATCH 3/4] updating packages --- Pipfile | 12 +-- Pipfile.lock | 236 +++++++++++++++++++++++++-------------------------- 2 files changed, 124 insertions(+), 124 deletions(-) diff --git a/Pipfile b/Pipfile index 026bd0d..34651be 100644 --- a/Pipfile +++ b/Pipfile @@ -4,7 +4,7 @@ verify_ssl = true name = "pypi" [packages] -black = "==23.1.0" +black = "==23.3.0" isort = "==5.12.0" flake8 = "==6.0.0" flake8-bugbear = "==23.3.23" @@ -13,7 +13,7 @@ project-summarizer = "==0.5.0" pylint = "==2.17.2" pylint-utils = "==0.5.0" pyroma = "==4.2" -pytest = "==7.3.0" +pytest = "==7.3.1" pytest-cov = "==4.0.0" pytest-console-scripts = "==1.3.1" pytest-timeout = "==2.1.0" @@ -22,10 +22,10 @@ pytest-xdist = "==3.2.1" setuptools = "==65.3.0" twine = "==4.0.2" typing-extensions = "==4.5.0" -mypy = "==1.1.1" -colorama = "==0.4.5" -sourcery = "==1.1.0" -coverage = "==7.2.1" +mypy = "==1.2.0" +colorama = "==0.4.6" +sourcery = "==1.2.0" +coverage = "==7.2.3" bandit = "==1.7.5" tomli = "==2.0.1" diff --git a/Pipfile.lock b/Pipfile.lock index 642ec30..34e5148 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "6706beeeaf219d45fece6701d94fd9e5add2f16ffd61dcc256f55f75bf602705" + "sha256": "2e5c6679ba94c8c0db762cf6b495a4827c3cc84031d6ec0b6e1840ae69f7e85a" }, "pipfile-spec": 6, "requires": { @@ -42,34 +42,34 @@ }, "black": { "hashes": [ - "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd", - "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555", - "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481", - "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468", - "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9", - "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a", - "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958", - "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580", - "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26", - "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32", - "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8", - "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753", - "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b", - "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074", - "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651", - "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24", - "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6", - "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad", - "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac", - "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221", - "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06", - "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27", - "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648", - "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739", - "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104" + "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5", + "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915", + "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326", + "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940", + "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b", + "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30", + "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c", + "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c", + "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab", + "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27", + "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2", + "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961", + "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9", + "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb", + "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70", + "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331", + "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2", + "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266", + "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d", + "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6", + "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b", + "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925", + "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8", + "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4", + "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3" ], "index": "pypi", - "version": "==23.1.0" + "version": "==23.3.0" }, "bleach": { "hashes": [ @@ -194,11 +194,11 @@ }, "colorama": { "hashes": [ - "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da", - "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4" + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" ], "index": "pypi", - "version": "==0.4.5" + "version": "==0.4.6" }, "columnar": { "hashes": [ @@ -212,60 +212,60 @@ "toml" ], "hashes": [ - "sha256:0339dc3237c0d31c3b574f19c57985fcbe494280153bbcad33f2cdf469f4ac3e", - "sha256:09643fb0df8e29f7417adc3f40aaf379d071ee8f0350ab290517c7004f05360b", - "sha256:0bd7e628f6c3ec4e7d2d24ec0e50aae4e5ae95ea644e849d92ae4805650b4c4e", - "sha256:0cf557827be7eca1c38a2480484d706693e7bb1929e129785fe59ec155a59de6", - "sha256:0f8318ed0f3c376cfad8d3520f496946977abde080439d6689d7799791457454", - "sha256:1b7fb13850ecb29b62a447ac3516c777b0e7a09ecb0f4bb6718a8654c87dfc80", - "sha256:22c308bc508372576ffa3d2dbc4824bb70d28eeb4fcd79d4d1aed663a06630d0", - "sha256:3004765bca3acd9e015794e5c2f0c9a05587f5e698127ff95e9cfba0d3f29339", - "sha256:3a209d512d157379cc9ab697cbdbb4cfd18daa3e7eebaa84c3d20b6af0037384", - "sha256:436313d129db7cf5b4ac355dd2bd3f7c7e5294af077b090b85de75f8458b8616", - "sha256:49567ec91fc5e0b15356da07a2feabb421d62f52a9fff4b1ec40e9e19772f5f8", - "sha256:4dd34a935de268a133e4741827ae951283a28c0125ddcdbcbba41c4b98f2dfef", - "sha256:570c21a29493b350f591a4b04c158ce1601e8d18bdcd21db136fbb135d75efa6", - "sha256:5928b85416a388dd557ddc006425b0c37e8468bd1c3dc118c1a3de42f59e2a54", - "sha256:5d2b9b5e70a21474c105a133ba227c61bc95f2ac3b66861143ce39a5ea4b3f84", - "sha256:617a94ada56bbfe547aa8d1b1a2b8299e2ec1ba14aac1d4b26a9f7d6158e1273", - "sha256:6a034480e9ebd4e83d1aa0453fd78986414b5d237aea89a8fdc35d330aa13bae", - "sha256:6fce673f79a0e017a4dc35e18dc7bb90bf6d307c67a11ad5e61ca8d42b87cbff", - "sha256:78d2c3dde4c0b9be4b02067185136b7ee4681978228ad5ec1278fa74f5ca3e99", - "sha256:7f099da6958ddfa2ed84bddea7515cb248583292e16bb9231d151cd528eab657", - "sha256:80559eaf6c15ce3da10edb7977a1548b393db36cbc6cf417633eca05d84dd1ed", - "sha256:834c2172edff5a08d78e2f53cf5e7164aacabeb66b369f76e7bb367ca4e2d993", - "sha256:861cc85dfbf55a7a768443d90a07e0ac5207704a9f97a8eb753292a7fcbdfcfc", - "sha256:8649371570551d2fd7dee22cfbf0b61f1747cdfb2b7587bb551e4beaaa44cb97", - "sha256:87dc37f16fb5e3a28429e094145bf7c1753e32bb50f662722e378c5851f7fdc6", - "sha256:8a6450da4c7afc4534305b2b7d8650131e130610cea448ff240b6ab73d7eab63", - "sha256:8d3843ca645f62c426c3d272902b9de90558e9886f15ddf5efe757b12dd376f5", - "sha256:8dca3c1706670297851bca1acff9618455122246bdae623be31eca744ade05ec", - "sha256:97a3189e019d27e914ecf5c5247ea9f13261d22c3bb0cfcfd2a9b179bb36f8b1", - "sha256:99f4dd81b2bb8fc67c3da68b1f5ee1650aca06faa585cbc6818dbf67893c6d58", - "sha256:9e872b082b32065ac2834149dc0adc2a2e6d8203080501e1e3c3c77851b466f9", - "sha256:a81dbcf6c6c877986083d00b834ac1e84b375220207a059ad45d12f6e518a4e3", - "sha256:abacd0a738e71b20e224861bc87e819ef46fedba2fb01bc1af83dfd122e9c319", - "sha256:ae82c988954722fa07ec5045c57b6d55bc1a0890defb57cf4a712ced65b26ddd", - "sha256:b0c0d46de5dd97f6c2d1b560bf0fcf0215658097b604f1840365296302a9d1fb", - "sha256:b1991a6d64231a3e5bbe3099fb0dd7c9aeaa4275ad0e0aeff4cb9ef885c62ba2", - "sha256:b2167d116309f564af56f9aa5e75ef710ef871c5f9b313a83050035097b56820", - "sha256:bd5a12239c0006252244f94863f1c518ac256160cd316ea5c47fb1a11b25889a", - "sha256:bdd3f2f285ddcf2e75174248b2406189261a79e7fedee2ceeadc76219b6faa0e", - "sha256:c77f2a9093ccf329dd523a9b2b3c854c20d2a3d968b6def3b820272ca6732242", - "sha256:cb5f152fb14857cbe7f3e8c9a5d98979c4c66319a33cad6e617f0067c9accdc4", - "sha256:cca7c0b7f5881dfe0291ef09ba7bb1582cb92ab0aeffd8afb00c700bf692415a", - "sha256:d2ef6cae70168815ed91388948b5f4fcc69681480a0061114db737f957719f03", - "sha256:d9256d4c60c4bbfec92721b51579c50f9e5062c21c12bec56b55292464873508", - "sha256:e191a63a05851f8bce77bc875e75457f9b01d42843f8bd7feed2fc26bbe60833", - "sha256:e2b50ebc2b6121edf352336d503357321b9d8738bb7a72d06fc56153fd3f4cd8", - "sha256:e3ea04b23b114572b98a88c85379e9e9ae031272ba1fb9b532aa934c621626d4", - "sha256:e4d70c853f0546855f027890b77854508bdb4d6a81242a9d804482e667fff6e6", - "sha256:f29351393eb05e6326f044a7b45ed8e38cb4dcc38570d12791f271399dc41431", - "sha256:f3d07edb912a978915576a776756069dede66d012baa503022d3a0adba1b6afa", - "sha256:fac6343bae03b176e9b58104a9810df3cdccd5cfed19f99adfa807ffbf43cf9b" + "sha256:06ddd9c0249a0546997fdda5a30fbcb40f23926df0a874a60a8a185bc3a87d93", + "sha256:0743b0035d4b0e32bc1df5de70fba3059662ace5b9a2a86a9f894cfe66569013", + "sha256:0f3736a5d34e091b0a611964c6262fd68ca4363df56185902528f0b75dbb9c1f", + "sha256:1127b16220f7bfb3f1049ed4a62d26d81970a723544e8252db0efde853268e21", + "sha256:172db976ae6327ed4728e2507daf8a4de73c7cc89796483e0a9198fd2e47b462", + "sha256:182eb9ac3f2b4874a1f41b78b87db20b66da6b9cdc32737fbbf4fea0c35b23fc", + "sha256:1bb1e77a9a311346294621be905ea8a2c30d3ad371fc15bb72e98bfcfae532df", + "sha256:1fd78b911aea9cec3b7e1e2622c8018d51c0d2bbcf8faaf53c2497eb114911c1", + "sha256:20d1a2a76bb4eb00e4d36b9699f9b7aba93271c9c29220ad4c6a9581a0320235", + "sha256:21b154aba06df42e4b96fc915512ab39595105f6c483991287021ed95776d934", + "sha256:2c2e58e45fe53fab81f85474e5d4d226eeab0f27b45aa062856c89389da2f0d9", + "sha256:2c3b2803e730dc2797a017335827e9da6da0e84c745ce0f552e66400abdfb9a1", + "sha256:3146b8e16fa60427e03884301bf8209221f5761ac754ee6b267642a2fd354c48", + "sha256:344e714bd0fe921fc72d97404ebbdbf9127bac0ca1ff66d7b79efc143cf7c0c4", + "sha256:387065e420aed3c71b61af7e82c7b6bc1c592f7e3c7a66e9f78dd178699da4fe", + "sha256:3f04becd4fcda03c0160d0da9c8f0c246bc78f2f7af0feea1ec0930e7c93fa4a", + "sha256:4a42e1eff0ca9a7cb7dc9ecda41dfc7cbc17cb1d02117214be0561bd1134772b", + "sha256:4ea748802cc0de4de92ef8244dd84ffd793bd2e7be784cd8394d557a3c751e21", + "sha256:55416d7385774285b6e2a5feca0af9652f7f444a4fa3d29d8ab052fafef9d00d", + "sha256:5d0391fb4cfc171ce40437f67eb050a340fdbd0f9f49d6353a387f1b7f9dd4fa", + "sha256:63cdeaac4ae85a179a8d6bc09b77b564c096250d759eed343a89d91bce8b6367", + "sha256:72fcae5bcac3333a4cf3b8f34eec99cea1187acd55af723bcbd559adfdcb5535", + "sha256:7c4ed4e9f3b123aa403ab424430b426a1992e6f4c8fd3cb56ea520446e04d152", + "sha256:83957d349838a636e768251c7e9979e899a569794b44c3728eaebd11d848e58e", + "sha256:87ecc7c9a1a9f912e306997ffee020297ccb5ea388421fe62a2a02747e4d5539", + "sha256:8f69770f5ca1994cb32c38965e95f57504d3aea96b6c024624fdd5bb1aa494a1", + "sha256:8f6c930fd70d91ddee53194e93029e3ef2aabe26725aa3c2753df057e296b925", + "sha256:965ee3e782c7892befc25575fa171b521d33798132692df428a09efacaffe8d0", + "sha256:974bc90d6f6c1e59ceb1516ab00cf1cdfbb2e555795d49fa9571d611f449bcb2", + "sha256:981b4df72c93e3bc04478153df516d385317628bd9c10be699c93c26ddcca8ab", + "sha256:aa784405f0c640940595fa0f14064d8e84aff0b0f762fa18393e2760a2cf5841", + "sha256:ae7863a1d8db6a014b6f2ff9c1582ab1aad55a6d25bac19710a8df68921b6e30", + "sha256:aeae2aa38395b18106e552833f2a50c27ea0000122bde421c31d11ed7e6f9c91", + "sha256:b2317d5ed777bf5a033e83d4f1389fd4ef045763141d8f10eb09a7035cee774c", + "sha256:be19931a8dcbe6ab464f3339966856996b12a00f9fe53f346ab3be872d03e257", + "sha256:be9824c1c874b73b96288c6d3de793bf7f3a597770205068c6163ea1f326e8b9", + "sha256:c0045f8f23a5fb30b2eb3b8a83664d8dc4fb58faddf8155d7109166adb9f2040", + "sha256:c86bd45d1659b1ae3d0ba1909326b03598affbc9ed71520e0ff8c31a993ad911", + "sha256:ca0f34363e2634deffd390a0fef1aa99168ae9ed2af01af4a1f5865e362f8623", + "sha256:d298c2815fa4891edd9abe5ad6e6cb4207104c7dd9fd13aea3fdebf6f9b91259", + "sha256:d2a3a6146fe9319926e1d477842ca2a63fe99af5ae690b1f5c11e6af074a6b5c", + "sha256:dfd393094cd82ceb9b40df4c77976015a314b267d498268a076e940fe7be6b79", + "sha256:e58c0d41d336569d63d1b113bd573db8363bc4146f39444125b7f8060e4e04f5", + "sha256:ea3f5bc91d7d457da7d48c7a732beaf79d0c8131df3ab278e6bba6297e23c6c4", + "sha256:ea53151d87c52e98133eb8ac78f1206498c015849662ca8dc246255265d9c3c4", + "sha256:eb0edc3ce9760d2f21637766c3aa04822030e7451981ce569a1b3456b7053f22", + "sha256:f649dd53833b495c3ebd04d6eec58479454a1784987af8afb77540d6c1767abd", + "sha256:f760073fcf8f3d6933178d67754f4f2d4e924e321f4bb0dcef0424ca0215eba1", + "sha256:fa546d66639d69aa967bf08156eb8c9d0cd6f6de84be9e8c9819f52ad499c910", + "sha256:fd214917cabdd6f673a29d708574e9fbdb892cb77eb426d0eae3490d95ca7859", + "sha256:fff5aaa6becf2c6a1699ae6a39e2e6fb0672c2d42eca8eb0cafa91cf2e9bd312" ], "index": "pypi", - "version": "==7.2.1" + "version": "==7.2.3" }, "defusedxml": { "hashes": [ @@ -494,35 +494,35 @@ }, "mypy": { "hashes": [ - "sha256:0a28a76785bf57655a8ea5eb0540a15b0e781c807b5aa798bd463779988fa1d5", - "sha256:19ba15f9627a5723e522d007fe708007bae52b93faab00f95d72f03e1afa9598", - "sha256:21b437be1c02712a605591e1ed1d858aba681757a1e55fe678a15c2244cd68a5", - "sha256:26cdd6a22b9b40b2fd71881a8a4f34b4d7914c679f154f43385ca878a8297389", - "sha256:2888ce4fe5aae5a673386fa232473014056967f3904f5abfcf6367b5af1f612a", - "sha256:2b0c373d071593deefbcdd87ec8db91ea13bd8f1328d44947e88beae21e8d5e9", - "sha256:315ac73cc1cce4771c27d426b7ea558fb4e2836f89cb0296cbe056894e3a1f78", - "sha256:39c7119335be05630611ee798cc982623b9e8f0cff04a0b48dfc26100e0b97af", - "sha256:4b398d8b1f4fba0e3c6463e02f8ad3346f71956b92287af22c9b12c3ec965a9f", - "sha256:4e4e8b362cdf99ba00c2b218036002bdcdf1e0de085cdb296a49df03fb31dfc4", - "sha256:59bbd71e5c58eed2e992ce6523180e03c221dcd92b52f0e792f291d67b15a71c", - "sha256:5b5f81b40d94c785f288948c16e1f2da37203c6006546c5d947aab6f90aefef2", - "sha256:5cb14ff9919b7df3538590fc4d4c49a0f84392237cbf5f7a816b4161c061829e", - "sha256:61bf08362e93b6b12fad3eab68c4ea903a077b87c90ac06c11e3d7a09b56b9c1", - "sha256:64cc3afb3e9e71a79d06e3ed24bb508a6d66f782aff7e56f628bf35ba2e0ba51", - "sha256:69b35d1dcb5707382810765ed34da9db47e7f95b3528334a3c999b0c90fe523f", - "sha256:9401e33814cec6aec8c03a9548e9385e0e228fc1b8b0a37b9ea21038e64cdd8a", - "sha256:a380c041db500e1410bb5b16b3c1c35e61e773a5c3517926b81dfdab7582be54", - "sha256:ae9ceae0f5b9059f33dbc62dea087e942c0ccab4b7a003719cb70f9b8abfa32f", - "sha256:b7c7b708fe9a871a96626d61912e3f4ddd365bf7f39128362bc50cbd74a634d5", - "sha256:c1c10fa12df1232c936830839e2e935d090fc9ee315744ac33b8a32216b93707", - "sha256:ce61663faf7a8e5ec6f456857bfbcec2901fbdb3ad958b778403f63b9e606a1b", - "sha256:d64c28e03ce40d5303450f547e07418c64c241669ab20610f273c9e6290b4b0b", - "sha256:d809f88734f44a0d44959d795b1e6f64b2bbe0ea4d9cc4776aa588bb4229fc1c", - "sha256:dbb19c9f662e41e474e0cff502b7064a7edc6764f5262b6cd91d698163196799", - "sha256:ef6a01e563ec6a4940784c574d33f6ac1943864634517984471642908b30b6f7" + "sha256:023fe9e618182ca6317ae89833ba422c411469156b690fde6a315ad10695a521", + "sha256:031fc69c9a7e12bcc5660b74122ed84b3f1c505e762cc4296884096c6d8ee140", + "sha256:2de7babe398cb7a85ac7f1fd5c42f396c215ab3eff731b4d761d68d0f6a80f48", + "sha256:2e93a8a553e0394b26c4ca683923b85a69f7ccdc0139e6acd1354cc884fe0128", + "sha256:390bc685ec209ada4e9d35068ac6988c60160b2b703072d2850457b62499e336", + "sha256:3a2d219775a120581a0ae8ca392b31f238d452729adbcb6892fa89688cb8306a", + "sha256:3efde4af6f2d3ccf58ae825495dbb8d74abd6d176ee686ce2ab19bd025273f41", + "sha256:4a99fe1768925e4a139aace8f3fb66db3576ee1c30b9c0f70f744ead7e329c9f", + "sha256:4b41412df69ec06ab141808d12e0bf2823717b1c363bd77b4c0820feaa37249e", + "sha256:4c8d8c6b80aa4a1689f2a179d31d86ae1367ea4a12855cc13aa3ba24bb36b2d8", + "sha256:4d19f1a239d59f10fdc31263d48b7937c585810288376671eaf75380b074f238", + "sha256:4e4a682b3f2489d218751981639cffc4e281d548f9d517addfd5a2917ac78119", + "sha256:695c45cea7e8abb6f088a34a6034b1d273122e5530aeebb9c09626cea6dca4cb", + "sha256:701189408b460a2ff42b984e6bd45c3f41f0ac9f5f58b8873bbedc511900086d", + "sha256:70894c5345bea98321a2fe84df35f43ee7bb0feec117a71420c60459fc3e1eed", + "sha256:8293a216e902ac12779eb7a08f2bc39ec6c878d7c6025aa59464e0c4c16f7eb9", + "sha256:8d26b513225ffd3eacece727f4387bdce6469192ef029ca9dd469940158bc89e", + "sha256:a197ad3a774f8e74f21e428f0de7f60ad26a8d23437b69638aac2764d1e06a6a", + "sha256:bea55fc25b96c53affab852ad94bf111a3083bc1d8b0c76a61dd101d8a388cf5", + "sha256:c9a084bce1061e55cdc0493a2ad890375af359c766b8ac311ac8120d3a472950", + "sha256:d0e9464a0af6715852267bf29c9553e4555b61f5904a4fc538547a4d67617937", + "sha256:d8e9187bfcd5ffedbe87403195e1fc340189a68463903c39e2b63307c9fa0394", + "sha256:eaeaa0888b7f3ccb7bcd40b50497ca30923dba14f385bde4af78fac713d6d6f6", + "sha256:f46af8d162f3d470d8ffc997aaf7a269996d205f9d746124a179d3abe05ac602", + "sha256:f70a40410d774ae23fcb4afbbeca652905a04de7948eaf0b1789c8d1426b72d1", + "sha256:fe91be1c51c90e2afe6827601ca14353bbf3953f343c2129fa1e247d55fd95ba" ], "index": "pypi", - "version": "==1.1.1" + "version": "==1.2.0" }, "mypy-extensions": { "hashes": [ @@ -670,11 +670,11 @@ }, "pytest": { "hashes": [ - "sha256:58ecc27ebf0ea643ebfdf7fb1249335da761a00c9f955bcd922349bcb68ee57d", - "sha256:933051fa1bfbd38a21e73c3960cebdad4cf59483ddba7696c48509727e17f201" + "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362", + "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3" ], "index": "pypi", - "version": "==7.3.0" + "version": "==7.3.1" }, "pytest-console-scripts": { "hashes": [ @@ -705,7 +705,7 @@ "sha256:acb739f89fabb3d798c099e9e0c035003062367a441910aaaf2281bc1972ee14", "sha256:fcc653f65fe3035b478820b5284fbf0f52803622ee3f60a2faed7a7d3ba1f41e" ], - "markers": "python_version >= '3.7' and python_version < '4.0'", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==2.0.4" }, "pytest-timeout": { @@ -791,7 +791,7 @@ "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" ], - "markers": "python_version >= '3.7' and python_version < '4.0'", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==2.28.2" }, "requests-toolbelt": { @@ -836,12 +836,12 @@ }, "sourcery": { "hashes": [ - "sha256:2054e9c61d5c36c04b047b7245a3715be9ad437d52fff796f9c295a2d24a426f", - "sha256:63ae11a15b73e19114da0c1a85a3ae07ca7bf52486949549e5fcf790a8e183cf", - "sha256:ca87384c1705edf34c00ad6c3de34a1de07031761ff05253df809a2b6a701201" + "sha256:311f635327bff4cfaaadd28eb52aadc7a8d8b37e563bdaa16691fba6dd411e36", + "sha256:ef0a64f0c5bb6f54ae34e48a3c11871a52b719695356875880dafeb29ce04bae", + "sha256:f44ea946afc06f3e1d1d1f882a84a544c2605e013ddb327e8da8dae0f9dd7525" ], "index": "pypi", - "version": "==1.1.0" + "version": "==1.2.0" }, "stevedore": { "hashes": [ From db780dc3d230389b5f645408aeb98877365a50bf Mon Sep 17 00:00:00 2001 From: Jack De Winter Date: Sun, 23 Apr 2023 09:02:45 -0700 Subject: [PATCH 4/4] updating yaml versions --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6e0620b..2949f61 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@master - name: Setup Python 3.8 - uses: actions/setup-python@v4.5.0 + uses: actions/setup-python@v4.6.0 with: python-version: 3.8 - name: Install PipEnv @@ -59,7 +59,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@master - name: Setup Python 3.8 - uses: actions/setup-python@v4.5.0 + uses: actions/setup-python@v4.6.0 with: python-version: 3.8 - name: Install PipEnv