22 Commits

Author SHA1 Message Date
bd1aa09b4c Merge branch 'develop' 2021-11-14 09:12:31 +00:00
b0d635eb04 bumping to v2.3.2 2021-11-14 09:12:26 +00:00
1d72b976a4 removing old docs, adding requirements 2021-11-14 08:53:11 +00:00
4f93519c41 Merge branch 'develop' 2021-11-14 08:33:29 +00:00
3a2a8951a7 updating docstrings, adding duty, updating README 2021-11-14 08:33:22 +00:00
89655d46ae patching to 2.2.2 2021-10-23 21:10:11 +01:00
bbc580424c Merge branch 'develop' 2021-10-23 21:08:37 +01:00
8add8aaefd Merge branch 'feature/skip_header_in_config_init' into develop 2021-10-23 21:08:27 +01:00
1af790f01a updating tests 2021-10-23 21:08:16 +01:00
485ab9ef09 change abc to raise NotImplementedError for tests 2021-10-23 21:08:07 +01:00
844a2f6f3f changing env var to use strings without extra quotes 2021-10-23 21:07:31 +01:00
2092245dad adding skip header directory option 2021-10-23 21:07:07 +01:00
16f753fdf3 updating todo 2021-10-23 21:05:36 +01:00
9f1caf79ff patch - v2.2.1 2021-10-23 05:06:10 +01:00
70911f98b0 add expand_user to Config 2021-10-23 05:05:46 +01:00
441a26127f remove DS_Store 2021-10-23 05:05:35 +01:00
9cc6f2483d bumping to 2.2.0 2021-10-22 22:45:13 +01:00
6e24f9d70b adding Squash to __init__.py 2021-10-22 22:44:50 +01:00
8c18d01f05 bumping version to 2.1 2021-10-20 22:29:19 +01:00
d7700c4863 adding squasher utility 2021-10-20 22:29:08 +01:00
948bc65e76 removing old files 2021-10-20 22:26:46 +01:00
a0627a0922 Merge branch 'feature/toml_to_yaml' into develop 2021-10-19 21:46:49 +01:00
43 changed files with 804 additions and 820 deletions

BIN
.DS_Store vendored

Binary file not shown.

7
.coveragerc Normal file
View File

@@ -0,0 +1,7 @@
[report]
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain if tests don't hit defensive assertion code:
raise NotImplementedError

3
.gitignore vendored
View File

@@ -127,3 +127,6 @@ dmypy.json
# Pyre type checker
.pyre/
# custom
.DS_Store

81
README.md Normal file
View File

@@ -0,0 +1,81 @@
# Panaetius
This package provides:
- Functionality to read user variables from a `config.yml` or environment variables.
- A convenient default logging formatter printing `json` that can save to disk and rotate.
- Utility functions.
## Config
### options
#### skip_header_init
If `skip_header_init=True` then the `config_path` will not use the `header_variable` as the
sub-directory in the `config_path`.
E.g
`CONFIG = panaetius.Config("tembo", "~/tembo/.config", skip_header_init=True)`
Will look in `~/tembo/config/config.yml`.
If `skip_header_init=False` then would look in `~/tembo/config/tembo/config.yml`.
### Module
Convenient to place in a package/sub-package `__init__.py`.
See Tembo for an example: <https://github.com/tembo-pages/tembo-core/blob/main/tembo/cli/__init__.py>
Example snippet to use in a module:
```python
import os
import panaetius
from panaetius.exceptions import LoggingDirectoryDoesNotExistException
if (config_path := os.environ.get("TEMBO_CONFIG")) is not None:
CONFIG = panaetius.Config("tembo", config_path, skip_header_init=True)
else:
CONFIG = panaetius.Config("tembo", "~/tembo/.config", skip_header_init=True)
panaetius.set_config(CONFIG, "base_path", "~/tembo")
panaetius.set_config(CONFIG, "template_path", "~/tembo/.templates")
panaetius.set_config(CONFIG, "scopes", {})
panaetius.set_config(CONFIG, "logging.level", "DEBUG")
panaetius.set_config(CONFIG, "logging.path")
try:
logger = panaetius.set_logger(
CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level)
)
except LoggingDirectoryDoesNotExistException:
_LOGGING_PATH = CONFIG.logging_path
CONFIG.logging_path = ""
logger = panaetius.set_logger(
CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level)
)
logger.warning("Logging directory %s does not exist", _LOGGING_PATH)
```
This means in `./tembo/cli/cli.py` you can
```python
import tembo.cli
# access the CONFIG instance + variables from the config.yml
tembo.cli.CONFIG
```
## Utility Functions
### Squasher
Squashes a json object or Python dictionary into a single level dictionary.

View File

@@ -1,33 +0,0 @@
# Author
Daniel Tomlinson (dtomlinson@panaetius.co.uk)
# Requires
`>= python3.7`
# Python requirements
- toml = "^0.10.0"
- pylite = "^0.1.0"
# Documentation
_soon_
# Installation
_soon_
# Easy Way
## Python
### From pip
### From local wheel
### From source
# Example Usage

View File

@@ -1,62 +0,0 @@
Author
=======
Daniel Tomlinson (dtomlinson@panaetius.co.uk)
Requires
=========
`>= python3.7`
Python requirements
====================
- toml = "^0.10.0"
- pylite = "^0.1.0"
Documentation
==============
Read the documentation on `read the docs`_.
.. _read the docs: https://panaetius.readthedocs.io/en/latest/introduction.html
Installation
==============
You can install ``panaetius`` the following ways:
Python
-------
.. Attention:: You should install in a python virtual environment
From pypi using pip
~~~~~~~~~~~~~~~~~~~~
.. code-block:: bash
pip install panaetius
From local wheel
~~~~~~~~~~~~~~~~~
Download the latest verion from the `releases`_ page.
.. _releases: https://github.com/dtomlinson91/panaetius/releases
Install with pip:
.. code-block:: bash
pip install -U panaetius-1.0.2-py3-none-any.whl
From source
~~~~~~~~~~~~
Clone the repo and install using ``setup.py``:
.. code-block:: bash
python setup.py

3
TODO
View File

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

View File

@@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@@ -1,35 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View File

@@ -1,87 +0,0 @@
@import url("css/theme.css");
.modname {
font-size: 0.8em;
opacity: 0.4;
}
.modname::before {
content: '- ';
}
.title {
font-weight: bold;
font-size: 1.2em;
background-color: #eee;
display: block;
padding: 1px 5px;
border-left: 2px solid #ddd;
}
/*colour of the text in the toc*/
.wy-menu-vertical header, .wy-menu-vertical p.caption{
color: #b26d46;;
}
/*colour of the top left header*/
.wy-side-nav-search{
background-color: #31465a;
}
/*colours of the class definitions*/
.rst-content dl:not(.docutils) dt{
background: #e2d7d1;
color: #0b2852;
border-top: solid 3px #31465a;
}
/*colour of the link in the class defintions*/
.rst-content .viewcode-link, .rst-content .viewcode-back{
color: #4b674a;
}
/*colour of the function definitions*/
.rst-content dl:not(.docutils) dl dt{
border-left: solid 3px #31465a;
background: #e2d7d1;
color: #0b2852;
}
/*colour of the link in the function definitions*/
.rst-content .viewcode-link, .rst-content .viewcode-back{
color: #4b674a;
}
/*edit the width of the body*/
.wy-nav-content{
max-width: 1200px;
}
/*code example blocks*/
.rst-content pre.literal-block, .rst-content div[class^="highlight"] pre, .rst-content .linenodiv pre{
background: #b4bec8;
color: black;
/*border-style: solid;*/
/*border-width: thin;*/
}
/*colour of inline code blocks using ``*/
.rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal{
color: #b26d46;
}
/* Change code blocks font and bump up font size slightly (normally 12px)*/
.rst-content pre.literal-block, .rst-content div[class^="highlight"] pre, .rst-content .linenodiv pre {
font-family: 'Inconsolata', monospace !important;
font-size: 14px !important;
white-space: pre-wrap;
}
/* Change code descriptions and literal blocks (inline code via ``) to match the normal font size being used in the sphinx_RTD_theme text (normally 14px)*/
.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) code.descclassname, code.docutils {
font-family: 'Inconsolata', monospace !important;
font-size: 14px !important;
}
/*variables text*/
dl.class > dd > table.docutils.field-list tbody tr.field-odd.field th.field-name::before{
content: '(Class Attributes) ';
}

View File

@@ -1,17 +0,0 @@
Version history
================
1.1
----
- Adding overwrite to ``__header__`` functionality. See the Configuration documentation page on how to configure.
1.0.2
------
- Minor fixes and documentation updates.
1.0
--------
- Initial release.

View File

@@ -1,95 +0,0 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
import panaetius
from panaetius.__version__ import __version__ as version
import sphinx_rtd_theme
# -- Project information -----------------------------------------------------
project = 'panaetius'
copyright = '2019, Daniel Tomlinson'
author = 'Daniel Tomlinson'
# The full version, including alpha/beta/rc tags
release = version
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.napoleon',
'sphinx.ext.todo',
]
# -- Napoleon Settings -----------------------------------------------------
napoleon_google_docstring = False
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = True
napoleon_include_private_with_doc = False
napoleon_include_special_with_doc = False
napoleon_use_admonition_for_examples = False
napoleon_use_admonition_for_notes = False
napoleon_use_admonition_for_references = False
napoleon_use_ivar = True
napoleon_use_param = True
napoleon_use_rtype = True
napoleon_use_keyword = True
autodoc_member_order = 'bysource'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The master toctree document.
master_doc = 'index'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_static_path = ['_static']
html_context = {'css_files': ['_static/custom.css']}
html_theme_options = {
'collapse_navigation': True,
'display_version': True,
'prev_next_buttons_location': 'both',
#'navigation_depth': 3,
}
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# Enable todo
todo_include_todos = True

View File

@@ -1,68 +0,0 @@
Configuration
=============
panaetius is fairly easy to configure. There are just a couple of options to be aware of.
Manual configuration of ``Config`` instance
--------------------------------------------
Configuring with a ``__header__.py`` is deprecated. Manually set this value.
Use the following snippet to configure (in ``__init__.py``):
.. code-block:: python
import panaetius
from panaetius.config import Config
CONFIG = Config(path="~/.config/island-code-extractor", header="island-code-extractor")
panaetius.set_config(CONFIG, "reddit.secret")
Access this in your code by importing the ``CONFIG`` instance from your module:
.. code-block:: python
from island_code_extractor import CONFIG
from island_code_extractor import panaetius
CONFIG.reddit_output_path
panaetius.logger.info("Using logger")
__header__.py
-------------
You should set a ``__header__.py`` next to your script or module.
This ``__header__.py`` should contain a ``__header__`` variable that sets the name of your project/script.
E.g a ``__header__.py`` for the module ``plex_posters`` would look like:
.. code-block:: python
__header__ = 'plex_posters'
Your config file can then be created at ``~/.config/__header__/config.toml``.
Your environment variables can be created with:
.. code-block:: bash
HEADER_FOO = "bar"
HEADER_SUBSECTION_FOO = "bar"
The headers of the toml file would look like:
.. code-block:: toml
[__header__]
foo = bar
[__header__.subsection]
foo = bar
If you are writing a script, simply place this ``__header__.py`` along side your script. Panaetius will pick this up when the script is ran.
If you are writing a module, you can either place the ``__header__.py`` alongside the script that uses your module. If this is not possible, panaetius will set the default ``__header__`` variable to the name of the virtualenv that the script is activated from.
If neither of the above aren't possible (say your script is running in a lambda on AWS), then ``__header__`` will be set to the default of ``panaetius``.

View File

@@ -1,5 +0,0 @@
.. role:: modname
:class: modname
.. role:: title
:class: title

View File

@@ -1,3 +0,0 @@
Table of Contents
=================
.. include:: toc.rst

View File

@@ -1,154 +0,0 @@
Introduction
=============
.. image:: https://img.shields.io/readthedocs/panaetius?style=for-the-badge :target: https://panaetius.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. image:: https://img.shields.io/github/v/tag/dtomlinson91/panaetius?style=for-the-badge :alt: GitHub tag (latest by date)
.. image:: https://img.shields.io/github/commit-activity/m/dtomlinson91/panaetius?style=for-the-badge :alt: GitHub commit activity
.. image:: https://img.shields.io/github/issues/dtomlinson91/panaetius?style=for-the-badge :alt: GitHub issues
.. image:: https://img.shields.io/github/license/dtomlinson91/panaetius?style=for-the-badge :alt: GitHubtbc
Python module to gracefully handle a .config file/environment variables for scripts, with built in masking for sensitive options. Provides a Splunk friendly formatted logger instance.
Usage
------
Setting a config file
~~~~~~~~~~~~~~~~~~~~~~
The main functionality of ``panaetius`` is using a config file to store variables.
Your ``config.toml`` can be created and found in ``~/.config/__header__/config.toml`` where ``__header__`` is equal to the variable configured/set. `See how to configure`_ this variable in the configuration section of panaetius.
.. _See how to configure: https://panaetius.readthedocs.io/en/latest/configuration.html#header-py
Setting values in a config.toml/environment variables
#######################################################
A ``config.toml`` can be created in the default folder for the module. In this example this would be found in ``~/.config/example_module/config.toml``.
An example ``config.toml`` could look like:
.. code-block:: toml
[example_module]
test = "a6cbf36649b029f3618a0cc1"
[example_module.logging]
path = "~/.config/example_module"
level = "DEBUG"
[example_module.foo]
bar = "6b3b96815218960ceaf7cceb"
These are equivalent to the environment variables:
.. code-block:: bash
EXAMPLE_MODULE_TEST
EXAMPLE_MODULE_LOGGING_PATH
EXAMPLE_MODULE_LOGGING_LEVEL
EXAMPLE_MODULE_FOO_BAR
.. Attention::
Environment variables take precedent over the ``config.toml``. If both are set then the environment variable will be used.
You can overwrite the ``config.toml`` location by setting the environment variable:
.. code-block:: bash
DEFAULT_CONFIG_PATH = "~/path/to/config"
Setting values in your code
############################
Values in a ``config.toml`` or from an environment variable need to be set in your work in order for you to use them. You can do this easily by
- importing panaetius.
- using the :func:`~panaetius.library.set_config` function.
E.g your script could contain:
.. code-block:: python
import panaetius
panaetius.set_config(panaetius.CONFIG, 'logging.path')
.. Note::
The ``key`` attribute in :func:`~panaetius.library.set_config` is specified as a string, with the hirearchy in the config file split with a ``.``
.. Important::
The default value for a variable defined using :func:`~panaetius.library.set_config` is ``None``. See the documentation of this function to see all the options available.
Accessing values
#################
You can then access the result of this variable later in your code:
.. code-block:: python
panaetius.CONFIG.logging_path
Logging
~~~~~~~~
In order to save to disk, you need to specify a path for the log file in the config file/environment variable. There is no need to register this with :func:`~panaetius.library.set_config` as ``panaetius`` will do this automatically.
There are other options available for you to configure a logger. These are (including the default values which can be overwritten):
.. code-block:: toml
[example_module.logging]
backup_count = 3
format = "{\n\t"time": "%(asctime)s",\n\t"file_name": "%(filename)s",'
'\n\t"module": "%(module)s",\n\t"function":"%(funcName)s",\n\t'
'"line_number": "%(lineno)s",\n\t"logging_level":'
'"%(levelname)s",\n\t"message": "%(message)s"\n}"
level = "INFO" # Level should be in CAPS
rotate_bytes = 512000
You can use the logger in your code by:
.. code-block:: python
panaetius.logger.info('some log message')
which gives an output of:
.. code-block:: json
{
"time": "2020-01-13 23:07:17,913",
"file_name": "test.py",
"module": "test",
"function":"<module>",
"line_number": "33",
"logging_level":"INFO",
"message": "some logging message"
}
Importing and using the api
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See `panaetius api page`_ on how to use and import the module.
.. _panaetius api page: https://panaetius.readthedocs.io/en/latest/modules/panaetius.html
Configuration
---------------
See `configuration page`_ on how to configure ``panaetius``.
.. _configuration page: https://panaetius.readthedocs.io/en/latest/configuration.html

View File

@@ -1,9 +0,0 @@
.. include:: ../global.rst
panaetius.config :modname:`panaetius.config`
---------------------------------------------
.. automodule:: panaetius.config
:members:
:undoc-members:
:show-inheritance:

View File

@@ -1,9 +0,0 @@
.. include:: ../global.rst
panaetius.config_inst :modname:`panaetius.config_inst`
--------------------------------------------------------
.. automodule:: panaetius.config_inst
:members:
:undoc-members:
:show-inheritance:

View File

@@ -1,9 +0,0 @@
.. include:: ../global.rst
panaetius.db :modname:`panaetius.db`
-------------------------------------
.. automodule:: panaetius.db
:members:
:undoc-members:
:show-inheritance:

View File

@@ -1,9 +0,0 @@
.. include:: ../global.rst
panaetius.header :modname:`panaetius.header`
---------------------------------------------
.. automodule:: panaetius.header
:members:
:undoc-members:
:show-inheritance:

View File

@@ -1,9 +0,0 @@
.. include:: ../global.rst
panaetius.library :modname:`panaetius.library`
------------------------------------------------
.. automodule:: panaetius.library
:members:
:undoc-members:
:show-inheritance:

View File

@@ -1,9 +0,0 @@
.. include:: ../global.rst
panaetius.logging :modname:`panaetius.logging`
----------------------------------------------
.. automodule:: panaetius.logging
:members:
:undoc-members:
:show-inheritance:

View File

@@ -1,66 +0,0 @@
.. include:: ../global.rst
*********
panaetius
*********
API
===
The following is availble by importing the module:
.. code-block:: python
import panaetius
panaetius.CONFIG
----------------
:obj:`panaetius.CONFIG` provides an instance of :class:`panaetius.config.Config`
panaetius.set_config()
-----------------------
Conveniently provides :func:`panaetius.library.set_config`
Use in your module/script with:
.. code-block:: python
panaetius.set_config(panaetius.CONFIG, 'aws.secret_key', str, mask=True)
panaetius.CONFIG.aws_secret_key
-------------------------------
Conveniently provides access to all attributes that have been declared with :func:`panaetius.library.set_config`:
.. code-block:: python
my_secret_key = panaetius.CONFIG.aws_secret_key
panaetius.logger
-----------------
:obj:`panaetius.logger` provides a logger instance already formatted with a nice json output.
.. code-block:: python
panaetius.logger.info('some logging message')
This gives a logger output of:
.. code-block:: json
{
"time": "2020-01-13 23:07:17,913",
"file_name": "test.py",
"module": "test",
"function":"<module>",
"line_number": "33",
"logging_level":"INFO",
"message": "some logging message"
}

View File

@@ -1,27 +0,0 @@
.. toctree::
:maxdepth: 1
:caption: Overview
:titlesonly:
introduction
configuration
changelog
.. toctree::
:maxdepth: 4
:caption: Modules
:titlesonly:
modules/panaetius.rst
.. toctree::
:maxdepth: 4
:caption: Submodules
:titlesonly:
modules/panaetius.config.rst
modules/panaetius.config_inst.rst
modules/panaetius.db.rst
modules/panaetius.header.rst
modules/panaetius.library.rst
modules/panaetius.logging.rst

175
duties.py Normal file
View File

@@ -0,0 +1,175 @@
from __future__ import annotations
import os
import pathlib
import re
import shutil
from duty import duty
PACKAGE_NAME = "panaetius"
@duty
def update_deps(ctx, dry: bool = False):
"""
Update the dependencies using Poetry.
Example:
`duty update_deps dry=False`
"""
dry_run = "--dry-run" if dry else ""
ctx.run(
["poetry", "update", dry_run],
title=f"Updating poetry deps {dry_run}",
)
@duty
def test(ctx):
"""Run tests using pytest"""
pytest_results = ctx.run(["pytest", "-v"])
print(pytest_results)
@duty
def coverage(ctx):
"""
Generate a coverage HTML report.
Example:
`duty coverage`
"""
ctx.run(["coverage", "run", "--source", PACKAGE_NAME, "-m", "pytest"])
ctx.run(["coverage", "html"])
@duty
def version(ctx, bump: str = "patch"):
"""
Bump the version using Poetry and update _version.py.
Args:
bump (str, optional) = poetry version flag. Available options are:
patch, minor, major, prepatch, preminor, premajor, prerelease.
Defaults to patch.
Example:
`duty version bump=major`
"""
# bump with poetry
result = ctx.run(["poetry", "version", bump])
new_version = re.search(r"(?:.*)(?:\s)(\d+\.\d+\.\d+)$", result)
print(new_version.group(0))
# update _version.py
version_file = pathlib.Path(PACKAGE_NAME) / "_version.py"
with version_file.open("w", encoding="utf-8") as version_file:
version_file.write(
f'"""Module containing the version of {PACKAGE_NAME}."""\n\n'
+ f'__version__ = "{new_version.group(1)}"\n'
)
print(f"Bumped _version.py to {new_version.group(1)}")
@duty
def build(ctx):
"""
Build with poetry and extract the `setup.py` and copy to project root.
Example:
`duty build`
"""
repo_root = pathlib.Path(".")
# build with poetry
result = ctx.run(["poetry", "build"])
print(result)
# extract the setup.py from the tar
extracted_tar = re.search(r"(?:.*)(?:Built\s)(.*)", result)
tar_file = pathlib.Path(f"./dist/{extracted_tar.group(1)}")
shutil.unpack_archive(tar_file, tar_file.parents[0])
# copy setup.py to repo root
extracted_path = tar_file.parents[0] / os.path.splitext(tar_file.stem)[0]
setup_py = extracted_path / "setup.py"
shutil.copyfile(setup_py, (repo_root / "setup.py"))
# cleanup
shutil.rmtree(extracted_path)
@duty
def export(ctx):
"""
Export the dependencies to a requirements.txt file.
Example:
`duty export`
"""
requirements_content = ctx.run(
[
"poetry",
"export",
"-f",
"requirements.txt",
"--without-hashes",
]
)
requirements_dev_content = ctx.run(
[
"poetry",
"export",
"-f",
"requirements.txt",
"--without-hashes",
"--dev",
]
)
requirements = pathlib.Path(".") / "requirements.txt"
requirements_dev = pathlib.Path(".") / "requirements_dev.txt"
with requirements.open("w", encoding="utf-8") as req:
req.write(requirements_content)
with requirements_dev.open("w", encoding="utf-8") as req:
req.write(requirements_dev_content)
@duty
def publish(ctx, password:str):
"""
Publish the package to pypi.org.
Args:
password (str): pypi.org password.
Example:
`duty publish password=$my_password`
"""
dist_dir = pathlib.Path(".") / "dist"
rm_result = rm_tree(dist_dir)
print(rm_result)
publish_result = ctx.run(["poetry", "publish", "-u", "dtomlinson", "-p", password, "--build"])
print(publish_result)
def rm_tree(directory: pathlib.Path):
"""
Recursively delete a directory and all its contents.
Args:
directory (pathlib.Path): The directory to delete.
"""
for child in directory.glob('*'):
if child.is_file():
child.unlink()
else:
rm_tree(child)
directory.rmdir()

View File

@@ -1,5 +1,7 @@
"""
panaetius - a utility library to read variables and provide convenient logging.
Panaetius package.
A utility library to read variables and provide convenient logging.
Author: Daniel Tomlinson (dtomlinson@panaetius.co.uk)
"""

3
panaetius/_version.py Normal file
View File

@@ -0,0 +1,3 @@
"""Module containing the version of panaetius."""
__version__ = "2.3.2"

View File

@@ -1,5 +1,5 @@
"""
Access variables from a config file or an environment variable.
Config module to 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.yml` or an environment variable.
@@ -15,31 +15,50 @@ from typing import Any
# import toml
import yaml
from panaetius.exceptions import KeyErrorTooDeepException, InvalidPythonException
from panaetius.exceptions import KeyErrorTooDeepException
class Config:
"""The configuration class to access variables."""
"""
A configuration class to access user variables.
def __init__(self, header_variable: str, config_path: str = "") -> None:
Args:
header_variable (str): the `header` variable.
config_path (str|None=None): the path where the header directory is stored.
skip_header_init (bool=False): if True will not use a header subdirectory in the
`config_path`.
"""
def __init__(
self,
header_variable: str,
config_path: str | None = None,
skip_header_init: bool = False,
) -> 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`.
Defaults to None on initialisation.
skip_header_init (bool, optional): If True will not use a header
subdirectory in the `config_path`. Defaults to False.
Examples:
`config_path` defaults to None on initialisation but will be set to `~/.config`.
Example:
A header of `data_analysis` with a config_path of `~/myapps` will define
a config file in `~/myapps/data_analysis/config.yml`.
"""
self.header_variable = header_variable
self.config_path = (
pathlib.Path(config_path)
if config_path
pathlib.Path(config_path).expanduser()
if config_path is not None
else pathlib.Path.home() / ".config"
)
self.skip_header_init = skip_header_init
self._missing_config = self._check_config_file_exists()
# default logging options
@@ -50,12 +69,19 @@ class Config:
@property
def config(self) -> dict:
"""
Return the contents of the config file. If missing returns an empty dictionary.
Return the contents of the config file.
If no config file is specified then this returns an empty dictionary.
Returns:
dict: The contents of the `.yml` loaded as a python dictionary.
dict: The contents of the config `.yml` loaded as a python dictionary.
"""
config_file_location = self.config_path / self.header_variable / "config.yml"
if self.skip_header_init:
config_file_location = self.config_path / "config.yml"
else:
config_file_location = (
self.config_path / self.header_variable / "config.yml"
)
try:
with open(config_file_location, "r", encoding="utf-8") as config_file:
# return dict(toml.load(config_file))
@@ -103,7 +129,10 @@ class Config:
return self._get_env_value(env_key, default)
def _check_config_file_exists(self) -> bool:
if self.skip_header_init is False:
config_file_location = self.config_path / self.header_variable / "config.yml"
else:
config_file_location = self.config_path / "config.yml"
try:
with open(config_file_location, "r", encoding="utf-8"):
return False
@@ -165,7 +194,8 @@ class Config:
try:
return ast.literal_eval(value)
except (ValueError, SyntaxError):
raise InvalidPythonException(f"{value} is not valid Python.") # noqa
# string without spaces: ValueError, with spaces; SyntaxError
return value
def __load_default_value(self, default: Any) -> Any: # noqa
return default

View File

@@ -1,13 +1,13 @@
"""Exceptions for the module."""
"""Module that defines custom exceptions for Panetius."""
class KeyErrorTooDeepException(Exception):
pass
"""Raised if the keys in the config.yml are nested too deeply."""
class LoggingDirectoryDoesNotExistException(Exception):
pass
"""Raised if the logging directory does not exist."""
class InvalidPythonException(Exception):
pass
"""Raised if the environement variable Python type is invalid."""

View File

@@ -63,11 +63,17 @@ def set_logger(config_inst: Config, logging_format_inst: LoggingData) -> logging
# configure file handler
if config_inst.logging_path is not None:
if not config_inst.skip_header_init:
logging_file = (
pathlib.Path(config_inst.logging_path)
/ config_inst.header_variable
/ f"{config_inst.header_variable}.log"
).expanduser()
else:
logging_file = (
pathlib.Path(config_inst.logging_path)
/ f"{config_inst.header_variable}.log"
).expanduser()
if not logging_file.parents[0].exists():
raise LoggingDirectoryDoesNotExistException()
@@ -98,11 +104,11 @@ class LoggingData(metaclass=ABCMeta):
@property
@abstractmethod
def format(self) -> str:
pass
raise NotImplementedError
@abstractmethod
def __init__(self, logging_level: str):
self.logging_level = logging_level
raise NotImplementedError
class SimpleLogger(LoggingData):

View File

@@ -0,0 +1,3 @@
"""Sub-package which defines general utility functions."""
from panaetius.utilities.squasher import Squash

View File

@@ -0,0 +1,64 @@
"""Sub-module that defines squashing json objects into a single json object."""
from __future__ import annotations
from copy import deepcopy
import itertools
from typing import Iterator, Tuple
class Squash:
"""Squash a json object or Python dictionary into a single level dictionary."""
def __init__(self, data: dict) -> None:
"""
Create a Squash object to squash data into a single level dictionary.
Args:
data (dict): [description]
Example:
squashed_data = Squash(my_data)
squashed_data.as_dict
"""
self.data = data
@property
def as_dict(self) -> dict:
"""
Return the squashed data as a dictionary.
Returns:
dict: The original data squashed as a dict.
"""
return self._squash()
@staticmethod
def _unpack_dict(
key: str, value: dict | list | str
) -> Iterator[Tuple[str, dict | list | str]]:
if isinstance(value, dict):
for sub_key, sub_value in value.items():
temporary_key = f"{key}_{sub_key}"
yield temporary_key, sub_value
elif isinstance(value, list):
for index, sub_value in enumerate(value):
temporary_key = f"{key}_{index}"
yield temporary_key, sub_value
else:
yield key, value
def _squash(self) -> dict:
result = deepcopy(self.data)
while True:
result = dict(
itertools.chain.from_iterable(
itertools.starmap(self._unpack_dict, result.items())
)
)
if not any(
isinstance(value, dict) for value in result.values()
) and not any(isinstance(value, list) for value in result.values()):
break
return result

175
poetry.lock generated
View File

@@ -1,3 +1,18 @@
[[package]]
name = "ansimarkup"
version = "1.5.0"
description = "Produce colored terminal text with an xml-like markup"
category = "dev"
optional = false
python-versions = "*"
[package.dependencies]
colorama = "*"
[package.extras]
devel = ["bumpversion (>=0.5.2)", "check-manifest (>=0.35)", "readme-renderer (>=16.0)", "flake8", "pep8-naming"]
tests = ["tox (>=2.6.0)", "pytest (>=3.0.3)", "pytest-cov (>=2.3.1)"]
[[package]]
name = "astroid"
version = "2.8.2"
@@ -49,6 +64,14 @@ PyYAML = ">=5.3.1"
six = ">=1.10.0"
stevedore = ">=1.20.0"
[[package]]
name = "cached-property"
version = "1.5.2"
description = "A decorator for caching properties in classes."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "colorama"
version = "0.4.4"
@@ -76,6 +99,18 @@ category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "duty"
version = "0.7.0"
description = "A simple task runner."
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
cached-property = {version = ">=1.5,<2.0", markers = "python_version < \"3.8\""}
failprint = ">=0.8,<1.0"
[[package]]
name = "execnet"
version = "1.9.0"
@@ -87,6 +122,19 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
testing = ["pre-commit"]
[[package]]
name = "failprint"
version = "0.8.0"
description = "Run a command, print its output only if it fails."
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
ansimarkup = ">=1.4,<2.0"
jinja2 = ">=2.11,<4"
ptyprocess = {version = ">=0.6,<1.0", markers = "sys_platform != \"win32\""}
[[package]]
name = "flake8"
version = "2.3.0"
@@ -173,6 +221,20 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]
plugins = ["setuptools"]
[[package]]
name = "jinja2"
version = "3.0.3"
description = "A very fast and expressive template engine."
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "lazy-object-proxy"
version = "1.6.0"
@@ -181,6 +243,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[[package]]
name = "markupsafe"
version = "2.0.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "mccabe"
version = "0.6.1"
@@ -315,6 +385,14 @@ with_mypy = ["mypy (>=0.600)"]
with_pyroma = ["pyroma (>=2.4)"]
with_vulture = ["vulture (>=1.5)"]
[[package]]
name = "ptyprocess"
version = "0.7.0"
description = "Run a subprocess in a pseudo terminal"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "py"
version = "1.10.0"
@@ -613,9 +691,13 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
content-hash = "f68e5ab35a155ce5ea567b670f93f4678b6c65d1335017b21431b315297a0410"
content-hash = "dcca7e2e7b6854ab40c11f6e3385b08fffb34b874981f1079205855badd7caea"
[metadata.files]
ansimarkup = [
{file = "ansimarkup-1.5.0-py2.py3-none-any.whl", hash = "sha256:3146ca74af5f69e48a9c3d41b31085c0d6378f803edeb364856d37c11a684acf"},
{file = "ansimarkup-1.5.0.tar.gz", hash = "sha256:96c65d75bbed07d3dcbda8dbede8c2252c984f90d0ca07434b88a6bbf345fad3"},
]
astroid = [
{file = "astroid-2.8.2-py3-none-any.whl", hash = "sha256:9eaeaf92b3e21b70bec1a262e7eb118d2e96294892a5de595c92a12adc80dfc2"},
{file = "astroid-2.8.2.tar.gz", hash = "sha256:304e99c129794f2cfda584a12b71fde85205da950e2f330f4be09150525ae949"},
@@ -632,6 +714,10 @@ bandit = [
{file = "bandit-1.7.0-py3-none-any.whl", hash = "sha256:216be4d044209fa06cf2a3e51b319769a51be8318140659719aa7a115c35ed07"},
{file = "bandit-1.7.0.tar.gz", hash = "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"},
]
cached-property = [
{file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"},
{file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
@@ -675,10 +761,18 @@ dodgy = [
{file = "dodgy-0.2.1-py3-none-any.whl", hash = "sha256:51f54c0fd886fa3854387f354b19f429d38c04f984f38bc572558b703c0542a6"},
{file = "dodgy-0.2.1.tar.gz", hash = "sha256:28323cbfc9352139fdd3d316fa17f325cc0e9ac74438cbba51d70f9b48f86c3a"},
]
duty = [
{file = "duty-0.7.0-py3-none-any.whl", hash = "sha256:45068baf1639f16464aa40e9d8f698f0ae09408368fe53a34e9bfe6993dfd743"},
{file = "duty-0.7.0.tar.gz", hash = "sha256:5ebfd4640ab41e3058f1d8433f74228d60c9a808def1784e65319ef1899a9d15"},
]
execnet = [
{file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"},
{file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"},
]
failprint = [
{file = "failprint-0.8.0-py3-none-any.whl", hash = "sha256:a8215a7aca5ce687116b995cd3a9667180f222ab88c4328a5007d2fa0b5c0f78"},
{file = "failprint-0.8.0.tar.gz", hash = "sha256:4633b52f9395bf042ad996c96cd7819a94b2021833030dd1eb692ebbd86b89a1"},
]
flake8 = [
{file = "flake8-2.3.0-py2.py3-none-any.whl", hash = "sha256:c99cc9716d6655d9c8bcb1e77632b8615bf0abd282d7abd9f5c2148cad7fc669"},
{file = "flake8-2.3.0.tar.gz", hash = "sha256:5ee1a43ccd0716d6061521eec6937c983efa027793013e572712c4da55c7c83e"},
@@ -707,6 +801,10 @@ isort = [
{file = "isort-5.9.3-py3-none-any.whl", hash = "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2"},
{file = "isort-5.9.3.tar.gz", hash = "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899"},
]
jinja2 = [
{file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"},
{file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"},
]
lazy-object-proxy = [
{file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"},
{file = "lazy_object_proxy-1.6.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b"},
@@ -731,6 +829,77 @@ lazy-object-proxy = [
{file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"},
]
markupsafe = [
{file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
{file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"},
{file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
{file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
@@ -792,6 +961,10 @@ prospector = [
{file = "prospector-1.5.1-py3-none-any.whl", hash = "sha256:47f8ff3fd36ae276967eb392ca20b300a7bdea66c0d0252250a4d89a6c03ab15"},
{file = "prospector-1.5.1.tar.gz", hash = "sha256:851c2892cd615cfee91fd27cfaf7a5061d14daf2853aa8f012e927b98f919578"},
]
ptyprocess = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
]
py = [
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
{file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},

View File

@@ -1,10 +1,10 @@
[tool.poetry]
name = "panaetius"
version = "1.1"
version = "2.3.2"
description = "Python module to gracefully handle a .config file/environment variables for scripts, with built in masking for sensitive options. Provides a Splunk friendly formatted logger instance."
license = "MIT"
authors = ["dtomlinson <dtomlinson@panaetius.co.uk>"]
readme = "./README.rst"
readme = "./README.md"
homepage = "https://github.com/dtomlinson91/panaetius"
repository = "https://github.com/dtomlinson91/panaetius"
documentation = "https://panaetius.readthedocs.io/en/latest/introduction.html"
@@ -34,6 +34,7 @@ pytest = "^6.2.5"
pytest-datadir = "^1.3.1"
pytest-xdist = "^2.4.0"
coverage = "^6.0.2"
duty = "^0.7.0"
[build-system]
requires = ["poetry>=0.12"]

View File

@@ -1,7 +1,2 @@
pylite==0.1.0 \
--hash=sha256:e338d20d3f8f72dd84d1e58f2fd6dba008d593e0cfacfb5fbdd5a297b830628e \
--hash=sha256:eb46f5beb1f2102672fd4355c013ac2feebc0df284d65f7711f2041a0a410141
toml==0.10.0 \
--hash=sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c \
--hash=sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e \
--hash=sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3
pyyaml==6.0; python_version >= "3.6"
toml==0.10.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")

60
requirements_dev.txt Normal file
View File

@@ -0,0 +1,60 @@
ansimarkup==1.5.0; python_version >= "3.6"
astroid==2.8.2; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1"
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
attrs==21.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
bandit==1.7.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.5"
cached-property==1.5.2; python_version < "3.8" and python_version >= "3.6"
colorama==0.4.4; sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.6.1" and python_version < "4.0" and platform_system == "Windows" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
coverage==6.0.2; python_version >= "3.6"
dodgy==0.2.1; python_full_version >= "3.6.1" and python_version < "4.0"
duty==0.7.0; python_version >= "3.6"
execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
failprint==0.8.0; python_version >= "3.6"
flake8-polyfill==1.0.2; python_full_version >= "3.6.1" and python_version < "4.0"
flake8==2.3.0; python_full_version >= "3.6.1" and python_version < "4.0"
gitdb==4.0.7; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.7"
gitpython==3.1.24; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.7"
importlib-metadata==4.8.1; python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") and python_full_version >= "3.6.1"
iniconfig==1.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
isort==5.9.3; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6"
jinja2==3.0.3; python_version >= "3.6"
lazy-object-proxy==1.6.0; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1"
markupsafe==2.0.1; python_version >= "3.6"
mccabe==0.6.1; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6"
mypy-extensions==0.4.3; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.5"
mypy==0.910; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.5"
packaging==21.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pbr==5.6.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6"
pep8-naming==0.10.0; python_full_version >= "3.6.1" and python_version < "4.0"
pep8==1.7.1; python_full_version >= "3.6.1" and python_version < "4.0"
platformdirs==2.4.0; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1"
pluggy==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
prospector==1.5.1; python_full_version >= "3.6.1" and python_version < "4.0"
ptyprocess==0.7.0; sys_platform != "win32" and python_version >= "3.6"
py==1.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pycodestyle==2.8.0; python_full_version >= "3.6.1" and python_version < "4.0"
pydocstyle==6.1.1; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6"
pyflakes==2.3.1; python_full_version >= "3.6.1" and python_version < "4.0"
pylint-celery==0.3; python_full_version >= "3.6.1" and python_version < "4.0"
pylint-django==2.4.4; python_full_version >= "3.6.1" and python_version < "4.0"
pylint-flask==0.6; python_full_version >= "3.6.1" and python_version < "4.0"
pylint-plugin-utils==0.6; python_full_version >= "3.6.1" and python_version < "4.0"
pylint==2.11.1; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1"
pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
pytest-datadir==1.3.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
pytest-forked==1.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pytest-xdist==2.4.0; python_version >= "3.6"
pytest==6.2.5; python_version >= "3.6"
pyyaml==6.0; python_version >= "3.6"
requirements-detector==0.7; python_full_version >= "3.6.1" and python_version < "4.0"
setoptconf-tmp==0.3.1; python_full_version >= "3.6.1" and python_version < "4.0"
six==1.16.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.5"
smmap==4.0.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.7"
snowballstemmer==2.1.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6"
stevedore==3.4.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6"
toml==0.10.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
typed-ast==1.4.3; python_full_version >= "3.6.1" and python_version < "3.8" and python_version >= "3.6" and implementation_name == "cpython"
types-toml==0.10.1
typing-extensions==3.10.0.2; python_full_version >= "3.6.1" and python_version < "3.8" and python_version >= "3.7"
wrapt==1.12.1; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1"
zipp==3.6.0; python_version < "3.8" and python_version >= "3.6"

View File

@@ -1,43 +0,0 @@
Documentation:
☐ Rewrite documentation using `mkdocs` and using `.md`.
☐ Update the metadata in the `pyproject.toml`.
☐ Create a new `Readme.md` and remove the `.rst`.
Misc:
☐ 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
☐ Bump the version to release 2.0
Archive:
✔ Handle if a bool is passed in as a default @done(21-10-16 05:25) @project(Coding.No Config File)
✔ Handle if a bool is passed in as a default @done(21-10-16 05:25) @project(Coding.Config File)
✔ Create SimpleLogger, AdvancedLogger, CustomLogger classes @done(21-10-16 16:22) @project(Coding.Logging)
✔ Logging path should take by default the config path unless overwritten? @done(21-10-16 23:49) @project(Coding.Logging)
✔ Check logging path + config path are valid, if not raise error. @done(21-10-18 00:04) @project(Coding.Errors)
✔ Add tests for these. @done(21-10-18 00:04) @project(Coding.Errors)
✔ Check for a key > 2 levels, raise custom error, write test @done(21-10-17 23:30) @project(Coding.Errors)
✔ Check all functions and annotations. @done(21-10-18 01:07) @project(Coding.Linting)
✔ Write the docstrings for public functions/methods. @done(21-10-18 02:29) @project(Coding.Docstrings)
✔ When both a config file and a env var is found, use the env var. @done(21-10-18 00:38) @project(Coding.Functionality)
✔ If loading from a default, don't covert to TOML @done(21-10-17 20:33) @project(Tests.Bugfixes)
✔ Env Vars should be given as python objects @done(21-10-17 20:33) @project(Tests.Bugfixes)
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
✔ Test default config path set to "~/.config" @done(21-10-17 17:25) @project(Tests.__init__)
✔ Test config path is set when passed in @done(21-10-17 17:25) @project(Tests.__init__)
✔ Check testing config file is returned as dict @done(21-10-17 17:25) @project(Tests.config property)
✔ Check _self.missing_config and empty dict is returned @done(21-10-17 17:25) @project(Tests.config property)
✔ Arrays & tables loaded correctly from config file @done(21-10-17 20:34) @project(Tests.get_value.config_file)
✔ test when key length is 1 the value is returned @done(21-10-17 18:55) @project(Tests.get_value.config_file)
✔ test when key length is 2 the value is returned @done(21-10-17 18:55) @project(Tests.get_value.config_file)
✔ test when key not found and no env var default is loaded @done(21-10-17 19:01) @project(Tests.get_value.config_file)
✔ test bool's are properly converted @done(21-10-17 19:01) @project(Tests.get_value.config_file)
✔ test when key not found and env var is set value is loaded @done(21-10-17 20:43) @project(Tests.get_value.config_file)
✔ check if env key is missing the default is read in @done(21-10-17 20:55) @project(Tests.get_value.env_var)
✔ check if env key is present the values are read in @done(21-10-17 22:24) @project(Tests.get_value.env_var)
✔ parametrise a test to read in values form env vars and they're set correctly @done(21-10-17 22:24) @project(Tests.get_value.env_var)
✔ test that the env var is valid python @done(21-10-18 01:03) @project(Tests.get_value.env_var)
✔ test set_config works @done(21-10-17 23:29) @project(Tests.library)

View File

@@ -1,27 +1,25 @@
# -*- coding: utf-8 -*-
from distutils.core import setup
package_dir = \
{'': 'src'}
from setuptools import setup
packages = \
['panaetius']
['panaetius', 'panaetius.utilities']
package_data = \
{'': ['*']}
install_requires = \
['pylite>=0.1.0,<0.2.0', 'toml>=0.10.0,<0.11.0']
['PyYAML>=6.0,<7.0', 'toml>=0.10.0,<0.11.0']
setup_kwargs = {
'name': 'panaetius',
'version': '1.0.2',
'description': 'Python module to gracefully handle a .config file/environment variables for scripts, with built in masking for sensitive options. Provides a Splunk friendly logger instance.',
'long_description': 'Author\n=======\n\nDaniel Tomlinson (dtomlinson@panaetius.co.uk)\n\nRequires\n=========\n\n`>= python3.7`\n\nPython requirements\n====================\n\n- toml = "^0.10.0"\n- pylite = "^0.1.0"\n\nDocumentation\n==============\n\nRead the documentation on `read the docs`_.\n\n.. _read the docs: https://panaetius.readthedocs.io/en/latest/introduction.html\n\nInstallation\n==============\n\nYou can install ..:obj:`panaetius`\n\nEasy Way\n=========\n\nPython\n-------\n\nFrom pip\n~~~~~~~~~\n\nFrom local wheel\n~~~~~~~~~~~~~~~~~\n\nFrom source\n~~~~~~~~~~~~\n\nExample Usage\n==============\n\n',
'version': '2.3.1',
'description': 'Python module to gracefully handle a .config file/environment variables for scripts, with built in masking for sensitive options. Provides a Splunk friendly formatted logger instance.',
'long_description': '# Panaetius\n\nThis package provides:\n\n- Functionality to read user variables from a `config.yml` or environment variables.\n- A convenient default logging formatter printing `json` that can save to disk and rotate.\n- Utility functions.\n\n## Config\n\n### options\n\n#### skip_header_init\n\nIf `skip_header_init=True` then the `config_path` will not use the `header_variable` as the\nsub-directory in the `config_path`.\n\nE.g\n\n`CONFIG = panaetius.Config("tembo", "~/tembo/.config", skip_header_init=True)`\n\nWill look in `~/tembo/config/config.yml`.\n\nIf `skip_header_init=False` then would look in `~/tembo/config/tembo/config.yml`.\n\n### Module\n\nConvenient to place in a package/sub-package `__init__.py`.\n\nSee Tembo for an example: <https://github.com/tembo-pages/tembo-core/blob/main/tembo/cli/__init__.py>\n\nExample snippet to use in a module:\n\n```python\nimport os\n\nimport panaetius\nfrom panaetius.exceptions import LoggingDirectoryDoesNotExistException\n\n\nif (config_path := os.environ.get("TEMBO_CONFIG")) is not None:\n CONFIG = panaetius.Config("tembo", config_path, skip_header_init=True)\nelse:\n CONFIG = panaetius.Config("tembo", "~/tembo/.config", skip_header_init=True)\n\n\npanaetius.set_config(CONFIG, "base_path", "~/tembo")\npanaetius.set_config(CONFIG, "template_path", "~/tembo/.templates")\npanaetius.set_config(CONFIG, "scopes", {})\npanaetius.set_config(CONFIG, "logging.level", "DEBUG")\npanaetius.set_config(CONFIG, "logging.path")\n\ntry:\n logger = panaetius.set_logger(\n CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level)\n )\nexcept LoggingDirectoryDoesNotExistException:\n _LOGGING_PATH = CONFIG.logging_path\n CONFIG.logging_path = ""\n logger = panaetius.set_logger(\n CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level)\n )\n logger.warning("Logging directory %s does not exist", _LOGGING_PATH)\n\n```\n\nThis means in `./tembo/cli/cli.py` you can\n\n```python\nimport tembo.cli\n\n# access the CONFIG instance + variables from the config.yml\ntembo.cli.CONFIG\n```\n\n\n## Utility Functions\n\n### Squasher\n\nSquashes a json object or Python dictionary into a single level dictionary.\n',
'author': 'dtomlinson',
'author_email': 'dtomlinson@panaetius.co.uk',
'maintainer': None,
'maintainer_email': None,
'url': 'https://github.com/dtomlinson91/panaetius',
'package_dir': package_dir,
'packages': packages,
'package_data': package_data,
'install_requires': install_requires,

View File

@@ -0,0 +1,9 @@
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

@@ -29,6 +29,17 @@ def test_user_config_path_set(header, shared_datadir):
assert str(config.config_path) == config_path
def test_user_config_path_without_header_dir_set(header, shared_datadir):
# arrange
config_path = str(shared_datadir / "without_header")
# act
config = panaetius.Config(header, config_path, skip_header_init=True)
# assert
assert str(config.config_path) == config_path
# test config files
@@ -44,6 +55,18 @@ def test_config_file_exists(header, shared_datadir):
assert config._missing_config is False
def test_config_file_without_header_dir_exists(header, shared_datadir):
# arrange
config_path = str(shared_datadir / "without_header")
# act
config = panaetius.Config(header, config_path, skip_header_init=True)
_ = config.config
# assert
assert config._missing_config is False
def test_config_file_contents_read_success(
header, shared_datadir, testing_config_contents
):
@@ -106,7 +129,7 @@ def test_get_value_from_key(
def test_get_value_environment_var_override(header, shared_datadir):
# arrange
os.environ[f"{header.upper()}_SOME_TOP_STRING"] = '"some_overridden_value"'
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")
@@ -158,7 +181,7 @@ def test_get_value_missing_key_from_default(header, shared_datadir):
def test_get_value_missing_key_from_env(header, shared_datadir):
# arrange
os.environ[f"{header.upper()}_MISSING_KEY"] = '"some missing key"'
os.environ[f"{header.upper()}_MISSING_KEY"] = "some missing key"
config_path = str(shared_datadir / "without_logging")
config = panaetius.Config(header, config_path)
@@ -205,7 +228,7 @@ def test_missing_config_read_from_default(header, shared_datadir):
@pytest.mark.parametrize(
"env_value,expected_value",
[
('"a missing string"', "a missing string"),
("a missing string", "a missing string"),
("1", 1),
("1.0", 1.0),
("True", True),
@@ -237,6 +260,7 @@ def test_missing_config_read_from_env_var(
del os.environ[f"{header.upper()}_MISSING_KEY_READ_FROM_ENV_VAR"]
@pytest.mark.skip(reason="No longer needed as strings are loaded without quotes")
def test_missing_config_read_from_env_var_invalid_python(header):
# arrange
os.environ[f"{header.upper()}_INVALID_PYTHON"] = "a string without quotes"

View File

@@ -21,6 +21,7 @@ def test_logging_directory_does_not_exist(header, shared_datadir):
assert str(logging_exception.value) == ""
# TODO: change this test so it asserts the dir exists
def test_logging_directory_does_exist(header, shared_datadir):
# arrange
config = Config(header)
@@ -32,3 +33,5 @@ def test_logging_directory_does_exist(header, shared_datadir):
# assert
assert isinstance(logger, logging.Logger)
# TODO: add tests to check that SimpleLogger, AdvancedLogger, CustomLogger work as intended

View File

View File

@@ -0,0 +1,119 @@
import pytest
from panaetius import utilities
def test_squashed_data(squashed_data, squashed_data_result):
# act
squashed_data_pre_squashed = utilities.squasher.Squash(squashed_data).as_dict
# assert
assert squashed_data_pre_squashed == squashed_data_result
@pytest.fixture
def squashed_data():
return {
"destination_addresses": [
"Washington, DC, USA",
"Philadelphia, PA, USA",
"Santa Barbara, CA, USA",
"Miami, FL, USA",
"Austin, TX, USA",
"Napa County, CA, USA",
],
"origin_addresses": ["New York, NY, USA"],
"rows": [
{
"elements": [
{
"distance": {"text": "227 mi", "value": 365468},
"duration": {
"text": "3 hours 54 mins",
"value": 14064,
},
"status": "OK",
},
{
"distance": {"text": "94.6 mi", "value": 152193},
"duration": {"text": "1 hour 44 mins", "value": 6227},
"status": "OK",
},
{
"distance": {"text": "2,878 mi", "value": 4632197},
"duration": {
"text": "1 day 18 hours",
"value": 151772,
},
"status": "OK",
},
{
"distance": {"text": "1,286 mi", "value": 2069031},
"duration": {
"text": "18 hours 43 mins",
"value": 67405,
},
"status": "OK",
},
{
"distance": {"text": "1,742 mi", "value": 2802972},
"duration": {"text": "1 day 2 hours", "value": 93070},
"status": "OK",
},
{
"distance": {"text": "2,871 mi", "value": 4620514},
"duration": {
"text": "1 day 18 hours",
"value": 152913,
},
"status": "OK",
},
]
}
],
"status": "OK",
}
@pytest.fixture
def squashed_data_result():
return {
"destination_addresses_0": "Washington, DC, USA",
"destination_addresses_1": "Philadelphia, PA, USA",
"destination_addresses_2": "Santa Barbara, CA, USA",
"destination_addresses_3": "Miami, FL, USA",
"destination_addresses_4": "Austin, TX, USA",
"destination_addresses_5": "Napa County, CA, USA",
"origin_addresses_0": "New York, NY, USA",
"rows_0_elements_0_distance_text": "227 mi",
"rows_0_elements_0_distance_value": 365468,
"rows_0_elements_0_duration_text": "3 hours 54 mins",
"rows_0_elements_0_duration_value": 14064,
"rows_0_elements_0_status": "OK",
"rows_0_elements_1_distance_text": "94.6 mi",
"rows_0_elements_1_distance_value": 152193,
"rows_0_elements_1_duration_text": "1 hour 44 mins",
"rows_0_elements_1_duration_value": 6227,
"rows_0_elements_1_status": "OK",
"rows_0_elements_2_distance_text": "2,878 mi",
"rows_0_elements_2_distance_value": 4632197,
"rows_0_elements_2_duration_text": "1 day 18 hours",
"rows_0_elements_2_duration_value": 151772,
"rows_0_elements_2_status": "OK",
"rows_0_elements_3_distance_text": "1,286 mi",
"rows_0_elements_3_distance_value": 2069031,
"rows_0_elements_3_duration_text": "18 hours 43 mins",
"rows_0_elements_3_duration_value": 67405,
"rows_0_elements_3_status": "OK",
"rows_0_elements_4_distance_text": "1,742 mi",
"rows_0_elements_4_distance_value": 2802972,
"rows_0_elements_4_duration_text": "1 day 2 hours",
"rows_0_elements_4_duration_value": 93070,
"rows_0_elements_4_status": "OK",
"rows_0_elements_5_distance_text": "2,871 mi",
"rows_0_elements_5_distance_value": 4620514,
"rows_0_elements_5_duration_text": "1 day 18 hours",
"rows_0_elements_5_duration_value": 152913,
"rows_0_elements_5_status": "OK",
"status": "OK",
}