adding latest tests

This commit is contained in:
2021-10-18 00:12:20 +01:00
parent 4ae4eb085c
commit f73a6d2441
17 changed files with 402 additions and 95 deletions

15
.vscode/settings.json vendored
View File

@@ -4,20 +4,5 @@
"python.linting.enabled": true, "python.linting.enabled": true,
"python.pythonPath": ".venv/bin/python", "python.pythonPath": ".venv/bin/python",
"restructuredtext.confPath": "${workspaceFolder}/docs/source", "restructuredtext.confPath": "${workspaceFolder}/docs/source",
"workbench.colorCustomizations": {
"editorGroup.border": "#3ea389",
"panel.border": "#3ea389",
"sash.hoverBorder": "#3ea389",
"sideBar.border": "#3ea389",
"statusBar.background": "#307e6a",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#3ea389",
"statusBarItem.remoteBackground": "#307e6a",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#307e6a",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#307e6a99",
"titleBar.inactiveForeground": "#e7e7e799"
},
"peacock.color": "#307E6A" "peacock.color": "#307E6A"
} }

3
TODO Normal file
View File

@@ -0,0 +1,3 @@
Todo:
☐ Item

View File

@@ -1,10 +1,14 @@
from __future__ import annotations from __future__ import annotations
import ast
import os import os
import pathlib import pathlib
from typing import Any from typing import Any
import toml import toml
from panaetius.exceptions import KeyErrorTooDeepException
class Config: class Config:
"""docstring for Config().""" """docstring for Config()."""
@@ -16,7 +20,7 @@ class Config:
if config_path if config_path
else pathlib.Path.home() / ".config" else pathlib.Path.home() / ".config"
) )
self._missing_config = False self._missing_config = self._check_config_file_exists()
# default logging options # default logging options
self.logging_path: str | None = None self.logging_path: str | None = None
@@ -24,42 +28,52 @@ class Config:
self.logging_backup_count: int = 0 self.logging_backup_count: int = 0
@property @property
def config(self) -> dict[str, Any]: def config(self) -> dict:
config_file_location = self.config_path / self.header_variable / "config.toml" config_file_location = self.config_path / self.header_variable / "config.toml"
try: try:
with open(config_file_location, "r", encoding="utf-8") as config_file: with open(config_file_location, "r", encoding="utf-8") as config_file:
return dict(toml.load(config_file)) return dict(toml.load(config_file))
except FileNotFoundError: except FileNotFoundError:
self._missing_config = True
return {} return {}
def get_value(self, key: str, default: Any, coerce: bool = False) -> Any: def get_value(self, key: str, default: Any) -> Any:
env_key = f"{self.header_variable.upper()}_{key.upper().replace('.', '_')}" env_key = f"{self.header_variable.upper()}_{key.upper().replace('.', '_')}"
if not self._missing_config: if not self._missing_config:
# look in the config file # look in the config file
return self._get_config_value(env_key, key, default, coerce) return self._get_config_value(env_key, key, default)
# no config file, look for env vars # no config file, look for env vars
return self._get_env_value(env_key, default, coerce) return self._get_env_value(env_key, default)
def _get_config_value( def _check_config_file_exists(self) -> bool:
self, env_key: str, key: str, default: Any, coerce: bool = False config_file_location = self.config_path / self.header_variable / "config.toml"
) -> Any: try:
with open(config_file_location, "r", encoding="utf-8"):
return False
except FileNotFoundError:
return True
def _get_config_value(self, env_key: str, key: str, default: Any) -> Any:
try: try:
# look under top header # look under top header
# REVIEW: could this be auto handled for a key of arbitrary length? # REVIEW: could this be auto handled for a key of arbitrary length?
if len(key.split(".")) > 2:
raise KeyErrorTooDeepException(
f"Your key of {key} can only be 2 levels deep maximum. "
f"You have {len(key.split('.'))}"
)
if len(key.split(".")) == 1: if len(key.split(".")) == 1:
return self.__get_config_value_key_split_once(key) return self.__get_config_value_key_split_once(key)
if len(key.split(".")) == 2: if len(key.split(".")) == 2:
return self.__get_config_value_key_split_twice(key) return self.__get_config_value_key_split_twice(key)
raise KeyError raise KeyError()
except (KeyError, TypeError): except (KeyError, TypeError):
value = os.environ.get(env_key.replace("-", "_")) value = os.environ.get(env_key.replace("-", "_"))
if value is None: if value is None:
return self.__get_config_value_missing_key_value_is_none(default) 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, coerce value if flag is set, else return a TOML string
return self.__get_config_value_missing_key_value_is_not_none(value, coerce) return self.__get_config_value_missing_key_value_is_not_none(value)
def __get_config_value_key_split_once(self, key: str) -> Any: def __get_config_value_key_split_once(self, key: str) -> Any:
name = key.lower() name = key.lower()
@@ -72,33 +86,18 @@ class Config:
def __get_config_value_missing_key_value_is_none(self, default: Any) -> Any: def __get_config_value_missing_key_value_is_none(self, default: Any) -> Any:
return self.__load_default_value(default) return self.__load_default_value(default)
def __get_config_value_missing_key_value_is_not_none( def __get_config_value_missing_key_value_is_not_none(self, value: str) -> Any:
self, value: str, coerce: bool return self.__load_value(value)
) -> Any:
return self.__load_value(value, coerce)
def _get_env_value( # noqa def _get_env_value(self, env_key: str, default: Any) -> Any: # noqa
self, env_key: str, default: Any, coerce: bool = False
) -> Any:
# look for an environment variable, fallback to default # look for an environment variable, fallback to default
value = os.environ.get(env_key.replace("-", "_")) value = os.environ.get(env_key.replace("-", "_"))
if value is None: if value is None:
return self.__load_default_value(default) return self.__load_default_value(default)
return self.__load_value(value, coerce) return self.__load_value(value)
def __load_value(self, value: str, coerce: bool) -> Any: # noqa def __load_value(self, value: str) -> Any: # noqa
value = str(value).lower() if isinstance(value, bool) else value return ast.literal_eval(value)
return (
toml.loads(f"value = {value}")["value"]
if coerce
else toml.loads(f'value = "{value}"')["value"]
)
def __load_default_value(self, default: Any) -> Any: # noqa def __load_default_value(self, default: Any) -> Any: # noqa
if isinstance(default, str): return default
return toml.loads(f'value = "{default}"')["value"]
# if default is bool convert to lower case toml syntax
default = str(default).lower() if isinstance(default, bool) else default
return (
toml.loads(f"value = {default}")["value"] if default is not None else None
)

6
panaetius/exceptions.py Normal file
View File

@@ -0,0 +1,6 @@
class KeyErrorTooDeepException(Exception):
pass
class LoggingDirectoryDoesNotExistException(Exception):
pass

View File

@@ -9,7 +9,6 @@ def set_config(
config_inst: Config, config_inst: Config,
key: str, key: str,
default: Any = None, default: Any = None,
coerce: bool = False,
): ):
config_var = key.lower().replace(".", "_") config_var = key.lower().replace(".", "_")
setattr(config_inst, config_var, config_inst.get_value(key, default, coerce)) setattr(config_inst, config_var, config_inst.get_value(key, default))

View File

@@ -8,6 +8,7 @@ import sys
from panaetius import Config from panaetius import Config
from panaetius.library import set_config from panaetius.library import set_config
from panaetius.exceptions import LoggingDirectoryDoesNotExistException
def set_logger(config_inst: Config, logging_format_inst: LoggingData) -> logging.Logger: def set_logger(config_inst: Config, logging_format_inst: LoggingData) -> logging.Logger:
@@ -22,6 +23,9 @@ def set_logger(config_inst: Config, logging_format_inst: LoggingData) -> logging
/ f"{config_inst.header_variable}.log" / f"{config_inst.header_variable}.log"
).expanduser() ).expanduser()
if not logging_file.parents[0].exists():
raise LoggingDirectoryDoesNotExistException()
if config_inst.logging_rotate_bytes == 0: if config_inst.logging_rotate_bytes == 0:
set_config(config_inst, "logging.rotate_bytes", 512000) set_config(config_inst, "logging.rotate_bytes", 512000)
if config_inst.logging_backup_count == 0: if config_inst.logging_backup_count == 0:

48
poetry.lock generated
View File

@@ -57,6 +57,17 @@ category = "dev"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "coverage"
version = "6.0.2"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
toml = ["tomli"]
[[package]] [[package]]
name = "dodgy" name = "dodgy"
version = "0.2.1" version = "0.2.1"
@@ -610,7 +621,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.7" python-versions = "^3.7"
content-hash = "bc75d0878aaf4033c2d9520333a559d29e81962a8a0138ed8861014d2fc77eac" content-hash = "468d1aa5e0c440262f6041ad859358a84ef32462941aa6f3ba71838a52cc1ced"
[metadata.files] [metadata.files]
astroid = [ astroid = [
@@ -633,6 +644,41 @@ colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
] ]
coverage = [
{file = "coverage-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1549e1d08ce38259de2bc3e9a0d5f3642ff4a8f500ffc1b2df73fd621a6cdfc0"},
{file = "coverage-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcae10fccb27ca2a5f456bf64d84110a5a74144be3136a5e598f9d9fb48c0caa"},
{file = "coverage-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:53a294dc53cfb39c74758edaa6305193fb4258a30b1f6af24b360a6c8bd0ffa7"},
{file = "coverage-6.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8251b37be1f2cd9c0e5ccd9ae0380909c24d2a5ed2162a41fcdbafaf59a85ebd"},
{file = "coverage-6.0.2-cp310-cp310-win32.whl", hash = "sha256:db42baa892cba723326284490283a68d4de516bfb5aaba369b4e3b2787a778b7"},
{file = "coverage-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:bbffde2a68398682623d9dd8c0ca3f46fda074709b26fcf08ae7a4c431a6ab2d"},
{file = "coverage-6.0.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:60e51a3dd55540bec686d7fff61b05048ca31e804c1f32cbb44533e6372d9cc3"},
{file = "coverage-6.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a6a9409223a27d5ef3cca57dd7cd4dfcb64aadf2fad5c3b787830ac9223e01a"},
{file = "coverage-6.0.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4b34ae4f51bbfa5f96b758b55a163d502be3dcb24f505d0227858c2b3f94f5b9"},
{file = "coverage-6.0.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3bbda1b550e70fa6ac40533d3f23acd4f4e9cb4e6e77251ce77fdf41b3309fb2"},
{file = "coverage-6.0.2-cp36-cp36m-win32.whl", hash = "sha256:4e28d2a195c533b58fc94a12826f4431726d8eb029ac21d874345f943530c122"},
{file = "coverage-6.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a82d79586a0a4f5fd1cf153e647464ced402938fbccb3ffc358c7babd4da1dd9"},
{file = "coverage-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3be1206dc09fb6298de3fce70593e27436862331a85daee36270b6d0e1c251c4"},
{file = "coverage-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9cd3828bbe1a40070c11fe16a51df733fd2f0cb0d745fb83b7b5c1f05967df7"},
{file = "coverage-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d036dc1ed8e1388e995833c62325df3f996675779541f682677efc6af71e96cc"},
{file = "coverage-6.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04560539c19ec26995ecfb3d9307ff154fbb9a172cb57e3b3cfc4ced673103d1"},
{file = "coverage-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:e4fb7ced4d9dec77d6cf533acfbf8e1415fe799430366affb18d69ee8a3c6330"},
{file = "coverage-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:77b1da5767ed2f44611bc9bc019bc93c03fa495728ec389759b6e9e5039ac6b1"},
{file = "coverage-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:61b598cbdbaae22d9e34e3f675997194342f866bb1d781da5d0be54783dce1ff"},
{file = "coverage-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36e9040a43d2017f2787b28d365a4bb33fcd792c7ff46a047a04094dc0e2a30d"},
{file = "coverage-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9f1627e162e3864a596486774876415a7410021f4b67fd2d9efdf93ade681afc"},
{file = "coverage-6.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e7a0b42db2a47ecb488cde14e0f6c7679a2c5a9f44814393b162ff6397fcdfbb"},
{file = "coverage-6.0.2-cp38-cp38-win32.whl", hash = "sha256:a1b73c7c4d2a42b9d37dd43199c5711d91424ff3c6c22681bc132db4a4afec6f"},
{file = "coverage-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:1db67c497688fd4ba85b373b37cc52c50d437fd7267520ecd77bddbd89ea22c9"},
{file = "coverage-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f2f184bf38e74f152eed7f87e345b51f3ab0b703842f447c22efe35e59942c24"},
{file = "coverage-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1cf1deb3d5544bd942356364a2fdc8959bad2b6cf6eb17f47d301ea34ae822"},
{file = "coverage-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ad9b8c1206ae41d46ec7380b78ba735ebb77758a650643e841dd3894966c31d0"},
{file = "coverage-6.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:381d773d896cc7f8ba4ff3b92dee4ed740fb88dfe33b6e42efc5e8ab6dfa1cfe"},
{file = "coverage-6.0.2-cp39-cp39-win32.whl", hash = "sha256:424c44f65e8be58b54e2b0bd1515e434b940679624b1b72726147cfc6a9fc7ce"},
{file = "coverage-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:abbff240f77347d17306d3201e14431519bf64495648ca5a49571f988f88dee9"},
{file = "coverage-6.0.2-pp36-none-any.whl", hash = "sha256:7092eab374346121805fb637572483270324407bf150c30a3b161fc0c4ca5164"},
{file = "coverage-6.0.2-pp37-none-any.whl", hash = "sha256:30922626ce6f7a5a30bdba984ad21021529d3d05a68b4f71ea3b16bda35b8895"},
{file = "coverage-6.0.2.tar.gz", hash = "sha256:6807947a09510dc31fa86f43595bf3a14017cd60bf633cc746d52141bfa6b149"},
]
dodgy = [ dodgy = [
{file = "dodgy-0.2.1-py3-none-any.whl", hash = "sha256:51f54c0fd886fa3854387f354b19f429d38c04f984f38bc572558b703c0542a6"}, {file = "dodgy-0.2.1-py3-none-any.whl", hash = "sha256:51f54c0fd886fa3854387f354b19f429d38c04f984f38bc572558b703c0542a6"},
{file = "dodgy-0.2.1.tar.gz", hash = "sha256:28323cbfc9352139fdd3d316fa17f325cc0e9ac74438cbba51d70f9b48f86c3a"}, {file = "dodgy-0.2.1.tar.gz", hash = "sha256:28323cbfc9352139fdd3d316fa17f325cc0e9ac74438cbba51d70f9b48f86c3a"},

View File

@@ -103,9 +103,9 @@ dodgy:
bandit: bandit:
run: true run: true
options: # options:
# ignore assert warning # ignore assert warning
B101 # - B101
mypy: mypy:
run: true run: true

View File

@@ -33,6 +33,7 @@ types-toml = "^0.10.1"
pytest = "^6.2.5" pytest = "^6.2.5"
pytest-datadir = "^1.3.1" pytest-datadir = "^1.3.1"
pytest-xdist = "^2.4.0" pytest-xdist = "^2.4.0"
coverage = "^6.0.2"
[build-system] [build-system]
requires = ["poetry>=0.12"] requires = ["poetry>=0.12"]

View File

@@ -1,3 +1,3 @@
; parallel tests with pytest-xdist ; ; parallel tests with pytest-xdist
[pytest] ; [pytest]
addopts=-n4 ; addopts=-n4

View File

@@ -14,8 +14,9 @@ Coding:
✔ Logging path should take by default the config path unless overwritten? @done(21-10-16 23:49) ✔ Logging path should take by default the config path unless overwritten? @done(21-10-16 23:49)
Errors: Errors:
Check logging path + config path are valid, if not raise error. Check logging path + config path are valid, if not raise error. @done(21-10-18 00:04)
Add tests for these. Add tests for these. @done(21-10-18 00:04)
✔ Check for a key > 2 levels, raise custom error, write test @done(21-10-17 23:30)
Linting: Linting:
☐ Check all functions and annotations. ☐ Check all functions and annotations.
@@ -23,21 +24,45 @@ Coding:
Docstrings: Docstrings:
☐ Write the docstrings for public functions/methods. ☐ Write the docstrings for public functions/methods.
Functionality:
☐ When both a config file and a env var is found, use the env var.
Documentation: Documentation:
☐ Rewrite documentation using `mkdocs` and using `.md`. @2h ☐ Rewrite documentation using `mkdocs` and using `.md`. @2h
Misc:
☐ Use the python runner to build the docs & run the tests
☐ document this in trilium
Tests: Tests:
Config File: Bugfixes:
✔ If loading from a default, don't covert to TOML @done(21-10-17 20:33)
✔ Env Vars should be given as python objects @done(21-10-17 20:33)
Environment Variable: The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None.
use ast.literal_eval()
https://docs.python.org/3/library/ast.html#ast.literal_eval
__init__: __init__:
Test default config path set to "~/.config" Test default config path set to "~/.config" @done(21-10-17 17:25)
Test config path is set when passed in Test config path is set when passed in @done(21-10-17 17:25)
config property: config property:
Check testing config file is returned as dict Check testing config file is returned as dict @done(21-10-17 17:25)
Check _self.missing_config and empty dict is returned Check _self.missing_config and empty dict is returned @done(21-10-17 17:25)
get_value:
config_file:
✔ Arrays & tables loaded correctly from config file @done(21-10-17 20:34)
✔ test when key length is 1 the value is returned @done(21-10-17 18:55)
✔ test when key length is 2 the value is returned @done(21-10-17 18:55)
✔ test when key not found and no env var default is loaded @done(21-10-17 19:01)
✔ test bool's are properly converted @done(21-10-17 19:01)
✔ test when key not found and env var is set value is loaded @done(21-10-17 20:43)
env_var:
✔ 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)
library:
✔ test set_config works @done(21-10-17 23:29)

View File

@@ -4,3 +4,20 @@ import pytest
@pytest.fixture() @pytest.fixture()
def header(): def header():
return "panaetius_testing" return "panaetius_testing"
@pytest.fixture()
def testing_config_contents():
return {
"panaetius_testing": {
"some_top_string": "some_top_value",
"second": {
"some_second_string": "some_second_value",
"some_second_int": 1,
"some_second_float": 1.0,
"some_second_list": ["some", "second", "value"],
"some_second_table": {"first": ["some", "first", "value"]},
"some_second_table_bools": {"bool": [True, False]},
},
}
}

View File

@@ -7,6 +7,4 @@ some_second_int = 1
some_second_float = 1.0 some_second_float = 1.0
some_second_list = ["some", "second", "value"] some_second_list = ["some", "second", "value"]
some_second_table = { "first" = ["some", "first", "value"] } some_second_table = { "first" = ["some", "first", "value"] }
some_second_table_bools = { "bool" = [true, false] }
# [panaetius_testing.logging]
# path = ""

View File

@@ -1,54 +1,57 @@
import os import os
from panaetius import Config, set_config, set_logger, SimpleLogger from panaetius import Config, set_config, set_logger, SimpleLogger
# from panaetius.logging import AdvancedLogger
from panaetius.logging import AdvancedLogger
if __name__ == "__main__": if __name__ == "__main__":
os.environ["PANAETIUS_TEST_PATH"] = "/usr/local" os.environ["PANAETIUS_TEST_PATH"] = '"/usr/local"'
os.environ["PANAETIUS_TEST_BOOL"] = "true" os.environ["PANAETIUS_TEST_BOOL"] = "True"
print(os.environ.get("PANAETIUS_TEST_PATH")) # print(os.environ.get("PANAETIUS_TEST_PATH"))
# os.environ[ # os.environ[
# "PANAETIUS_TEST_TOML_POINTS" # "PANAETIUS_TEST_TOML_POINTS"
# ] = "[ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 }]" # ] = "[ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 }]"
os.environ["PANAETIUS_TEST_NOC_PATH"] = "/usr/locals" os.environ["PANAETIUS_TEST_NOC_PATH"] = '"/usr/locals"'
os.environ["PANAETIUS_TEST_NOC_FLOAT"] = "2.0" os.environ["PANAETIUS_TEST_NOC_FLOAT"] = "2.0"
os.environ["PANAETIUS_TEST_NOC_BOOL"] = "true" os.environ["PANAETIUS_TEST_NOC_BOOL"] = "True"
os.environ["PANAETIUS_TEST_NOC_EMBEDDED_PATH"] = "/usr/local" os.environ["PANAETIUS_TEST_NOC_EMBEDDED_PATH"] = '"/usr/local"'
os.environ["PANAETIUS_TEST_NOC_EMBEDDED_FLOAT"] = "2.0" os.environ["PANAETIUS_TEST_NOC_EMBEDDED_FLOAT"] = "2.0"
os.environ["PANAETIUS_TEST_NOC_EMBEDDED_BOOL"] = "true" os.environ["PANAETIUS_TEST_NOC_EMBEDDED_BOOL"] = "True"
# c = Config("panaetius_test") c = Config("panaetius_test")
c = Config("panaetius_test_noc") # c = Config("panaetius_test_noc")
set_config(c, key="toml.points", coerce=True) set_config(c, key="toml.points")
set_config(c, key="path", default="some path") set_config(c, key="path", default="some path")
set_config(c, key="top", default="some top") set_config(c, key="top", default="some top")
set_config(c, key="logging.path") set_config(c, key="logging.path")
set_config(c, key="nonexistent.item", default="some nonexistent item") set_config(c, key="nonexistent.item", default="some nonexistent item")
set_config(c, key="nonexistent.item") set_config(c, key="nonexistent.item")
set_config(c, key="toml.points_config") set_config(c, key="toml.points_config")
set_config(c, key="float", coerce=True) set_config(c, key="float")
set_config(c, key="float_str", default="2.0") set_config(c, key="float_str", default="2.0")
set_config(c, key="bool", coerce=True) set_config(c, key="bool")
set_config(c, key="noexistbool", default=False) set_config(c, key="noexistbool", default=False)
set_config(c, key="middle.middle") set_config(c, key="middle.middle")
# set_config(c, key="path") # set_config(c, key="path")
# set_config(c, key="float", coerce=True) # set_config(c, key="float")
# set_config(c, key="bool", coerce=True) # set_config(c, key="bool")
# set_config(c, key="noexiststr", default="2.0") # set_config(c, key="noexiststr", default="2.0")
# set_config(c, key="noexistfloat", default=2.0) # set_config(c, key="noexistfloat", default=2.0)
# set_config(c, key="noexistbool", default=False) # set_config(c, key="noexistbool", default=False)
set_config(c, key="embedded.path") set_config(c, key="embedded.path")
set_config(c, key="embedded.float", coerce=True) set_config(c, key="embedded.float")
set_config(c, key="embedded.bool", coerce=True) set_config(c, key="embedded.bool")
set_config(c, key="embedded.noexiststr", default="2.0") set_config(c, key="embedded.noexiststr", default="2.0")
set_config(c, key="embedded.noexistfloat", default=2.0) set_config(c, key="embedded.noexistfloat", default=2.0)
set_config(c, key="embedded.noexistbool", default=False) set_config(c, key="embedded.noexistbool", default=False)
logger = set_logger(c, SimpleLogger()) # logger = set_logger(c, SimpleLogger())
# logger = set_logger(c, AdvancedLogger(logging_level="INFO")) logger = set_logger(c, AdvancedLogger(logging_level="DEBUG"))
logger.info("test logging message") logger.info("test logging message")
logger.debug("debugging message") logger.debug("debugging message")
for i in dir(c):
logger.debug(i + ": " + str(getattr(c, i)) + " - " + str(type(getattr(c, i))))

View File

@@ -1,8 +1,13 @@
import os
import pathlib import pathlib
from uuid import uuid4
import toml import pytest
import panaetius import panaetius
from panaetius.exceptions import KeyErrorTooDeepException
# test config paths
def test_default_config_path_set(header): def test_default_config_path_set(header):
@@ -13,9 +18,9 @@ def test_default_config_path_set(header):
assert str(config.config_path) == str(pathlib.Path.home() / ".config") assert str(config.config_path) == str(pathlib.Path.home() / ".config")
def test_user_config_path_set(header, datadir): def test_user_config_path_set(header, shared_datadir):
# arrange # arrange
config_path = str(datadir / "without_logging") config_path = str(shared_datadir / "without_logging")
# act # act
config = panaetius.Config(header, config_path) config = panaetius.Config(header, config_path)
@@ -24,13 +29,182 @@ def test_user_config_path_set(header, datadir):
assert str(config.config_path) == config_path assert str(config.config_path) == config_path
def test_config_file_exists(header, datadir): # test config files
def test_config_file_exists(header, shared_datadir):
# arrange # arrange
config_path = str(datadir / "without_logging") config_path = str(shared_datadir / "without_logging")
# act
config = panaetius.Config(header, config_path)
_ = config.config
# assert
assert config._missing_config is False
def test_config_file_contents_read_success(header, shared_datadir, testing_config_contents):
# arrange
config_path = str(shared_datadir / "without_logging")
# act # act
config = panaetius.Config(header, config_path) config = panaetius.Config(header, config_path)
config_contents = config.config config_contents = config.config
# assert # assert
assert config._missing_config == False assert config_contents == testing_config_contents
@pytest.mark.parametrize(
"set_config_key,get_config_key,expected_value",
[
("some_top_string", "some_top_string", "some_top_value"),
("second.some_second_string", "second_some_second_string", "some_second_value"),
(
"second.some_second_list",
"second_some_second_list",
["some", "second", "value"],
),
(
"second.some_second_table",
"second_some_second_table",
{"first": ["some", "first", "value"]},
),
(
"second.some_second_table_bools",
"second_some_second_table_bools",
{"bool": [True, False]},
),
],
)
def test_get_value_from_key(
set_config_key, get_config_key, expected_value, header, shared_datadir
):
"""
Test the following:
- keys are read from top level key
- keys are read from two level key
- inline arrays are read correctly
- inline tables are read correctly
- inline tables & arrays read bools correctly
"""
# arrange
config_path = str(shared_datadir / "without_logging")
config = panaetius.Config(header, config_path)
panaetius.set_config(config, set_config_key)
# act
config_value = getattr(config, get_config_key)
# assert
assert config_value == expected_value
def test_key_level_too_deep(header, shared_datadir):
# arrange
config_path = str(shared_datadir / "without_logging")
config = panaetius.Config(header, config_path)
key = "a.key.too.deep"
# act
with pytest.raises(KeyErrorTooDeepException) as key_error_too_deep:
panaetius.set_config(config, key)
# assert
assert (
str(key_error_too_deep.value)
== f"Your key of {key} can only be 2 levels deep maximum. "
f"You have 4"
)
def test_get_value_missing_key_from_default(header, shared_datadir):
# arrange
config_path = str(shared_datadir / "without_logging")
config = panaetius.Config(header, config_path)
panaetius.set_config(
config,
"missing.key_from_default",
default=["some", "default", "value", 1.0, True],
)
# act
default_value = getattr(config, "missing_key_from_default")
# assert
assert default_value == ["some", "default", "value", 1.0, True]
def test_get_value_missing_key_from_env(header, shared_datadir):
# arrange
os.environ[f"{header.upper()}_MISSING_KEY"] = '"some missing key"'
config_path = str(shared_datadir / "without_logging")
config = panaetius.Config(header, config_path)
panaetius.set_config(config, "missing_key")
# act
value_from_key = getattr(config, "missing_key")
# assert
assert value_from_key == "some missing key"
# test env vars
def test_config_file_does_not_exist(header, shared_datadir):
# arrange
config_path = str(shared_datadir / "nonexistent_folder")
# act
config = panaetius.Config(header, config_path)
config_contents = config.config
# assert
assert config._missing_config is True
assert config_contents == {}
def test_missing_config_read_from_default(header, shared_datadir):
# arrange
config_path = str(shared_datadir / "nonexistent_folder")
# act
config = panaetius.Config(header, config_path)
panaetius.set_config(config, "missing.key_read_from_default", default=True)
# assert
assert getattr(config, "missing_key_read_from_default") is True
@pytest.mark.parametrize(
"env_value,expected_value",
[
('"a missing string"', "a missing string"),
("1", 1),
("1.0", 1.0),
("True", True),
(
'["an", "array", "of", "items", 1, True]',
["an", "array", "of", "items", 1, True],
),
(
'{"an": "array", "of": "items", "1": True}',
{"an": "array", "of": "items", "1": True},
),
],
)
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
# act
config = panaetius.Config(header, config_path)
panaetius.set_config(config, "missing.key_read_from_env_var")
# assert
assert getattr(config, "missing_key_read_from_env_var") == expected_value

13
tests/test_library.py Normal file
View File

@@ -0,0 +1,13 @@
import panaetius
def test_set_config(header, shared_datadir):
# arrange
config_path = str(shared_datadir / "without_logging")
# act
config = panaetius.Config(header, config_path)
panaetius.set_config(config, "some_top_string")
# assert
assert getattr(config, "some_top_string") == "some_top_value"

34
tests/test_logging.py Normal file
View File

@@ -0,0 +1,34 @@
import logging
from uuid import uuid4
import pytest
from panaetius import set_logger, SimpleLogger, Config, set_config
from panaetius.exceptions import LoggingDirectoryDoesNotExistException
def test_logging_directory_does_not_exist(header, shared_datadir):
# arrange
config = Config(header)
logging_path = str(shared_datadir / str(uuid4()))
set_config(config, "logging.path", default=str(logging_path))
# act
with pytest.raises(LoggingDirectoryDoesNotExistException) as logging_exception:
_ = set_logger(config, SimpleLogger())
# assert
assert str(logging_exception.value) == ""
def test_logging_directory_does_exist(header, shared_datadir):
# arrange
config = Config(header)
logging_path = str(shared_datadir / "without_logging")
set_config(config, "logging.path", default=str(logging_path))
# act
logger = set_logger(config, SimpleLogger())
# assert
assert isinstance(logger, logging.Logger)