From 9299a12eb60e73a7b2b8fe94d5ef2f1e749607ec Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Mon, 18 Oct 2021 01:03:49 +0100 Subject: [PATCH] adding latest tests --- panaetius/config.py | 19 +++++++++++---- panaetius/exceptions.py | 3 +++ rewrite.todo | 6 +++-- tests/test_config.py | 52 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index 0f9f44a..193f287 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -7,7 +7,7 @@ from typing import Any import toml -from panaetius.exceptions import KeyErrorTooDeepException +from panaetius.exceptions import KeyErrorTooDeepException, InvalidPythonException class Config: @@ -57,6 +57,12 @@ class Config: try: # look under top header # REVIEW: could this be auto handled for a key of arbitrary length? + + # check for env variable and have it take priority + value = os.environ.get(env_key.replace("-", "_")) + if value is not None: + return self.__get_config_value_env_var_override(value) + if len(key.split(".")) > 2: raise KeyErrorTooDeepException( f"Your key of {key} can only be 2 levels deep maximum. " @@ -69,10 +75,9 @@ class Config: raise KeyError() except (KeyError, TypeError): - value = os.environ.get(env_key.replace("-", "_")) if value is None: return self.__get_config_value_missing_key_value_is_none(default) - # if env var, coerce value if flag is set, else return a TOML string + # if env var is present, load it return self.__get_config_value_missing_key_value_is_not_none(value) def __get_config_value_key_split_once(self, key: str) -> Any: @@ -89,6 +94,9 @@ class Config: def __get_config_value_missing_key_value_is_not_none(self, value: str) -> Any: return self.__load_value(value) + def __get_config_value_env_var_override(self, value: str) -> Any: + return self.__load_value(value) + def _get_env_value(self, env_key: str, default: Any) -> Any: # noqa # look for an environment variable, fallback to default value = os.environ.get(env_key.replace("-", "_")) @@ -97,7 +105,10 @@ class Config: return self.__load_value(value) def __load_value(self, value: str) -> Any: # noqa - return ast.literal_eval(value) + try: + return ast.literal_eval(value) + except (ValueError, SyntaxError): + raise InvalidPythonException(f"{value} is not valid Python.") # noqa def __load_default_value(self, default: Any) -> Any: # noqa return default diff --git a/panaetius/exceptions.py b/panaetius/exceptions.py index a7a6333..fbaea3a 100644 --- a/panaetius/exceptions.py +++ b/panaetius/exceptions.py @@ -4,3 +4,6 @@ class KeyErrorTooDeepException(Exception): class LoggingDirectoryDoesNotExistException(Exception): pass + +class InvalidPythonException(Exception): + pass diff --git a/rewrite.todo b/rewrite.todo index b28f883..f3f3a44 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -25,13 +25,14 @@ Coding: ☐ Write the docstrings for public functions/methods. Functionality: - ☐ When both a config file and a env var is found, use the env var. + ✔ When both a config file and a env var is found, use the env var. @done(21-10-18 00:38) Documentation: ☐ Rewrite documentation using `mkdocs` and using `.md`. @2h Misc: - ☐ Use the python runner to build the docs & run the tests + ☐ Use the python runner to build the docs & run the tests (including coverage html) + coverage run -m pytest && coverage report && coverage html ☐ document this in trilium Tests: @@ -63,6 +64,7 @@ Tests: ✔ check if env key is missing the default is read in @done(21-10-17 20:55) ✔ check if env key is present the values are read in @done(21-10-17 22:24) ✔ parametrise a test to read in values form env vars and they're set correctly @done(21-10-17 22:24) + ✔ test that the env var is valid python @done(21-10-18 01:03) library: ✔ test set_config works @done(21-10-17 23:29) diff --git a/tests/test_config.py b/tests/test_config.py index d9bb8ec..3137e22 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -5,7 +5,7 @@ from uuid import uuid4 import pytest import panaetius -from panaetius.exceptions import KeyErrorTooDeepException +from panaetius.exceptions import InvalidPythonException, KeyErrorTooDeepException # test config paths @@ -44,7 +44,9 @@ def test_config_file_exists(header, shared_datadir): assert config._missing_config is False -def test_config_file_contents_read_success(header, shared_datadir, testing_config_contents): +def test_config_file_contents_read_success( + header, shared_datadir, testing_config_contents +): # arrange config_path = str(shared_datadir / "without_logging") @@ -102,6 +104,23 @@ def test_get_value_from_key( assert config_value == expected_value +def test_get_value_environment_var_override(header, shared_datadir): + # arrange + os.environ[f"{header.upper()}_SOME_TOP_STRING"] = '"some_overridden_value"' + config_path = str(shared_datadir / "without_logging") + config = panaetius.Config(header, config_path) + panaetius.set_config(config, "some_top_string") + + # act + config_value = getattr(config, "some_top_string") + + # assert + assert config_value == "some_overridden_value" + + # cleanup + del os.environ[f"{header.upper()}_SOME_TOP_STRING"] + + def test_key_level_too_deep(header, shared_datadir): # arrange config_path = str(shared_datadir / "without_logging") @@ -151,6 +170,9 @@ def test_get_value_missing_key_from_env(header, shared_datadir): # assert assert value_from_key == "some missing key" + # cleanup + del os.environ[f"{header.upper()}_MISSING_KEY"] + # test env vars @@ -197,7 +219,9 @@ def test_missing_config_read_from_default(header, shared_datadir): ), ], ) -def test_missing_config_read_from_env_var(env_value, expected_value, header, shared_datadir): +def test_missing_config_read_from_env_var( + env_value, expected_value, header, shared_datadir +): # arrange config_path = str(shared_datadir / str(uuid4())) os.environ[f"{header.upper()}_MISSING_KEY_READ_FROM_ENV_VAR"] = env_value @@ -208,3 +232,25 @@ def test_missing_config_read_from_env_var(env_value, expected_value, header, sha # assert assert getattr(config, "missing_key_read_from_env_var") == expected_value + + # cleanup + del os.environ[f"{header.upper()}_MISSING_KEY_READ_FROM_ENV_VAR"] + + +def test_missing_config_read_from_env_var_invalid_python(header): + # arrange + os.environ[f"{header.upper()}_INVALID_PYTHON"] = "a string without quotes" + config = panaetius.Config(header) + + # act + with pytest.raises(InvalidPythonException) as invalid_python_exception: + panaetius.set_config(config, "invalid_python") + + # assert + assert ( + str(invalid_python_exception.value) + == "a string without quotes is not valid Python." + ) + + # cleanup + del os.environ[f"{header.upper()}_INVALID_PYTHON"]