adding latest testing + docstrings

This commit is contained in:
2021-10-18 02:31:17 +01:00
parent 9299a12eb6
commit ad840e6b27
8 changed files with 161 additions and 20 deletions

View File

@@ -1,3 +1,9 @@
"""
panaetius - a utility library to read variables and provide convenient logging.
Author: Daniel Tomlinson (dtomlinson@panaetius.co.uk)
"""
from panaetius.config import Config from panaetius.config import Config
from panaetius.library import set_config from panaetius.library import set_config
from panaetius.logging import set_logger, SimpleLogger, AdvancedLogger, CustomLogger from panaetius.logging import set_logger, SimpleLogger, AdvancedLogger, CustomLogger

View File

@@ -1,3 +1,10 @@
"""
Access variables from a config file or an environment variable.
This module defines the `Config` class to interact and read variables from either a
`config.toml` or an environment variable.
"""
from __future__ import annotations from __future__ import annotations
import ast import ast
@@ -11,9 +18,21 @@ from panaetius.exceptions import KeyErrorTooDeepException, InvalidPythonExceptio
class Config: class Config:
"""docstring for Config().""" """The configuration class to access variables."""
def __init__(self, header_variable: str, config_path: str = "") -> None: def __init__(self, header_variable: str, config_path: str = "") -> None:
"""
Create a Config object to set and access variables.
Args:
header_variable (str): Your header variable name.
config_path (str, optional): The path where the header directory is stored.
Defaults to `~/.config`.
Example:
A header of `data_analysis` with a config_path of `~/myapps` will define
a config file in `~/myapps/data_analysis/config.toml`.
"""
self.header_variable = header_variable self.header_variable = header_variable
self.config_path = ( self.config_path = (
pathlib.Path(config_path) pathlib.Path(config_path)
@@ -29,6 +48,12 @@ class Config:
@property @property
def config(self) -> dict: def config(self) -> dict:
"""
Return the contents of the config file. If missing returns an empty dictionary.
Returns:
dict: The contents of the `.toml` loaded as a python dictionary.
"""
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:
@@ -37,6 +62,36 @@ class Config:
return {} return {}
def get_value(self, key: str, default: Any) -> Any: def get_value(self, key: str, default: Any) -> Any:
"""
Get the value of a variable from the key name.
The key can either be one (`value`) or two (`data.value`) levels deep.
A key of (`value`) (with a header of `data_analysis`) would refer to a
`config.toml` of:
```
[data_analysis]
value = "some value"
```
or an environment variable of `DATA_ANALYSIS_VALUE="'some value'"`.
A key of (`data.value`) would refer to a `config.toml` of:
```
[data_analysis.data]
value = "some value"
```
or an environment variable of `DATA_ANALYSIS_DATA_VALUE="'some value'"`.
Args:
key (str): The key of the variable.
default (Any): The default value if the key cannot be found in the config
file, or an environment variable.
Returns:
Any: The value of the variable.
"""
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:

View File

@@ -1,3 +1,6 @@
"""Exceptions for the module."""
class KeyErrorTooDeepException(Exception): class KeyErrorTooDeepException(Exception):
pass pass
@@ -5,5 +8,6 @@ class KeyErrorTooDeepException(Exception):
class LoggingDirectoryDoesNotExistException(Exception): class LoggingDirectoryDoesNotExistException(Exception):
pass pass
class InvalidPythonException(Exception): class InvalidPythonException(Exception):
pass pass

View File

@@ -1,3 +1,5 @@
"""Module to provide functionality when interacting with variables."""
from __future__ import annotations from __future__ import annotations
from typing import Any from typing import Any
@@ -9,6 +11,33 @@ def set_config(
config_inst: Config, config_inst: Config,
key: str, key: str,
default: Any = None, default: Any = None,
): ) -> None:
"""
Define a variable to be read from a `config.toml` or an environment variable.
Args:
config_inst (Config): The instance of the `Config` class.
key (str): The key of the variable.
default (Any, optional): The default value if the key cannot be found in the config
file, or an environment variable. Defaults to None.
Example:
`set_config(CONFIG, "value", default=[1, 2])` would look for a
`config.toml` with the following structure (with `CONFIG` having a header of
`data_analysis`):
```
[data_analysis]
value = "some value"
```
Or an environment variable of `DATA_ANALYSIS_VALUE="'some value'"`.
If found, this value can be access with `CONFIG.value` which would return
`some_value`.
If neither the environment variable nor the `config.toml` are present, the
default of `[1, 2]` would be returned instead.
"""
config_var = key.lower().replace(".", "_") config_var = key.lower().replace(".", "_")
setattr(config_inst, config_var, config_inst.get_value(key, default)) setattr(config_inst, config_var, config_inst.get_value(key, default))

View File

@@ -1,3 +1,5 @@
"""Module to define a convenient logger instance with json formatted output."""
from __future__ import annotations from __future__ import annotations
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
@@ -12,6 +14,50 @@ 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:
"""
Set and return a `logging.Logger` instance for quick logging.
`logging_format_inst` should be an instance of either SimpleLogger, AdvancedLogger,
or CustomLogger.
SimpleLogger and AdvancedLogger define a logging format and a logging level info.
CustomLogger defines a logging level info and should have a logging format passed
in.
Logging to a file is defined by a `logging.path` key set on `Config`. This path
should exist as it will not be created.
Args:
config_inst (Config): The instance of the `Config` class.
logging_format_inst (LoggingData): The instance of the `LoggingData` class.
Raises:
LoggingDirectoryDoesNotExistException: If the logging directory specified does
not exist.
Returns:
logging.Logger: An configured instance of `logging.Logger` ready to be used.
Example:
```
logger = set_logger(CONFIG, SimpleLogger())
logger.info("some logging message")
```
Would create a logging output of:
```
{
"time": "2021-10-18 02:26:24,037",
"logging_level":"INFO",
"message": "some logging message"
}
```
"""
logger = logging.getLogger(config_inst.header_variable) logger = logging.getLogger(config_inst.header_variable)
log_handler_sys = logging.StreamHandler(sys.stdout) log_handler_sys = logging.StreamHandler(sys.stdout)

View File

@@ -12,9 +12,9 @@ pylint:
# disables TODO warnings # disables TODO warnings
- fixme - fixme
# !doc docstrings # !doc docstrings
- missing-module-docstring # - missing-module-docstring
- missing-class-docstring # - missing-class-docstring
- missing-function-docstring # - missing-function-docstring
# ! doc end of docstrings # ! doc end of docstrings
# disables warnings about abstract methods not overridden # disables warnings about abstract methods not overridden
- abstract-method - abstract-method
@@ -67,23 +67,24 @@ pep257:
disable: disable:
# !doc docstrings # !doc docstrings
# Missing docstring in __init__ # Missing docstring in __init__
- D107 # - D107
# Missing docstring in public module # Missing docstring in public module
- D100 # - D100
# Missing docstring in public class # Missing docstring in public class
- D101 # - D101
# Missing docstring in public method # Missing docstring in public method
- D102 # - D102
# Missing docstring in public function # Missing docstring in public function
- D103 # - D103
# Multi-line docstring summary should start at the second line
# - D213
# First word of the docstring should not be This
# - D404
# DEFAULT IGNORES
# 1 blank line required before class docstring # 1 blank line required before class docstring
- D203 - D203
# Multi-line docstring summary should start at the first line # Multi-line docstring summary should start at the first line
- D212 - D212
# Multi-line docstring summary should start at the second line
- D213
# First word of the docstring should not be This
- D404
# !doc end of docstrings # !doc end of docstrings
# Section name should end with a newline # Section name should end with a newline
- D406 - D406

View File

@@ -19,10 +19,10 @@ Coding:
✔ Check for a key > 2 levels, raise custom error, write test @done(21-10-17 23:30) ✔ 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. @done(21-10-18 01:07)
Docstrings: Docstrings:
Write the docstrings for public functions/methods. Write the docstrings for public functions/methods. @done(21-10-18 02:29)
Functionality: Functionality:
✔ When both a config file and a env var is found, use the env var. @done(21-10-18 00:38) ✔ When both a config file and a env var is found, use the env var. @done(21-10-18 00:38)

View File

@@ -50,8 +50,8 @@ if __name__ == "__main__":
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="DEBUG")) logger = set_logger(c, SimpleLogger(logging_level="DEBUG"))
logger.info("test logging message") logger.info("some logging message")
logger.debug("debugging message") logger.debug("debugging message")
for i in dir(c): # for i in dir(c):
logger.debug(i + ": " + str(getattr(c, i)) + " - " + str(type(getattr(c, i)))) # logger.debug(i + ": " + str(getattr(c, i)) + " - " + str(type(getattr(c, i))))