From c1ce2651accaef9e41899760364e65d1bfdb1e7c Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Mon, 11 Oct 2021 00:06:36 +0100 Subject: [PATCH 01/33] updating dependencies --- poetry.lock | 1151 ++++++++++++++---------------------------------- pyproject.toml | 13 +- 2 files changed, 327 insertions(+), 837 deletions(-) diff --git a/poetry.lock b/poetry.lock index 97095ae..f2ac3ef 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,329 +1,197 @@ [[package]] -category = "dev" -description = "A configurable sidebar-enabled Sphinx theme" -name = "alabaster" -optional = false -python-versions = "*" -version = "0.7.12" - -[[package]] -category = "dev" -description = "An abstract syntax tree for Python with inference support." name = "astroid" +version = "2.8.2" +description = "An abstract syntax tree for Python with inference support." +category = "dev" optional = false -python-versions = ">=3.5" -version = "2.4.1" +python-versions = "~=3.6" [package.dependencies] -lazy-object-proxy = ">=1.4.0,<1.5.0" -six = ">=1.12,<2.0" -wrapt = ">=1.11,<2.0" - -[package.dependencies.typed-ast] -python = "<3.8" -version = ">=1.4.0,<1.5" +lazy-object-proxy = ">=1.4.0" +typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} +wrapt = ">=1.11,<1.13" [[package]] -category = "dev" -description = "Atomic file writes." name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.3.0" [[package]] -category = "dev" -description = "Classes Without Boilerplate" name = "attrs" +version = "21.2.0" +description = "Classes Without Boilerplate" +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.3.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] -dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] -docs = ["sphinx", "zope.interface"] -tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] [[package]] -category = "dev" -description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" -name = "autopep8" -optional = false -python-versions = "*" -version = "1.4.4" - -[package.dependencies] -pycodestyle = ">=2.4.0" - -[[package]] -category = "dev" -description = "Internationalization utilities" -name = "babel" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.8.0" - -[package.dependencies] -pytz = ">=2015.7" - -[[package]] -category = "dev" -description = "Python package for providing Mozilla's CA Bundle." -name = "certifi" -optional = false -python-versions = "*" -version = "2019.11.28" - -[[package]] -category = "dev" -description = "Universal encoding detector for Python 2 and 3" -name = "chardet" -optional = false -python-versions = "*" -version = "3.0.4" - -[[package]] -category = "dev" -description = "Cross-platform colored terminal text." -marker = "sys_platform == \"win32\"" name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.4.3" [[package]] -category = "dev" -description = "Docutils -- Python Documentation Utilities" -name = "docutils" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.16" - -[[package]] -category = "dev" -description = "Dodgy: Searches for dodgy looking lines in Python code" name = "dodgy" +version = "0.2.1" +description = "Dodgy: Searches for dodgy looking lines in Python code" +category = "dev" optional = false python-versions = "*" -version = "0.2.1" [[package]] -category = "dev" -description = "the modular source code checker: pep8 pyflakes and co" name = "flake8" +version = "3.9.2" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "3.8.3" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.6.0a1,<2.7.0" -pyflakes = ">=2.2.0,<2.3.0" - -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" [[package]] -category = "dev" -description = "Polyfill package for Flake8 plugins" name = "flake8-polyfill" +version = "1.0.2" +description = "Polyfill package for Flake8 plugins" +category = "dev" optional = false python-versions = "*" -version = "1.0.2" [package.dependencies] flake8 = "*" [[package]] -category = "dev" -description = "Clean single-source support for Python 3 and 2" -name = "future" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "0.18.2" - -[[package]] -category = "dev" -description = "Internationalized Domain Names in Applications (IDNA)" -name = "idna" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.8" - -[[package]] -category = "dev" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -name = "imagesize" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.2.0" - -[[package]] -category = "dev" -description = "Read metadata from Python packages" -marker = "python_version < \"3.8\"" name = "importlib-metadata" +version = "4.8.1" +description = "Read metadata from Python packages" +category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "1.4.0" +python-versions = ">=3.6" [package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "importlib-resources"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +perf = ["ipython"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] -category = "dev" -description = "A Python utility / library to sort Python imports." name = "isort" +version = "5.9.3" +description = "A Python utility / library to sort Python imports." +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "4.3.21" +python-versions = ">=3.6.1,<4.0" [package.extras] -pipfile = ["pipreqs", "requirementslib"] -pyproject = ["toml"] -requirements = ["pipreqs", "pip-api"] -xdg_home = ["appdirs (>=1.4.0)"] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] [[package]] -category = "dev" -description = "An autocompletion tool for Python that can be used for text editors." -name = "jedi" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.15.2" - -[package.dependencies] -parso = ">=0.5.2" - -[package.extras] -testing = ["colorama (0.4.1)", "docopt", "pytest (>=3.9.0,<5.0.0)"] - -[[package]] -category = "dev" -description = "A very fast and expressive template engine." -name = "jinja2" -optional = false -python-versions = "*" -version = "2.10.3" - -[package.dependencies] -MarkupSafe = ">=0.23" - -[package.extras] -i18n = ["Babel (>=0.8)"] - -[[package]] -category = "dev" -description = "A fast and thorough lazy object proxy." name = "lazy-object-proxy" +version = "1.6.0" +description = "A fast and thorough lazy object proxy." +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.4.3" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] -category = "dev" -description = "Safely add untrusted strings to HTML/XML markup." -name = "markupsafe" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.1.1" - -[[package]] -category = "dev" -description = "McCabe checker, plugin for flake8" name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" optional = false python-versions = "*" -version = "0.6.1" [[package]] -category = "dev" -description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" +version = "8.10.0" +description = "More routines for operating on iterables, beyond itertools" +category = "dev" optional = false python-versions = ">=3.5" -version = "8.0.2" [[package]] -category = "dev" -description = "Core utilities for Python packages" -name = "packaging" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.0" - -[package.dependencies] -pyparsing = ">=2.0.2" -six = "*" - -[[package]] -category = "dev" -description = "A Python Parser" -name = "parso" -optional = false -python-versions = "*" -version = "0.5.2" - -[package.extras] -testing = ["docopt", "pytest (>=3.0.7)"] - -[[package]] -category = "dev" -description = "Check PEP-8 naming conventions, plugin for flake8" name = "pep8-naming" +version = "0.10.0" +description = "Check PEP-8 naming conventions, plugin for flake8" +category = "dev" optional = false python-versions = "*" -version = "0.10.0" [package.dependencies] flake8-polyfill = ">=1.0.2,<2" [[package]] +name = "platformdirs" +version = "2.4.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" -description = "plugin and hook calling mechanisms for python" -name = "pluggy" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.13.1" +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" [package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] -category = "dev" -description = "Prospector: python static analysis tool" name = "prospector" +version = "1.5.1" +description = "" +category = "dev" optional = false -python-versions = "*" -version = "1.3.0" +python-versions = ">=3.6.1,<4.0" [package.dependencies] -astroid = "2.4.1" -dodgy = ">=0.1.9" -mccabe = ">=0.5.0" +dodgy = ">=0.2.1,<0.3.0" +mccabe = ">=0.6.0,<0.7.0" pep8-naming = ">=0.3.3,<=0.10.0" -pycodestyle = ">=2.6.0,<2.7.0" +pycodestyle = ">=2.6.0,<2.9.0" pydocstyle = ">=2.0.0" -pyflakes = ">=2.2.0,<2.3.0" -pylint = "2.5.2" +pyflakes = ">=2.2.0,<2.4.0" +pylint = ">=2.8.3,<3" pylint-celery = "0.3" -pylint-django = "2.0.15" +pylint-django = ">=2.4.4,<3.0.0" pylint-flask = "0.6" -pylint-plugin-utils = ">=0.2.6" -pyyaml = "*" -requirements-detector = ">=0.6" -setoptconf = ">=0.2.0" +pylint-plugin-utils = ">=0.6,<0.7" +PyYAML = "*" +requirements-detector = ">=0.7,<0.8" +setoptconf-tmp = ">=0.3.1,<0.4.0" +toml = ">=0.10.2,<0.11.0" [package.extras] with_bandit = ["bandit (>=1.5.1)"] @@ -334,82 +202,67 @@ with_pyroma = ["pyroma (>=2.4)"] with_vulture = ["vulture (>=1.5)"] [[package]] -category = "dev" -description = "A full-screen, console-based Python debugger" -name = "pudb" -optional = false -python-versions = "*" -version = "2019.2" - -[package.dependencies] -pygments = ">=1.0" -urwid = ">=1.1.1" - -[[package]] -category = "dev" -description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" +version = "1.10.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.8.1" [[package]] -category = "dev" -description = "Python style guide checker" name = "pycodestyle" +version = "2.7.0" +description = "Python style guide checker" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.6.0" [[package]] -category = "dev" -description = "Python docstring style checker" name = "pydocstyle" +version = "6.1.1" +description = "Python docstring style checker" +category = "dev" optional = false -python-versions = ">=3.5" -version = "5.0.2" +python-versions = ">=3.6" [package.dependencies] snowballstemmer = "*" +[package.extras] +toml = ["toml"] + [[package]] -category = "dev" -description = "passive checker of Python programs" name = "pyflakes" +version = "2.3.1" +description = "passive checker of Python programs" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.2.0" [[package]] -category = "dev" -description = "Pygments is a syntax highlighting package written in Python." -name = "pygments" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.5.2" - -[[package]] -category = "dev" -description = "python code static checker" name = "pylint" +version = "2.11.1" +description = "python code static checker" +category = "dev" optional = false -python-versions = ">=3.5.*" -version = "2.5.2" +python-versions = "~=3.6" [package.dependencies] -astroid = ">=2.4.0,<=2.5" -colorama = "*" -isort = ">=4.2.5,<5" +astroid = ">=2.8.0,<2.9" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.7" +platformdirs = ">=2.2.0" toml = ">=0.7.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} [[package]] -category = "dev" -description = "pylint-celery is a Pylint plugin to aid Pylint in recognising and understandingerrors caused when using the Celery library" name = "pylint-celery" +version = "0.3" +description = "pylint-celery is a Pylint plugin to aid Pylint in recognising and understandingerrors caused when using the Celery library" +category = "dev" optional = false python-versions = "*" -version = "0.3" [package.dependencies] astroid = ">=1.0" @@ -417,591 +270,274 @@ pylint = ">=1.0" pylint-plugin-utils = ">=0.2.1" [[package]] -category = "dev" -description = "A Pylint plugin to help Pylint understand the Django web framework" name = "pylint-django" +version = "2.4.4" +description = "A Pylint plugin to help Pylint understand the Django web framework" +category = "dev" optional = false python-versions = "*" -version = "2.0.15" [package.dependencies] pylint = ">=2.0" pylint-plugin-utils = ">=0.5" [package.extras] -for_tests = ["coverage", "django-tables2", "factory-boy", "pytest"] +for_tests = ["django-tables2", "factory-boy", "coverage", "pytest"] with_django = ["django"] [[package]] -category = "dev" -description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +category = "dev" optional = false python-versions = "*" -version = "0.6" [package.dependencies] pylint-plugin-utils = ">=0.2.1" [[package]] -category = "dev" -description = "Utilities and helpers for writing Pylint plugins" name = "pylint-plugin-utils" +version = "0.6" +description = "Utilities and helpers for writing Pylint plugins" +category = "dev" optional = false python-versions = "*" -version = "0.6" [package.dependencies] pylint = ">=1.7" [[package]] -category = "main" -description = "Intract with sqlite3 in python as simple as it can be." name = "pylite" +version = "0.1.0" +description = "Intract with sqlite3 in python as simple as it can be." +category = "main" optional = false python-versions = "*" -version = "0.1.0" [[package]] -category = "dev" -description = "Python parsing module" -name = "pyparsing" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.6" - -[[package]] -category = "dev" -description = "pytest: simple powerful testing with Python" name = "pytest" +version = "3.10.1" +description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.10.1" [package.dependencies] atomicwrites = ">=1.0" attrs = ">=17.4.0" -colorama = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} more-itertools = ">=4.0.0" pluggy = ">=0.7" py = ">=1.5.0" -setuptools = "*" six = ">=1.10.0" [[package]] -category = "dev" -description = "JSON RPC 2.0 server library" -name = "python-jsonrpc-server" -optional = false -python-versions = "*" -version = "0.3.2" - -[package.dependencies] -future = ">=0.14.0" -ujson = "<=1.35" - -[package.extras] -test = ["versioneer", "pylint", "pycodestyle", "pyflakes", "pytest", "mock", "pytest-cov", "coverage"] - -[[package]] -category = "dev" -description = "Python Language Server for the Language Server Protocol" -name = "python-language-server" -optional = false -python-versions = "*" -version = "0.31.4" - -[package.dependencies] -jedi = ">=0.14.1,<0.16" -pluggy = "*" -python-jsonrpc-server = ">=0.3.2" -ujson = "<=1.35" - -[package.extras] -all = ["autopep8", "flake8", "mccabe", "pycodestyle", "pydocstyle (>=2.0.0)", "pyflakes (>=1.6.0)", "pylint", "rope (>=0.10.5)", "yapf"] -autopep8 = ["autopep8"] -flake8 = ["flake8"] -mccabe = ["mccabe"] -pycodestyle = ["pycodestyle"] -pydocstyle = ["pydocstyle (>=2.0.0)"] -pyflakes = ["pyflakes (>=1.6.0)"] -pylint = ["pylint"] -rope = ["rope (>0.10.5)"] -test = ["versioneer", "pylint", "pytest", "mock", "pytest-cov", "coverage", "numpy", "pandas", "matplotlib", "pyqt5"] -yapf = ["yapf"] - -[[package]] -category = "dev" -description = "World timezone definitions, modern and historical" -name = "pytz" -optional = false -python-versions = "*" -version = "2019.3" - -[[package]] -category = "dev" -description = "YAML parser and emitter for Python" name = "pyyaml" +version = "5.4.1" +description = "YAML parser and emitter for Python" +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.3.1" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] -category = "dev" -description = "Python HTTP for Humans." -name = "requests" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.22.0" - -[package.dependencies] -certifi = ">=2017.4.17" -chardet = ">=3.0.2,<3.1.0" -idna = ">=2.5,<2.9" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" - -[package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] - -[[package]] -category = "dev" -description = "Python tool to find and list requirements of a Python project" name = "requirements-detector" +version = "0.7" +description = "Python tool to find and list requirements of a Python project" +category = "dev" optional = false python-versions = "*" -version = "0.7" [package.dependencies] astroid = ">=1.4" [[package]] -category = "dev" -description = "a python refactoring library..." -name = "rope" -optional = false -python-versions = "*" -version = "0.16.0" - -[package.extras] -dev = ["pytest"] - -[[package]] -category = "dev" +name = "setoptconf-tmp" +version = "0.3.1" description = "A module for retrieving program settings from various sources in a consistant method." -name = "setoptconf" +category = "dev" optional = false python-versions = "*" -version = "0.2.0" [package.extras] -YAML = ["pyyaml"] +yaml = ["pyyaml"] [[package]] -category = "dev" -description = "Python 2 and 3 compatibility utilities" name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "1.13.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] -category = "dev" -description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms." name = "snowballstemmer" -optional = false -python-versions = "*" -version = "2.0.0" - -[[package]] -category = "dev" -description = "Python documentation generator" -name = "sphinx" -optional = false -python-versions = ">=3.5" -version = "2.3.1" - -[package.dependencies] -Jinja2 = ">=2.3" -Pygments = ">=2.0" -alabaster = ">=0.7,<0.8" -babel = ">=1.3,<2.0 || >2.0" -colorama = ">=0.3.5" -docutils = ">=0.12" -imagesize = "*" -packaging = "*" -requests = ">=2.5.0" -setuptools = "*" -snowballstemmer = ">=1.1" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = "*" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = "*" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -test = ["pytest", "pytest-cov", "html5lib", "flake8 (>=3.5.0)", "flake8-import-order", "mypy (>=0.761)", "docutils-stubs"] - -[[package]] -category = "dev" -description = "Read the Docs theme for Sphinx" -name = "sphinx-rtd-theme" -optional = false -python-versions = "*" -version = "0.4.3" - -[package.dependencies] -sphinx = "*" - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-applehelp" -optional = false -python-versions = "*" -version = "1.0.1" - -[package.extras] -test = ["pytest", "flake8", "mypy"] - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-devhelp" -optional = false -python-versions = "*" -version = "1.0.1" - -[package.extras] -test = ["pytest", "flake8", "mypy"] - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-htmlhelp" -optional = false -python-versions = "*" -version = "1.0.2" - -[package.extras] -test = ["pytest", "flake8", "mypy", "html5lib"] - -[[package]] -category = "dev" -description = "A sphinx extension which renders display math in HTML via JavaScript" -name = "sphinxcontrib-jsmath" -optional = false -python-versions = ">=3.5" -version = "1.0.1" - -[package.extras] -test = ["pytest", "flake8", "mypy"] - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-qthelp" -optional = false -python-versions = "*" -version = "1.0.2" - -[package.extras] -test = ["pytest", "flake8", "mypy"] - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-serializinghtml" -optional = false -python-versions = "*" -version = "1.1.3" - -[package.extras] -test = ["pytest", "flake8", "mypy"] - -[[package]] -category = "main" -description = "Python Library for Tom's Obvious, Minimal Language" -name = "toml" -optional = false -python-versions = "*" -version = "0.10.0" - -[[package]] -category = "dev" -description = "a fork of Python 2 and 3 ast modules with type comment support" -marker = "implementation_name == \"cpython\" and python_version < \"3.8\"" -name = "typed-ast" -optional = false -python-versions = "*" -version = "1.4.1" - -[[package]] -category = "dev" -description = "Ultra fast JSON encoder and decoder for Python" -marker = "platform_system != \"Windows\"" -name = "ujson" -optional = false -python-versions = "*" -version = "1.35" - -[[package]] -category = "dev" -description = "HTTP library with thread-safe connection pooling, file post, and more." -name = "urllib3" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" -version = "1.25.7" - -[package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] - -[[package]] -category = "dev" -description = "A full-featured console (xterm et al.) user interface library" -name = "urwid" -optional = false -python-versions = "*" version = "2.1.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" +optional = false +python-versions = "*" [[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "typed-ast" +version = "1.4.3" +description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" -description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = "*" + +[[package]] +name = "typing-extensions" +version = "3.10.0.2" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "dev" +optional = false +python-versions = "*" + +[[package]] name = "wrapt" -optional = false -python-versions = "*" version = "1.12.1" - -[[package]] +description = "Module for decorators, wrappers and monkey patching." category = "dev" -description = "A formatter for Python code." -name = "yapf" optional = false python-versions = "*" -version = "0.29.0" [[package]] -category = "dev" -description = "Backport of pathlib-compatible object wrapper for zip files" -marker = "python_version < \"3.8\"" name = "zipp" +version = "3.6.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" optional = false -python-versions = ">=2.7" -version = "0.6.0" - -[package.dependencies] -more-itertools = "*" +python-versions = ">=3.6" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pathlib2", "contextlib2", "unittest2"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] -content-hash = "a3b2fae74cc61f6e39dbd27be38fe8420710af16a4051f8d45c609a3221531b5" -lock-version = "1.0" +lock-version = "1.1" python-versions = "^3.7" +content-hash = "68bd5fa009bc4b1089842f9dd5316e839a579bc28077bd7efefea94c815a7852" [metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] astroid = [ - {file = "astroid-2.4.1-py3-none-any.whl", hash = "sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38"}, - {file = "astroid-2.4.1.tar.gz", hash = "sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1"}, + {file = "astroid-2.8.2-py3-none-any.whl", hash = "sha256:9eaeaf92b3e21b70bec1a262e7eb118d2e96294892a5de595c92a12adc80dfc2"}, + {file = "astroid-2.8.2.tar.gz", hash = "sha256:304e99c129794f2cfda584a12b71fde85205da950e2f330f4be09150525ae949"}, ] atomicwrites = [ - {file = "atomicwrites-1.3.0-py2.py3-none-any.whl", hash = "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"}, - {file = "atomicwrites-1.3.0.tar.gz", hash = "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"}, + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, - {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, -] -autopep8 = [ - {file = "autopep8-1.4.4.tar.gz", hash = "sha256:4d8eec30cc81bc5617dbf1218201d770dc35629363547f17577c61683ccfb3ee"}, -] -babel = [ - {file = "Babel-2.8.0-py2.py3-none-any.whl", hash = "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"}, - {file = "Babel-2.8.0.tar.gz", hash = "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38"}, -] -certifi = [ - {file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"}, - {file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"}, -] -chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] colorama = [ - {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, - {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, -] -docutils = [ - {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, - {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] dodgy = [ {file = "dodgy-0.2.1-py3-none-any.whl", hash = "sha256:51f54c0fd886fa3854387f354b19f429d38c04f984f38bc572558b703c0542a6"}, {file = "dodgy-0.2.1.tar.gz", hash = "sha256:28323cbfc9352139fdd3d316fa17f325cc0e9ac74438cbba51d70f9b48f86c3a"}, ] flake8 = [ - {file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"}, - {file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"}, + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] flake8-polyfill = [ {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, ] -future = [ - {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, -] -idna = [ - {file = "idna-2.8-py2.py3-none-any.whl", hash = "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"}, - {file = "idna-2.8.tar.gz", hash = "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407"}, -] -imagesize = [ - {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"}, - {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, -] importlib-metadata = [ - {file = "importlib_metadata-1.4.0-py2.py3-none-any.whl", hash = "sha256:bdd9b7c397c273bcc9a11d6629a38487cd07154fa255a467bf704cd2c258e359"}, - {file = "importlib_metadata-1.4.0.tar.gz", hash = "sha256:f17c015735e1a88296994c0697ecea7e11db24290941983b08c9feb30921e6d8"}, + {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, + {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, ] isort = [ - {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, - {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, -] -jedi = [ - {file = "jedi-0.15.2-py2.py3-none-any.whl", hash = "sha256:1349c1e8c107095a55386628bb3b2a79422f3a2cab8381e34ce19909e0cf5064"}, - {file = "jedi-0.15.2.tar.gz", hash = "sha256:e909527104a903606dd63bea6e8e888833f0ef087057829b89a18364a856f807"}, -] -jinja2 = [ - {file = "Jinja2-2.10.3-py2.py3-none-any.whl", hash = "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f"}, - {file = "Jinja2-2.10.3.tar.gz", hash = "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"}, + {file = "isort-5.9.3-py3-none-any.whl", hash = "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2"}, + {file = "isort-5.9.3.tar.gz", hash = "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899"}, ] lazy-object-proxy = [ - {file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"}, - {file = "lazy_object_proxy-1.4.3-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442"}, - {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win32.whl", hash = "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4"}, - {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a"}, - {file = "lazy_object_proxy-1.4.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d"}, - {file = "lazy_object_proxy-1.4.3-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a"}, - {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win32.whl", hash = "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e"}, - {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win_amd64.whl", hash = "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357"}, - {file = "lazy_object_proxy-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50"}, - {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db"}, - {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449"}, - {file = "lazy_object_proxy-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156"}, - {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531"}, - {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea"}, - {file = "lazy_object_proxy-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62"}, - {file = "lazy_object_proxy-1.4.3-cp38-cp38-win32.whl", hash = "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd"}, - {file = "lazy_object_proxy-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239"}, -] -markupsafe = [ - {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, - {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, + {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"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win32.whl", hash = "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-win32.whl", hash = "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a"}, + {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"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] more-itertools = [ - {file = "more-itertools-8.0.2.tar.gz", hash = "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d"}, - {file = "more_itertools-8.0.2-py3-none-any.whl", hash = "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564"}, -] -packaging = [ - {file = "packaging-20.0-py2.py3-none-any.whl", hash = "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb"}, - {file = "packaging-20.0.tar.gz", hash = "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8"}, -] -parso = [ - {file = "parso-0.5.2-py2.py3-none-any.whl", hash = "sha256:5c1f7791de6bd5dbbeac8db0ef5594b36799de198b3f7f7014643b0c5536b9d3"}, - {file = "parso-0.5.2.tar.gz", hash = "sha256:55cf25df1a35fd88b878715874d2c4dc1ad3f0eebd1e0266a67e1f55efccfbe1"}, + {file = "more-itertools-8.10.0.tar.gz", hash = "sha256:1debcabeb1df793814859d64a81ad7cb10504c24349368ccf214c664c474f41f"}, + {file = "more_itertools-8.10.0-py3-none-any.whl", hash = "sha256:56ddac45541718ba332db05f464bebfb0768110111affd27f66e0051f276fa43"}, ] pep8-naming = [ {file = "pep8-naming-0.10.0.tar.gz", hash = "sha256:f3b4a5f9dd72b991bf7d8e2a341d2e1aa3a884a769b5aaac4f56825c1763bf3a"}, {file = "pep8_naming-0.10.0-py2.py3-none-any.whl", hash = "sha256:5d9f1056cb9427ce344e98d1a7f5665710e2f20f748438e308995852cfa24164"}, ] +platformdirs = [ + {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, + {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, +] pluggy = [ - {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, - {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] prospector = [ - {file = "prospector-1.3.0.tar.gz", hash = "sha256:43e5e187c027336b0e4c4aa6a82d66d3b923b5ec5b51968126132e32f9d14a2f"}, -] -pudb = [ - {file = "pudb-2019.2.tar.gz", hash = "sha256:e8f0ea01b134d802872184b05bffc82af29a1eb2f9374a277434b932d68f58dc"}, + {file = "prospector-1.5.1-py3-none-any.whl", hash = "sha256:47f8ff3fd36ae276967eb392ca20b300a7bdea66c0d0252250a4d89a6c03ab15"}, + {file = "prospector-1.5.1.tar.gz", hash = "sha256:851c2892cd615cfee91fd27cfaf7a5061d14daf2853aa8f012e927b98f919578"}, ] py = [ - {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, - {file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pycodestyle = [ - {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, - {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] pydocstyle = [ - {file = "pydocstyle-5.0.2-py3-none-any.whl", hash = "sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586"}, - {file = "pydocstyle-5.0.2.tar.gz", hash = "sha256:f4f5d210610c2d153fae39093d44224c17429e2ad7da12a8b419aba5c2f614b5"}, + {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, + {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, ] pyflakes = [ - {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, - {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, -] -pygments = [ - {file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"}, - {file = "Pygments-2.5.2.tar.gz", hash = "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"}, + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pylint = [ - {file = "pylint-2.5.2-py3-none-any.whl", hash = "sha256:dd506acce0427e9e08fb87274bcaa953d38b50a58207170dbf5b36cf3e16957b"}, - {file = "pylint-2.5.2.tar.gz", hash = "sha256:b95e31850f3af163c2283ed40432f053acbc8fc6eba6a069cb518d9dbf71848c"}, + {file = "pylint-2.11.1-py3-none-any.whl", hash = "sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126"}, + {file = "pylint-2.11.1.tar.gz", hash = "sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436"}, ] pylint-celery = [ {file = "pylint-celery-0.3.tar.gz", hash = "sha256:41e32094e7408d15c044178ea828dd524beedbdbe6f83f712c5e35bde1de4beb"}, ] pylint-django = [ - {file = "pylint-django-2.0.15.tar.gz", hash = "sha256:06a64331c498a3f049ba669dc0c174b92209e164198d43e589b1096ee616d5f8"}, - {file = "pylint_django-2.0.15-py3-none-any.whl", hash = "sha256:3d3436ba8d0fae576ae2db160e33a8f2746a101fda4463f2b3ff3a8b6fccec38"}, + {file = "pylint-django-2.4.4.tar.gz", hash = "sha256:f63f717169b0c2e4e19c28f1c32c28290647330184fcb7427805ae9b6994f3fc"}, + {file = "pylint_django-2.4.4-py3-none-any.whl", hash = "sha256:aff49d9602a39c027b4ed7521a041438893205918f405800063b7ff692b7371b"}, ] pylint-flask = [ {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, @@ -1014,138 +550,101 @@ pylite = [ {file = "pylite-0.1.0-py3-none-any.whl", hash = "sha256:eb46f5beb1f2102672fd4355c013ac2feebc0df284d65f7711f2041a0a410141"}, {file = "pylite-0.1.0.tar.gz", hash = "sha256:e338d20d3f8f72dd84d1e58f2fd6dba008d593e0cfacfb5fbdd5a297b830628e"}, ] -pyparsing = [ - {file = "pyparsing-2.4.6-py2.py3-none-any.whl", hash = "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"}, - {file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"}, -] pytest = [ {file = "pytest-3.10.1-py2.py3-none-any.whl", hash = "sha256:3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec"}, {file = "pytest-3.10.1.tar.gz", hash = "sha256:e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"}, ] -python-jsonrpc-server = [ - {file = "python-jsonrpc-server-0.3.2.tar.gz", hash = "sha256:05bcf26eac4c98c96afec266acdf563d8f454e12612da9a3f9aabb66c46daf35"}, -] -python-language-server = [ - {file = "python-language-server-0.31.4.tar.gz", hash = "sha256:68d1a5ed20714e45ee417348ae46de45ab4ed32c6c02ad147cbb9d7ea5293adb"}, -] -pytz = [ - {file = "pytz-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"}, - {file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"}, -] pyyaml = [ - {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, - {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, - {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, - {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, - {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, -] -requests = [ - {file = "requests-2.22.0-py2.py3-none-any.whl", hash = "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"}, - {file = "requests-2.22.0.tar.gz", hash = "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4"}, + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] requirements-detector = [ {file = "requirements-detector-0.7.tar.gz", hash = "sha256:0d1e13e61ed243f9c3c86e6cbb19980bcb3a0e0619cde2ec1f3af70fdbee6f7b"}, ] -rope = [ - {file = "rope-0.16.0-py2-none-any.whl", hash = "sha256:ae1fa2fd56f64f4cc9be46493ce54bed0dd12dee03980c61a4393d89d84029ad"}, - {file = "rope-0.16.0-py3-none-any.whl", hash = "sha256:52423a7eebb5306a6d63bdc91a7c657db51ac9babfb8341c9a1440831ecf3203"}, - {file = "rope-0.16.0.tar.gz", hash = "sha256:d2830142c2e046f5fc26a022fe680675b6f48f81c7fc1f03a950706e746e9dfe"}, -] -setoptconf = [ - {file = "setoptconf-0.2.0.tar.gz", hash = "sha256:5b0b5d8e0077713f5d5152d4f63be6f048d9a1bb66be15d089a11c898c3cf49c"}, +setoptconf-tmp = [ + {file = "setoptconf-tmp-0.3.1.tar.gz", hash = "sha256:e0480addd11347ba52f762f3c4d8afa3e10ad0affbc53e3ffddc0ca5f27d5778"}, + {file = "setoptconf_tmp-0.3.1-py3-none-any.whl", hash = "sha256:76035d5cd1593d38b9056ae12d460eca3aaa34ad05c315b69145e138ba80a745"}, ] six = [ - {file = "six-1.13.0-py2.py3-none-any.whl", hash = "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd"}, - {file = "six-1.13.0.tar.gz", hash = "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] snowballstemmer = [ - {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"}, - {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, -] -sphinx = [ - {file = "Sphinx-2.3.1-py3-none-any.whl", hash = "sha256:298537cb3234578b2d954ff18c5608468229e116a9757af3b831c2b2b4819159"}, - {file = "Sphinx-2.3.1.tar.gz", hash = "sha256:e6e766b74f85f37a5f3e0773a1e1be8db3fcb799deb58ca6d18b70b0b44542a5"}, -] -sphinx-rtd-theme = [ - {file = "sphinx_rtd_theme-0.4.3-py2.py3-none-any.whl", hash = "sha256:00cf895504a7895ee433807c62094cf1e95f065843bf3acd17037c3e9a2becd4"}, - {file = "sphinx_rtd_theme-0.4.3.tar.gz", hash = "sha256:728607e34d60456d736cc7991fd236afb828b21b82f956c5ea75f94c8414040a"}, -] -sphinxcontrib-applehelp = [ - {file = "sphinxcontrib-applehelp-1.0.1.tar.gz", hash = "sha256:edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897"}, - {file = "sphinxcontrib_applehelp-1.0.1-py2.py3-none-any.whl", hash = "sha256:fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d"}, -] -sphinxcontrib-devhelp = [ - {file = "sphinxcontrib-devhelp-1.0.1.tar.gz", hash = "sha256:6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34"}, - {file = "sphinxcontrib_devhelp-1.0.1-py2.py3-none-any.whl", hash = "sha256:9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981"}, -] -sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib-htmlhelp-1.0.2.tar.gz", hash = "sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422"}, - {file = "sphinxcontrib_htmlhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"}, -] -sphinxcontrib-jsmath = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] -sphinxcontrib-qthelp = [ - {file = "sphinxcontrib-qthelp-1.0.2.tar.gz", hash = "sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f"}, - {file = "sphinxcontrib_qthelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20"}, -] -sphinxcontrib-serializinghtml = [ - {file = "sphinxcontrib-serializinghtml-1.1.3.tar.gz", hash = "sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227"}, - {file = "sphinxcontrib_serializinghtml-1.1.3-py2.py3-none-any.whl", hash = "sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768"}, + {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, + {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] toml = [ - {file = "toml-0.10.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"}, - {file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"}, - {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"}, + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] typed-ast = [ - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, - {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, - {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, - {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, - {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, - {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, - {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, + {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, + {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, + {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, + {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, + {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, + {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, + {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, + {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, + {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] -ujson = [ - {file = "ujson-1.35.tar.gz", hash = "sha256:f66073e5506e91d204ab0c614a148d5aa938bdbf104751be66f8ad7a222f5f86"}, -] -urllib3 = [ - {file = "urllib3-1.25.7-py2.py3-none-any.whl", hash = "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293"}, - {file = "urllib3-1.25.7.tar.gz", hash = "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"}, -] -urwid = [ - {file = "urwid-2.1.0.tar.gz", hash = "sha256:0896f36060beb6bf3801cb554303fef336a79661401797551ba106d23ab4cd86"}, +typing-extensions = [ + {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, + {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, + {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, ] wrapt = [ {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, ] -yapf = [ - {file = "yapf-0.29.0-py2.py3-none-any.whl", hash = "sha256:cad8a272c6001b3401de3278238fdc54997b6c2e56baa751788915f879a52fca"}, - {file = "yapf-0.29.0.tar.gz", hash = "sha256:712e23c468506bf12cadd10169f852572ecc61b266258422d45aaf4ad7ef43de"}, -] zipp = [ - {file = "zipp-0.6.0-py2.py3-none-any.whl", hash = "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"}, - {file = "zipp-0.6.0.tar.gz", hash = "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e"}, + {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, + {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, ] diff --git a/pyproject.toml b/pyproject.toml index e40ed53..175c6a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,17 +29,8 @@ pylite = "^0.1.0" [tool.poetry.dev-dependencies] pytest = "^3.0" -autopep8 = "^1.4" -pudb = "^2019.2" -McCabe = "^0.6.1" -YAPF = "^0.29.0" -pydocstyle = "^5.0" -Pyflakes = "^2.1" -Rope = "^0.16.0" -python-language-server = "^0.31.4" -pycodestyle = "^2.5" -sphinx = "^2.3" -sphinx_rtd_theme = "^0.4.3" +# sphinx = "^2.3" +# sphinx_rtd_theme = "^0.4.3" prospector = "^1.3.0" [build-system] From 2885ec8903b7f0f767b6aab8fc9a18e053881725 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Fri, 15 Oct 2021 04:14:25 +0100 Subject: [PATCH 02/33] adding latest --- .vscode/settings.json | 2 +- panaetius/__init__.py | 0 panaetius/config.py | 80 +++++++++++ poetry.lock | 264 +++++++++++++++++++++++++++++------ prospector.yaml | 110 +++++++++++++++ pyproject.toml | 5 +- rewrite.todo | 2 + src/panaetius/config.py | 29 ---- src/panaetius/config_inst.py | 6 +- 9 files changed, 417 insertions(+), 81 deletions(-) create mode 100644 panaetius/__init__.py create mode 100644 panaetius/config.py create mode 100644 prospector.yaml create mode 100644 rewrite.todo diff --git a/.vscode/settings.json b/.vscode/settings.json index 32ee17e..31b5b02 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,4 +4,4 @@ "python.linting.enabled": true, "python.pythonPath": ".venv/bin/python", "restructuredtext.confPath": "${workspaceFolder}/docs/source" -} \ No newline at end of file +} diff --git a/panaetius/__init__.py b/panaetius/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/panaetius/config.py b/panaetius/config.py new file mode 100644 index 0000000..6e0e274 --- /dev/null +++ b/panaetius/config.py @@ -0,0 +1,80 @@ +from __future__ import annotations +import os +import pathlib +from typing import Any + +import toml + + +class Config: + """docstring for Config().""" + + def __init__(self, header_variable: str, config_path: str = "") -> None: + self.header_variable = header_variable + self.config_path = ( + pathlib.Path(config_path) + if config_path + else pathlib.Path.home() / ".config" + ) + self._missing_config = False + + @property + def config(self) -> dict[str, Any]: + config_file_location = self.config_path / self.header_variable / "config.toml" + print(config_file_location) + try: + with open(config_file_location, "r", encoding="utf-8") as config_file: + return dict(toml.load(config_file)) + except FileNotFoundError: + self._missing_config = True + return {} + + def get_value( + self, key: str, default: str, cast: Any = None, mask: bool = False + ) -> Any: + env_key = f"{self.header_variable.upper()}_{key.upper().replace('.', '_')}" + + # look in the config file + if not self._missing_config: + try: + # look under top header + if len(key.split(".")) == 1: + if mask: + # TODO: write mask logic here + pass + else: + name = key.lower() + value = self.config[self.header_variable][name] + elif len(key.split(".")) == 2: + if mask: + # TODO: write mask logic here + pass + else: + section, name = key.lower().split(".") + value = self.config[self.header_variable][section][name] + # TODO: set a custom error and move this to the end to tell user that incorrect key was given and couldn't be found + # try: + # return cast(value) if cast else value + # except UnboundLocalError: + # # pass if nothing was found + # pass + except KeyError: + # key not found in config file + pass + except TypeError: + # key not found in config file + pass + + # look for an environment variable + value = os.environ.get(env_key.replace("-", "_"), default) + + +if __name__ == "__main__": + os.environ["PANAETIUS_PATH"] = "/usr/local" + c = Config("panaetius_test") + print(c.config) + c.get_value("path", "some path") + c.get_value("top", "str", list) + c.get_value("logging.path", "str", list) + c.get_value("nonexistent.item", "some default value") + pass diff --git a/poetry.lock b/poetry.lock index f2ac3ef..63d24c2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -34,6 +34,21 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] +[[package]] +name = "bandit" +version = "1.7.0" +description = "Security oriented static analyser for python code." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +GitPython = ">=1.0.1" +PyYAML = ">=5.3.1" +six = ">=1.10.0" +stevedore = ">=1.20.0" + [[package]] name = "colorama" version = "0.4.4" @@ -52,17 +67,16 @@ python-versions = "*" [[package]] name = "flake8" -version = "3.9.2" -description = "the modular source code checker: pep8 pyflakes and co" +version = "2.3.0" +description = "the modular source code checker: pep8, pyflakes and co" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = "*" [package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.7.0,<2.8.0" -pyflakes = ">=2.3.0,<2.4.0" +mccabe = ">=0.2.1" +pep8 = ">=1.5.7" +pyflakes = ">=0.8.1" [[package]] name = "flake8-polyfill" @@ -75,6 +89,29 @@ python-versions = "*" [package.dependencies] flake8 = "*" +[[package]] +name = "gitdb" +version = "4.0.7" +description = "Git Object Database" +category = "dev" +optional = false +python-versions = ">=3.4" + +[package.dependencies] +smmap = ">=3.0.1,<5" + +[[package]] +name = "gitpython" +version = "3.1.24" +description = "GitPython is a python library used to interact with Git repositories" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +gitdb = ">=4.0.1,<5" +typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} + [[package]] name = "importlib-metadata" version = "4.8.1" @@ -130,6 +167,48 @@ category = "dev" optional = false python-versions = ">=3.5" +[[package]] +name = "mypy" +version = "0.910" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +mypy-extensions = ">=0.4.3,<0.5.0" +toml = "*" +typed-ast = {version = ">=1.4.0,<1.5.0", markers = "python_version < \"3.8\""} +typing-extensions = ">=3.7.4" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<1.5.0)"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "pbr" +version = "5.6.0" +description = "Python Build Reasonableness" +category = "dev" +optional = false +python-versions = ">=2.6" + +[[package]] +name = "pep8" +version = "1.7.1" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "pep8-naming" version = "0.10.0" @@ -177,8 +256,10 @@ optional = false python-versions = ">=3.6.1,<4.0" [package.dependencies] +bandit = {version = ">=1.5.1", optional = true, markers = "extra == \"with_bandit\" or extra == \"with_everything\""} dodgy = ">=0.2.1,<0.3.0" mccabe = ">=0.6.0,<0.7.0" +mypy = {version = ">=0.600", optional = true, markers = "extra == \"with_mypy\" or extra == \"with_everything\""} pep8-naming = ">=0.3.3,<=0.10.0" pycodestyle = ">=2.6.0,<2.9.0" pydocstyle = ">=2.0.0" @@ -211,11 +292,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pycodestyle" -version = "2.7.0" +version = "2.8.0" description = "Python style guide checker" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pydocstyle" @@ -334,11 +415,11 @@ six = ">=1.10.0" [[package]] name = "pyyaml" -version = "5.4.1" +version = "6.0" description = "YAML parser and emitter for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.6" [[package]] name = "requirements-detector" @@ -370,6 +451,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "smmap" +version = "4.0.0" +description = "A pure Python implementation of a sliding window memory map manager" +category = "dev" +optional = false +python-versions = ">=3.5" + [[package]] name = "snowballstemmer" version = "2.1.0" @@ -378,6 +467,18 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "stevedore" +version = "3.4.0" +description = "Manage dynamic plugins for Python applications" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + [[package]] name = "toml" version = "0.10.2" @@ -394,6 +495,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "types-toml" +version = "0.10.1" +description = "Typing stubs for toml" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "typing-extensions" version = "3.10.0.2" @@ -425,7 +534,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "68bd5fa009bc4b1089842f9dd5316e839a579bc28077bd7efefea94c815a7852" +content-hash = "09c1545b730544eb1d67b233f4446b9175031db78a91cd662e0c84af56bf8023" [metadata.files] astroid = [ @@ -440,6 +549,10 @@ attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] +bandit = [ + {file = "bandit-1.7.0-py3-none-any.whl", hash = "sha256:216be4d044209fa06cf2a3e51b319769a51be8318140659719aa7a115c35ed07"}, + {file = "bandit-1.7.0.tar.gz", hash = "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"}, +] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, @@ -449,13 +562,21 @@ dodgy = [ {file = "dodgy-0.2.1.tar.gz", hash = "sha256:28323cbfc9352139fdd3d316fa17f325cc0e9ac74438cbba51d70f9b48f86c3a"}, ] flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, + {file = "flake8-2.3.0-py2.py3-none-any.whl", hash = "sha256:c99cc9716d6655d9c8bcb1e77632b8615bf0abd282d7abd9f5c2148cad7fc669"}, + {file = "flake8-2.3.0.tar.gz", hash = "sha256:5ee1a43ccd0716d6061521eec6937c983efa027793013e572712c4da55c7c83e"}, ] flake8-polyfill = [ {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, ] +gitdb = [ + {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"}, + {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"}, +] +gitpython = [ + {file = "GitPython-3.1.24-py3-none-any.whl", hash = "sha256:dc0a7f2f697657acc8d7f89033e8b1ea94dd90356b2983bca89dc8d2ab3cc647"}, + {file = "GitPython-3.1.24.tar.gz", hash = "sha256:df83fdf5e684fef7c6ee2c02fc68a5ceb7e7e759d08b694088d0cacb4eba59e5"}, +] importlib-metadata = [ {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, @@ -496,6 +617,43 @@ more-itertools = [ {file = "more-itertools-8.10.0.tar.gz", hash = "sha256:1debcabeb1df793814859d64a81ad7cb10504c24349368ccf214c664c474f41f"}, {file = "more_itertools-8.10.0-py3-none-any.whl", hash = "sha256:56ddac45541718ba332db05f464bebfb0768110111affd27f66e0051f276fa43"}, ] +mypy = [ + {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, + {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, + {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, + {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, + {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, + {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, + {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, + {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, + {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, + {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, + {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, + {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, + {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, + {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, + {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, + {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, + {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, + {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, + {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, + {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, + {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, + {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, + {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +pbr = [ + {file = "pbr-5.6.0-py2.py3-none-any.whl", hash = "sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"}, + {file = "pbr-5.6.0.tar.gz", hash = "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"}, +] +pep8 = [ + {file = "pep8-1.7.1-py2.py3-none-any.whl", hash = "sha256:b22cfae5db09833bb9bd7c8463b53e1a9c9b39f12e304a8d0bba729c501827ee"}, + {file = "pep8-1.7.1.tar.gz", hash = "sha256:fe249b52e20498e59e0b5c5256aa52ee99fc295b26ec9eaa85776ffdb9fe6374"}, +] pep8-naming = [ {file = "pep8-naming-0.10.0.tar.gz", hash = "sha256:f3b4a5f9dd72b991bf7d8e2a341d2e1aa3a884a769b5aaac4f56825c1763bf3a"}, {file = "pep8_naming-0.10.0-py2.py3-none-any.whl", hash = "sha256:5d9f1056cb9427ce344e98d1a7f5665710e2f20f748438e308995852cfa24164"}, @@ -517,8 +675,8 @@ py = [ {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pycodestyle = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, + {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, + {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, ] pydocstyle = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, @@ -555,35 +713,39 @@ pytest = [ {file = "pytest-3.10.1.tar.gz", hash = "sha256:e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"}, ] pyyaml = [ - {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, - {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, - {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, - {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, - {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, - {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, - {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, - {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, - {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] requirements-detector = [ {file = "requirements-detector-0.7.tar.gz", hash = "sha256:0d1e13e61ed243f9c3c86e6cbb19980bcb3a0e0619cde2ec1f3af70fdbee6f7b"}, @@ -596,10 +758,18 @@ six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +smmap = [ + {file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"}, + {file = "smmap-4.0.0.tar.gz", hash = "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182"}, +] snowballstemmer = [ {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] +stevedore = [ + {file = "stevedore-3.4.0-py3-none-any.whl", hash = "sha256:920ce6259f0b2498aaa4545989536a27e4e4607b8318802d7ddc3a533d3d069e"}, + {file = "stevedore-3.4.0.tar.gz", hash = "sha256:59b58edb7f57b11897f150475e7bc0c39c5381f0b8e3fa9f5c20ce6c89ec4aa1"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -636,6 +806,10 @@ typed-ast = [ {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] +types-toml = [ + {file = "types-toml-0.10.1.tar.gz", hash = "sha256:5c1f8f8d57692397c8f902bf6b4d913a0952235db7db17d2908cc110e70610cb"}, + {file = "types_toml-0.10.1-py3-none-any.whl", hash = "sha256:8cdfd2b7c89bed703158b042dd5cf04255dae77096db66f4a12ca0a93ccb07a5"}, +] typing-extensions = [ {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, diff --git a/prospector.yaml b/prospector.yaml new file mode 100644 index 0000000..4fa0003 --- /dev/null +++ b/prospector.yaml @@ -0,0 +1,110 @@ +output-format: vscode +doc-warnings: true +strictness: none + +ignore-patterns: + - (^|/)\..+ + +# https://pylint.pycqa.org/en/latest/technical_reference/features.html +pylint: + run: true + disable: + # disables TODO warnings + - fixme + # !doc missing docstrings + - missing-module-docstring + - missing-class-docstring + - missing-function-docstring + # disables warnings about abstract methods not overridden + - abstract-method + # used when an ancestor class method has an __init__ method which is not called by a derived class. + - super-init-not-called + # either all return statements in a function should return an expression, or none of them should. + # - inconsistent-return-statements + # Used when an expression that is not a function call is assigned to nothing. Probably something else was intended. + # - expression-not-assigned + # Used when a line is longer than a given number of characters. + - line-too-long + enable: + options: + max-locals: 15 + max-returns: 6 + max-branches: 12 + max-statements: 50 + max-parents: 7 + max-attributes: 20 + min-public-methods: 0 + max-public-methods: 25 + max-module-lines: 1000 + max-line-length: 88 + +mccabe: + run: true + options: + max-complexity: 10 + +# https://pep8.readthedocs.io/en/release-1.7.x/intro.html#error-codes +pep8: + run: true + options: + max-line-length: 88 + single-line-if-stmt: n + disable: + # line too long + - E501 + +pyroma: + run: false + disable: + - PYR19 + - PYR16 + +# https://pep257.readthedocs.io/en/latest/error_codes.html +# http://www.pydocstyle.org/en/6.1.1/error_codes.html +pep257: + disable: + # !doc docstrings + # Missing docstring in __init__ + - D107 + # Missing docstring in public module + - D100 + # Missing docstring in public class + - D101 + # Missing docstring in public method + - D102 + # Missing docstring in public function + - D103 + # 1 blank line required before class docstring + - D203 + # Multi-line docstring summary should start at the first line + - D212 + # Multi-line docstring summary should start at the second line + - D213 + # First word of the docstring should not be This + - D404 + # Section name should end with a newline + - D406 + # Missing dashed underline after section + - D407 + # Missing blank line after last section + - D413 + +# https://flake8.pycqa.org/en/latest/user/error-codes.html +pyflakes: + disable: + # module imported but unused + - F401 + +dodgy: + run: true + +bandit: + run: true + +mypy: + run: true + options: + # https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-type-hints-for-third-party-library + ignore-missing-imports: true + # https://mypy.readthedocs.io/en/stable/running_mypy.html#following-imports + follow-imports: normal diff --git a/pyproject.toml b/pyproject.toml index 175c6a5..62c0a8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,10 +28,11 @@ toml = "^0.10.0" pylite = "^0.1.0" [tool.poetry.dev-dependencies] -pytest = "^3.0" +pytest = "^3.0" # TODO: Update to latest # sphinx = "^2.3" # sphinx_rtd_theme = "^0.4.3" -prospector = "^1.3.0" +prospector = {extras = ["with_bandit", "with_mypy"], version = "^1.5.1"} +types-toml = "^0.10.1" [build-system] requires = ["poetry>=0.12"] diff --git a/rewrite.todo b/rewrite.todo new file mode 100644 index 0000000..cbfd08e --- /dev/null +++ b/rewrite.todo @@ -0,0 +1,2 @@ +Documentation: + ☐ Rewrite documentation using `mkdocs` and using `.md`. @2h diff --git a/src/panaetius/config.py b/src/panaetius/config.py index 5cceb83..be8bfd0 100644 --- a/src/panaetius/config.py +++ b/src/panaetius/config.py @@ -9,7 +9,6 @@ from panaetius.db import Mask # __all__ = ['Config'] -@export class Config: """Handles the config options for the module and stores config variables @@ -52,34 +51,6 @@ class Config: def read_config(self, path: str, write: bool = False) -> Union[dict, None]: """Reads the toml config file from `path` if it exists. - Parameters - ---------- - path : str - Path to config file. Should not contain `config.toml` - header : str - Header to overwrite if using in a module. - - Example: ``path = '~/.config/panaetius'`` - - Returns - ------- - Union[dict, None] - Returns a dict if the file is found else returns nothing. - - The dict contains a key for each header. Each key corresponds to a - dictionary containing a key, value pair for each config under - that header. - - Example:: - - [panaetius] - - [panaetius.foo] - foo = bar - - Returns a dict: - - ``{'panaetius' : {foo: {'foo': 'bar'}}}`` """ path += 'config.toml' if path[-1] == '/' else '/config.toml' diff --git a/src/panaetius/config_inst.py b/src/panaetius/config_inst.py index 61d90c3..43f0d1b 100644 --- a/src/panaetius/config_inst.py +++ b/src/panaetius/config_inst.py @@ -4,8 +4,6 @@ from panaetius.header import __header__ from panaetius.config import Config -DEFAULT_CONFIG_PATH = f'~/.config/{__header__.lower()}' -CONFIG_PATH = os.environ.get( - f'{__header__.upper()}_CONFIG_PATH', DEFAULT_CONFIG_PATH -) +DEFAULT_CONFIG_PATH = f"~/.config/{__header__.lower()}" +CONFIG_PATH = os.environ.get(f"{__header__.upper()}_CONFIG_PATH", DEFAULT_CONFIG_PATH) CONFIG = Config(CONFIG_PATH) From b9721f6ee4b845fc8bc77390fe5a0aba302781df Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 02:16:53 +0100 Subject: [PATCH 03/33] adding latest --- .vscode/settings.json | 18 ++++++++++- {src => old_src}/panaetius/__init__.py | 0 {src => old_src}/panaetius/__version__.py | 0 {src => old_src}/panaetius/config.py | 0 {src => old_src}/panaetius/config_inst.py | 0 {src => old_src}/panaetius/db.py | 0 {src => old_src}/panaetius/header.py | 0 {src => old_src}/panaetius/library.py | 0 {src => old_src}/panaetius/logging.py | 0 panaetius/__init__.py | 2 ++ panaetius/config.py | 38 +++++++++-------------- panaetius/library.py | 24 ++++++++++++++ tests/test_panaetius.py | 25 +++++++++++++-- 13 files changed, 80 insertions(+), 27 deletions(-) rename {src => old_src}/panaetius/__init__.py (100%) rename {src => old_src}/panaetius/__version__.py (100%) rename {src => old_src}/panaetius/config.py (100%) rename {src => old_src}/panaetius/config_inst.py (100%) rename {src => old_src}/panaetius/db.py (100%) rename {src => old_src}/panaetius/header.py (100%) rename {src => old_src}/panaetius/library.py (100%) rename {src => old_src}/panaetius/logging.py (100%) create mode 100644 panaetius/library.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 31b5b02..3525082 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,21 @@ "python.linting.prospectorEnabled": true, "python.linting.enabled": true, "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" } diff --git a/src/panaetius/__init__.py b/old_src/panaetius/__init__.py similarity index 100% rename from src/panaetius/__init__.py rename to old_src/panaetius/__init__.py diff --git a/src/panaetius/__version__.py b/old_src/panaetius/__version__.py similarity index 100% rename from src/panaetius/__version__.py rename to old_src/panaetius/__version__.py diff --git a/src/panaetius/config.py b/old_src/panaetius/config.py similarity index 100% rename from src/panaetius/config.py rename to old_src/panaetius/config.py diff --git a/src/panaetius/config_inst.py b/old_src/panaetius/config_inst.py similarity index 100% rename from src/panaetius/config_inst.py rename to old_src/panaetius/config_inst.py diff --git a/src/panaetius/db.py b/old_src/panaetius/db.py similarity index 100% rename from src/panaetius/db.py rename to old_src/panaetius/db.py diff --git a/src/panaetius/header.py b/old_src/panaetius/header.py similarity index 100% rename from src/panaetius/header.py rename to old_src/panaetius/header.py diff --git a/src/panaetius/library.py b/old_src/panaetius/library.py similarity index 100% rename from src/panaetius/library.py rename to old_src/panaetius/library.py diff --git a/src/panaetius/logging.py b/old_src/panaetius/logging.py similarity index 100% rename from src/panaetius/logging.py rename to old_src/panaetius/logging.py diff --git a/panaetius/__init__.py b/panaetius/__init__.py index e69de29..866a529 100644 --- a/panaetius/__init__.py +++ b/panaetius/__init__.py @@ -0,0 +1,2 @@ +from panaetius.config import Config +from panaetius.library import set_config diff --git a/panaetius/config.py b/panaetius/config.py index 6e0e274..f527b41 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -29,9 +29,8 @@ class Config: self._missing_config = True return {} - def get_value( - self, key: str, default: str, cast: Any = None, mask: bool = False - ) -> Any: + # TODO: fix the return error + def get_value(self, key: str, default: str | None, mask: bool) -> Any: env_key = f"{self.header_variable.upper()}_{key.upper().replace('.', '_')}" # look in the config file @@ -52,29 +51,22 @@ class Config: else: section, name = key.lower().split(".") value = self.config[self.header_variable][section][name] + return value # TODO: set a custom error and move this to the end to tell user that incorrect key was given and couldn't be found # try: # return cast(value) if cast else value # except UnboundLocalError: # # pass if nothing was found # pass - except KeyError: - # key not found in config file - pass - except TypeError: - # key not found in config file - pass - - # look for an environment variable - value = os.environ.get(env_key.replace("-", "_"), default) - - -if __name__ == "__main__": - os.environ["PANAETIUS_PATH"] = "/usr/local" - c = Config("panaetius_test") - print(c.config) - c.get_value("path", "some path") - c.get_value("top", "str", list) - c.get_value("logging.path", "str", list) - c.get_value("nonexistent.item", "some default value") - pass + except (KeyError, TypeError): + value = os.environ.get(env_key.replace("-", "_")) + if value is None: + return toml.loads(default) if default is not None else None + return toml.loads(value) + else: + # look for an environment variable, fallback to default + value = os.environ.get(env_key.replace("-", "_")) + if value is None: + return toml.loads(default) if default is not None else None + return toml.loads(value) + return default diff --git a/panaetius/library.py b/panaetius/library.py new file mode 100644 index 0000000..1a016ee --- /dev/null +++ b/panaetius/library.py @@ -0,0 +1,24 @@ +from __future__ import annotations +import ast +from typing import Any + +from panaetius import Config + + +def set_config( + config_inst: Config, + key: str, + default: str | None = None, + mask: bool = False, + coerce: bool = False, +): # sourcery skip: remove-redundant-pass + config_var = key.lower().replace(".", "_") + if not coerce: + setattr(config_inst, config_var, config_inst.get_value(key, default, mask)) + elif type(config_inst.get_value(key, default, mask)) is not coerce: # noqa + var = ast.literal_eval(config_inst.get_value(key, default, mask)) + if isinstance(var, (list, dict)): + setattr(config_inst, config_var, var) + else: + pass + # TODO: raise error to say type of coersion isn't valid! diff --git a/tests/test_panaetius.py b/tests/test_panaetius.py index caeeb82..c9d66dd 100644 --- a/tests/test_panaetius.py +++ b/tests/test_panaetius.py @@ -1,5 +1,24 @@ -from panaetius import __version__ +import os +from panaetius import Config, set_config -def test_version(): - assert __version__ == '0.1.0' +if __name__ == "__main__": + os.environ["PANAETIUS_TEST_PATH"] = "/usr/local" + print(os.environ.get("PANAETIUS_TEST_PATH")) + os.environ[ + "PANAETIUS_TEST_TOML_POINTS" + ] = "[ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 }]" + c = Config("panaetius_test") + # print(c.config) + # c.get_value("path", "some path") + # c.get_value("top", "str") + # c.get_value("logging.path", "str") + # c.get_value("nonexistent.item", "some default value") + set_config(c, key="path", default="some path") + set_config(c, key="top", default="some top") + set_config(c, key="logging.path", default="some logging path") + set_config(c, key="nonexistent.item", default="some nonexistent item") + set_config(c, key="nonexistent.item") + set_config(c, key="toml.points") + print(c) + pass From 2c0735fedfbc80b640285b5c883806da7f183fac Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 04:47:32 +0100 Subject: [PATCH 04/33] adding latest working --- panaetius/config.py | 49 ++++++++++++++++++++++++++++++----------- panaetius/library.py | 16 ++++---------- tests/test_panaetius.py | 12 +++++----- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index f527b41..cb03d92 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -21,7 +21,6 @@ class Config: @property def config(self) -> dict[str, Any]: config_file_location = self.config_path / self.header_variable / "config.toml" - print(config_file_location) try: with open(config_file_location, "r", encoding="utf-8") as config_file: return dict(toml.load(config_file)) @@ -30,7 +29,9 @@ class Config: return {} # TODO: fix the return error - def get_value(self, key: str, default: str | None, mask: bool) -> Any: + def get_value( + self, key: str, default: Any, mask: bool, coerce: bool = False + ) -> Any: env_key = f"{self.header_variable.upper()}_{key.upper().replace('.', '_')}" # look in the config file @@ -52,21 +53,43 @@ class Config: section, name = key.lower().split(".") value = self.config[self.header_variable][section][name] return value - # TODO: set a custom error and move this to the end to tell user that incorrect key was given and couldn't be found - # try: - # return cast(value) if cast else value - # except UnboundLocalError: - # # pass if nothing was found - # pass + except (KeyError, TypeError): value = os.environ.get(env_key.replace("-", "_")) if value is None: - return toml.loads(default) if default is not None else None - return toml.loads(value) + if isinstance(default, str): + # if default is a string, wrap TOML value in quotes + return toml.loads(f'value = "{default}"')["value"] + # if default is not a string, leave TOML value as is + return ( + toml.loads(f"value = {default}")["value"] + if default is not None + else None + ) + # if env var, coerce value if flag is set, else return a TOML string + return ( + toml.loads(f"value = {value}")["value"] + if coerce + else toml.loads(f'value = "{value}"')["value"] + ) + else: # look for an environment variable, fallback to default value = os.environ.get(env_key.replace("-", "_")) if value is None: - return toml.loads(default) if default is not None else None - return toml.loads(value) - return default + return ( + toml.loads(f'value = "{default}"')["value"] + if default is not None + else None + ) + return toml.loads(f'value = "{value}"')["value"] + + def _get_config_value( + self, key: str, default: Any, mask: bool, coerce: bool = False + ) -> Any: + pass + + def _get_env_value( + self, key: str, default: Any, mask: bool, coerce: bool = False + ) -> Any: + pass diff --git a/panaetius/library.py b/panaetius/library.py index 1a016ee..d01fc0f 100644 --- a/panaetius/library.py +++ b/panaetius/library.py @@ -1,5 +1,5 @@ from __future__ import annotations -import ast + from typing import Any from panaetius import Config @@ -8,17 +8,9 @@ from panaetius import Config def set_config( config_inst: Config, key: str, - default: str | None = None, + default: Any = None, mask: bool = False, coerce: bool = False, -): # sourcery skip: remove-redundant-pass +): config_var = key.lower().replace(".", "_") - if not coerce: - setattr(config_inst, config_var, config_inst.get_value(key, default, mask)) - elif type(config_inst.get_value(key, default, mask)) is not coerce: # noqa - var = ast.literal_eval(config_inst.get_value(key, default, mask)) - if isinstance(var, (list, dict)): - setattr(config_inst, config_var, var) - else: - pass - # TODO: raise error to say type of coersion isn't valid! + setattr(config_inst, config_var, config_inst.get_value(key, default, mask, coerce)) diff --git a/tests/test_panaetius.py b/tests/test_panaetius.py index c9d66dd..8979659 100644 --- a/tests/test_panaetius.py +++ b/tests/test_panaetius.py @@ -9,16 +9,16 @@ if __name__ == "__main__": "PANAETIUS_TEST_TOML_POINTS" ] = "[ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 }]" c = Config("panaetius_test") - # print(c.config) - # c.get_value("path", "some path") - # c.get_value("top", "str") - # c.get_value("logging.path", "str") - # c.get_value("nonexistent.item", "some default value") + set_config(c, key="path", default="some path") set_config(c, key="top", default="some top") set_config(c, key="logging.path", default="some logging path") set_config(c, key="nonexistent.item", default="some nonexistent item") set_config(c, key="nonexistent.item") - set_config(c, key="toml.points") + set_config(c, key="toml.points", coerce=True) + set_config(c, key="toml.points_config") + set_config(c, key="float", default=2.0) + set_config(c, key="float_str", default="2.0") + print(c) pass From 8dfae288324cadbebd290f3b7407e7ab675981d3 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 05:24:56 +0100 Subject: [PATCH 05/33] adding latest working --- panaetius/config.py | 107 ++++++++++++++++++++++------------------ tests/test_panaetius.py | 42 ++++++++++++---- 2 files changed, 91 insertions(+), 58 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index cb03d92..3a0bc30 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -34,62 +34,73 @@ class Config: ) -> Any: env_key = f"{self.header_variable.upper()}_{key.upper().replace('.', '_')}" - # look in the config file if not self._missing_config: - try: - # look under top header - if len(key.split(".")) == 1: - if mask: - # TODO: write mask logic here - pass - else: - name = key.lower() - value = self.config[self.header_variable][name] - elif len(key.split(".")) == 2: - if mask: - # TODO: write mask logic here - pass - else: - section, name = key.lower().split(".") - value = self.config[self.header_variable][section][name] - return value + # look in the config file + return self._get_config_value(env_key, key, default, mask, coerce) + # no config file, look for env vars + return self._get_env_value(env_key, default, coerce) - except (KeyError, TypeError): - value = os.environ.get(env_key.replace("-", "_")) - if value is None: - if isinstance(default, str): - # if default is a string, wrap TOML value in quotes - return toml.loads(f'value = "{default}"')["value"] - # if default is not a string, leave TOML value as is - return ( - toml.loads(f"value = {default}")["value"] - if default is not None - else None - ) - # if env var, coerce value if flag is set, else return a TOML string - return ( - toml.loads(f"value = {value}")["value"] - if coerce - else toml.loads(f'value = "{value}"')["value"] - ) + def _get_config_value( + self, env_key: str, key: str, default: Any, mask: bool, coerce: bool = False + ) -> Any: + try: + # look under top header + if len(key.split(".")) == 1: + if mask: + # TODO: write mask logic here + pass + else: + name = key.lower() + value = self.config[self.header_variable][name] + elif len(key.split(".")) == 2: + if mask: + # TODO: write mask logic here + pass + else: + section, name = key.lower().split(".") + value = self.config[self.header_variable][section][name] + return value - else: - # look for an environment variable, fallback to default + except (KeyError, TypeError): value = os.environ.get(env_key.replace("-", "_")) if value is None: + if isinstance(default, str): + # if default is a string, wrap TOML value in quotes + return toml.loads(f'value = "{default}"')["value"] + # if default is not a string, leave TOML value as is + # if default is a bool, convert to lower case for TOML syntax + default = str(default).lower() if isinstance(default, bool) else default return ( - toml.loads(f'value = "{default}"')["value"] + toml.loads(f"value = {default}")["value"] if default is not None else None ) - return toml.loads(f'value = "{value}"')["value"] + # if env var, coerce value if flag is set, else return a TOML string + return ( + toml.loads(f"value = {value}")["value"] + if coerce + else toml.loads(f'value = "{value}"')["value"] + ) - def _get_config_value( - self, key: str, default: Any, mask: bool, coerce: bool = False + def _get_env_value( # noqa + self, env_key: str, default: Any, coerce: bool = False ) -> Any: - pass - - def _get_env_value( - self, key: str, default: Any, mask: bool, coerce: bool = False - ) -> Any: - pass + # look for an environment variable, fallback to default + value = os.environ.get(env_key.replace("-", "_")) + if value is None: + if isinstance(default, str): + # if default is a string, wrap TOML value in quotes + return toml.loads(f'value = "{default}"')["value"] + # if default is not a string, leave TOML value as is + # if default is a bool, convert to lower case for 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 + ) + return ( + toml.loads(f"value = {value}")["value"] + if coerce + else toml.loads(f'value = "{value}"')["value"] + ) diff --git a/tests/test_panaetius.py b/tests/test_panaetius.py index 8979659..5d66052 100644 --- a/tests/test_panaetius.py +++ b/tests/test_panaetius.py @@ -8,17 +8,39 @@ if __name__ == "__main__": os.environ[ "PANAETIUS_TEST_TOML_POINTS" ] = "[ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 }]" - c = Config("panaetius_test") - set_config(c, key="path", default="some path") - set_config(c, key="top", default="some top") - set_config(c, key="logging.path", default="some logging path") - set_config(c, key="nonexistent.item", default="some nonexistent item") - set_config(c, key="nonexistent.item") - set_config(c, key="toml.points", coerce=True) - set_config(c, key="toml.points_config") - set_config(c, key="float", default=2.0) - set_config(c, key="float_str", default="2.0") + os.environ["PANAETIUS_TEST_NOC_EMBEDDED_PATH"] = "/usr/local" + os.environ["PANAETIUS_TEST_NOC_EMBEDDED_FLOAT"] = "2.0" + os.environ["PANAETIUS_TEST_NOC_EMBEDDED_BOOL"] = "true" + + # c = Config("panaetius_test") + c = Config("panaetius_test_noc") + + # set_config(c, key="path", default="some path") + # set_config(c, key="top", default="some top") + # set_config(c, key="logging.path", default="some logging path") + # set_config(c, key="nonexistent.item", default="some nonexistent item") + # set_config(c, key="nonexistent.item") + # set_config(c, key="toml.points", coerce=True) + # set_config(c, key="toml.points_config") + # set_config(c, key="float", default=2.0) + # set_config(c, key="float_str", default="2.0") + # set_config(c, key="bool", coerce=True) + # set_config(c, key="noexistbool", default=False) + + # set_config(c, key="path") + # set_config(c, key="float", coerce=True) + # set_config(c, key="bool", coerce=True) + # set_config(c, key="noexiststr", default="2.0") + # set_config(c, key="noexistfloat", default=2.0) + # set_config(c, key="noexistbool", default=False) + + set_config(c, key="embedded.path") + set_config(c, key="embedded.float", coerce=True) + set_config(c, key="embedded.bool", coerce=True) + set_config(c, key="embedded.noexiststr", default="2.0") + set_config(c, key="embedded.noexistfloat", default=2.0) + set_config(c, key="embedded.noexistbool", default=False) print(c) pass From 517fe974c6cf4fc903cb3e05c788bdecf0abcae4 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 05:25:00 +0100 Subject: [PATCH 06/33] updating todo --- rewrite.todo | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rewrite.todo b/rewrite.todo index cbfd08e..deed8d0 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -1,2 +1,15 @@ +Coding: + No Config File: + ☐ Handle if a bool is passed in as a default + needs to be lower case in the toml, need a check for this + + Config File: + ☐ Handle if a bool is passed in as a default + needs to be lower case in the toml, need a check for this + + Documentation: ☐ Rewrite documentation using `mkdocs` and using `.md`. @2h + + +Tests: From 1300974a04b7f44706dedf37a3b5bae47a276e74 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 05:25:07 +0100 Subject: [PATCH 07/33] updating prospector.yaml --- prospector.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/prospector.yaml b/prospector.yaml index 4fa0003..9e44522 100644 --- a/prospector.yaml +++ b/prospector.yaml @@ -11,10 +11,11 @@ pylint: disable: # disables TODO warnings - fixme - # !doc missing docstrings +# !doc docstrings - missing-module-docstring - missing-class-docstring - missing-function-docstring +# ! doc end of docstrings # disables warnings about abstract methods not overridden - abstract-method # used when an ancestor class method has an __init__ method which is not called by a derived class. @@ -37,6 +38,7 @@ pylint: max-public-methods: 25 max-module-lines: 1000 max-line-length: 88 + max-args: 8 mccabe: run: true @@ -63,7 +65,7 @@ pyroma: # http://www.pydocstyle.org/en/6.1.1/error_codes.html pep257: disable: - # !doc docstrings +# !doc docstrings # Missing docstring in __init__ - D107 # Missing docstring in public module @@ -82,6 +84,7 @@ pep257: - D213 # First word of the docstring should not be This - D404 +# !doc end of docstrings # Section name should end with a newline - D406 # Missing dashed underline after section From e4ae3f036390184ff14b065916e8e595a691e56e Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 05:44:39 +0100 Subject: [PATCH 08/33] adding latest working --- panaetius/config.py | 7 ++++++- panaetius/logging.py | 2 ++ rewrite.todo | 8 ++++++-- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 panaetius/logging.py diff --git a/panaetius/config.py b/panaetius/config.py index 3a0bc30..f616aa3 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -28,7 +28,6 @@ class Config: self._missing_config = True return {} - # TODO: fix the return error def get_value( self, key: str, default: Any, mask: bool, coerce: bool = False ) -> Any: @@ -40,6 +39,12 @@ class Config: # no config file, look for env vars return self._get_env_value(env_key, default, coerce) + def _get_config_value_key_split_once(self): + pass + + def _get_config_value_key_split_twice(self): + pass + def _get_config_value( self, env_key: str, key: str, default: Any, mask: bool, coerce: bool = False ) -> Any: diff --git a/panaetius/logging.py b/panaetius/logging.py new file mode 100644 index 0000000..f050abf --- /dev/null +++ b/panaetius/logging.py @@ -0,0 +1,2 @@ +import logging +from logging.handlers import RotatingFileHandler diff --git a/rewrite.todo b/rewrite.todo index deed8d0..6214ac2 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -1,12 +1,16 @@ Coding: No Config File: - ☐ Handle if a bool is passed in as a default + ✔ Handle if a bool is passed in as a default @done(21-10-16 05:25) needs to be lower case in the toml, need a check for this Config File: - ☐ Handle if a bool is passed in as a default + ✔ Handle if a bool is passed in as a default @done(21-10-16 05:25) needs to be lower case in the toml, need a check for this + Logging: + ☐ Create SimpleLogger, AdvancedLogger, CustomLogger classes + should simply have the different logging strings to output + should both specify whether to save to file or not Documentation: ☐ Rewrite documentation using `mkdocs` and using `.md`. @2h From 4b51a040cebcddb95818ea8277389c9550ad0bb0 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 05:55:04 +0100 Subject: [PATCH 09/33] adding latest working --- panaetius/config.py | 33 ++++++++++++++++++--------------- tests/test_panaetius.py | 25 +++++++++++++------------ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index f616aa3..5edc3c7 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -39,10 +39,23 @@ class Config: # no config file, look for env vars return self._get_env_value(env_key, default, coerce) - def _get_config_value_key_split_once(self): - pass + def _get_config_value_key_split_once(self, key: str, mask: bool) -> Any: + if mask: + # TODO: write mask logic here + pass + else: + name = key.lower() + return self.config[self.header_variable][name] - def _get_config_value_key_split_twice(self): + def _get_config_value_key_split_twice(self, key: str, mask: bool) -> Any: + if mask: + # TODO: write mask logic here + pass + else: + section, name = key.lower().split(".") + return self.config[self.header_variable][section][name] + + def _get_config_value_missing_key_value_is_none(self, default: Any): pass def _get_config_value( @@ -51,19 +64,9 @@ class Config: try: # look under top header if len(key.split(".")) == 1: - if mask: - # TODO: write mask logic here - pass - else: - name = key.lower() - value = self.config[self.header_variable][name] + value = self._get_config_value_key_split_once(key, mask) elif len(key.split(".")) == 2: - if mask: - # TODO: write mask logic here - pass - else: - section, name = key.lower().split(".") - value = self.config[self.header_variable][section][name] + value = self._get_config_value_key_split_twice(key, mask) return value except (KeyError, TypeError): diff --git a/tests/test_panaetius.py b/tests/test_panaetius.py index 5d66052..ee27c64 100644 --- a/tests/test_panaetius.py +++ b/tests/test_panaetius.py @@ -4,6 +4,7 @@ from panaetius import Config, set_config if __name__ == "__main__": os.environ["PANAETIUS_TEST_PATH"] = "/usr/local" + os.environ["PANAETIUS_TEST_BOOL"] = "true" print(os.environ.get("PANAETIUS_TEST_PATH")) os.environ[ "PANAETIUS_TEST_TOML_POINTS" @@ -14,19 +15,19 @@ if __name__ == "__main__": os.environ["PANAETIUS_TEST_NOC_EMBEDDED_BOOL"] = "true" # c = Config("panaetius_test") - c = Config("panaetius_test_noc") + c = Config("panaetius_test") - # set_config(c, key="path", default="some path") - # set_config(c, key="top", default="some top") - # set_config(c, key="logging.path", default="some logging path") - # set_config(c, key="nonexistent.item", default="some nonexistent item") - # set_config(c, key="nonexistent.item") - # set_config(c, key="toml.points", coerce=True) - # set_config(c, key="toml.points_config") - # set_config(c, key="float", default=2.0) - # set_config(c, key="float_str", default="2.0") - # set_config(c, key="bool", coerce=True) - # set_config(c, key="noexistbool", default=False) + set_config(c, key="path", default="some path") + set_config(c, key="top", default="some top") + set_config(c, key="logging.path", default="some logging path") + set_config(c, key="nonexistent.item", default="some nonexistent item") + set_config(c, key="nonexistent.item") + set_config(c, key="toml.points", coerce=True) + set_config(c, key="toml.points_config") + set_config(c, key="float", default=2.0) + set_config(c, key="float_str", default="2.0") + set_config(c, key="bool", coerce=True) + set_config(c, key="noexistbool", default=False) # set_config(c, key="path") # set_config(c, key="float", coerce=True) From 957ce56a4c5b9194bc4a33788b0cf0940c5861db Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 06:05:58 +0100 Subject: [PATCH 10/33] adding latest working config complete --- panaetius/config.py | 46 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index 5edc3c7..b5ec026 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -55,8 +55,25 @@ class Config: section, name = key.lower().split(".") return self.config[self.header_variable][section][name] - def _get_config_value_missing_key_value_is_none(self, default: Any): - pass + def _get_config_value_missing_key_value_is_none(self, default: Any) -> Any: + if isinstance(default, str): + # if default is a string, wrap TOML value in quotes + return toml.loads(f'value = "{default}"')["value"] + # if default is not a string, leave TOML value as is + # if default is a bool, convert to lower case for 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 + ) + + def _get_config_value_missing_key_value_is_not_none( + self, value: str, coerce: bool + ) -> Any: + return ( + toml.loads(f"value = {value}")["value"] + if coerce + else toml.loads(f'value = "{value}"')["value"] + ) def _get_config_value( self, env_key: str, key: str, default: Any, mask: bool, coerce: bool = False @@ -64,31 +81,16 @@ class Config: try: # look under top header if len(key.split(".")) == 1: - value = self._get_config_value_key_split_once(key, mask) - elif len(key.split(".")) == 2: - value = self._get_config_value_key_split_twice(key, mask) - return value + return self._get_config_value_key_split_once(key, mask) + if len(key.split(".")) == 2: + return self._get_config_value_key_split_twice(key, mask) except (KeyError, TypeError): value = os.environ.get(env_key.replace("-", "_")) if value is None: - if isinstance(default, str): - # if default is a string, wrap TOML value in quotes - return toml.loads(f'value = "{default}"')["value"] - # if default is not a string, leave TOML value as is - # if default is a bool, convert to lower case for 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 - ) + 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 - return ( - toml.loads(f"value = {value}")["value"] - if coerce - else toml.loads(f'value = "{value}"')["value"] - ) + return self._get_config_value_missing_key_value_is_not_none(value, coerce) def _get_env_value( # noqa self, env_key: str, default: Any, coerce: bool = False From b47170070a4f884dfa22548d4ebadbd24b46766a Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 15:29:18 +0100 Subject: [PATCH 11/33] adding latest not working on bool --- panaetius/config.py | 83 ++++++++++++++++++----------------------- tests/test_panaetius.py | 15 +++++--- 2 files changed, 47 insertions(+), 51 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index b5ec026..75152fb 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -39,7 +39,25 @@ class Config: # no config file, look for env vars return self._get_env_value(env_key, default, coerce) - def _get_config_value_key_split_once(self, key: str, mask: bool) -> Any: + def _get_config_value( + self, env_key: str, key: str, default: Any, mask: bool, coerce: bool = False + ) -> Any: + try: + # look under top header + if len(key.split(".")) == 1: + return self.__get_config_value_key_split_once(key, mask) + if len(key.split(".")) == 2: + return self.__get_config_value_key_split_twice(key, mask) + + except (KeyError, TypeError): + value = os.environ.get(env_key.replace("-", "_")) + if value is None: + return self.__get_config_value_missing_key_value_is_none(default) + # if env var, coerce value if flag is set, else return a TOML string + value = self.__get_config_value_missing_key_value_is_not_none(value, coerce) + return self.__get_config_value_missing_key_value_is_not_none(value, coerce) + + def __get_config_value_key_split_once(self, key: str, mask: bool) -> Any: if mask: # TODO: write mask logic here pass @@ -47,7 +65,7 @@ class Config: name = key.lower() return self.config[self.header_variable][name] - def _get_config_value_key_split_twice(self, key: str, mask: bool) -> Any: + def __get_config_value_key_split_twice(self, key: str, mask: bool) -> Any: if mask: # TODO: write mask logic here pass @@ -55,42 +73,13 @@ class Config: section, name = key.lower().split(".") return self.config[self.header_variable][section][name] - def _get_config_value_missing_key_value_is_none(self, default: Any) -> Any: - if isinstance(default, str): - # if default is a string, wrap TOML value in quotes - return toml.loads(f'value = "{default}"')["value"] - # if default is not a string, leave TOML value as is - # if default is a bool, convert to lower case for 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 - ) + def __get_config_value_missing_key_value_is_none(self, default: Any) -> Any: + 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, coerce: bool ) -> Any: - return ( - toml.loads(f"value = {value}")["value"] - if coerce - else toml.loads(f'value = "{value}"')["value"] - ) - - def _get_config_value( - self, env_key: str, key: str, default: Any, mask: bool, coerce: bool = False - ) -> Any: - try: - # look under top header - if len(key.split(".")) == 1: - return self._get_config_value_key_split_once(key, mask) - if len(key.split(".")) == 2: - return self._get_config_value_key_split_twice(key, mask) - - except (KeyError, TypeError): - value = os.environ.get(env_key.replace("-", "_")) - if value is None: - return self._get_config_value_missing_key_value_is_none(default) - # if env var, coerce value if flag is set, else return a TOML string - return self._get_config_value_missing_key_value_is_not_none(value, coerce) + return self.__load_value(value, coerce) def _get_env_value( # noqa self, env_key: str, default: Any, coerce: bool = False @@ -98,19 +87,21 @@ class Config: # look for an environment variable, fallback to default value = os.environ.get(env_key.replace("-", "_")) if value is None: - if isinstance(default, str): - # if default is a string, wrap TOML value in quotes - return toml.loads(f'value = "{default}"')["value"] - # if default is not a string, leave TOML value as is - # if default is a bool, convert to lower case for 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 - ) + return self.__load_default_value(default) + return self.__load_value(value, coerce) + + def __load_value(self, value: str, coerce: bool) -> Any: return ( toml.loads(f"value = {value}")["value"] if coerce else toml.loads(f'value = "{value}"')["value"] ) + + def __load_default_value(self, default: Any) -> Any: + if isinstance(default, str): + 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 + ) diff --git a/tests/test_panaetius.py b/tests/test_panaetius.py index ee27c64..954bc31 100644 --- a/tests/test_panaetius.py +++ b/tests/test_panaetius.py @@ -6,28 +6,33 @@ if __name__ == "__main__": os.environ["PANAETIUS_TEST_PATH"] = "/usr/local" os.environ["PANAETIUS_TEST_BOOL"] = "true" print(os.environ.get("PANAETIUS_TEST_PATH")) - os.environ[ - "PANAETIUS_TEST_TOML_POINTS" - ] = "[ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 }]" + # os.environ[ + # "PANAETIUS_TEST_TOML_POINTS" + # ] = "[ { 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_FLOAT"] = "2.0" + os.environ["PANAETIUS_TEST_NOC_BOOL"] = "true" os.environ["PANAETIUS_TEST_NOC_EMBEDDED_PATH"] = "/usr/local" os.environ["PANAETIUS_TEST_NOC_EMBEDDED_FLOAT"] = "2.0" os.environ["PANAETIUS_TEST_NOC_EMBEDDED_BOOL"] = "true" - # c = Config("panaetius_test") c = Config("panaetius_test") + # c = Config("panaetius_test_noc") + set_config(c, key="toml.points", coerce=True) set_config(c, key="path", default="some path") set_config(c, key="top", default="some top") set_config(c, key="logging.path", default="some logging path") set_config(c, key="nonexistent.item", default="some nonexistent item") set_config(c, key="nonexistent.item") - set_config(c, key="toml.points", coerce=True) + # FIXME: this isn't working after the refactor... set_config(c, key="toml.points_config") set_config(c, key="float", default=2.0) set_config(c, key="float_str", default="2.0") set_config(c, key="bool", coerce=True) set_config(c, key="noexistbool", default=False) + set_config(c, key="middle.middle") # set_config(c, key="path") # set_config(c, key="float", coerce=True) From 035d2b4bef1fd040f8b555c3b8743920f4c64ae1 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 16 Oct 2021 16:36:15 +0100 Subject: [PATCH 12/33] adding latest working --- panaetius/config.py | 37 ++++++++++++---------------- panaetius/library.py | 3 +-- panaetius/logging.py | 53 ++++++++++++++++++++++++++++++++++++++++- rewrite.todo | 3 ++- tests/test_panaetius.py | 1 - 5 files changed, 70 insertions(+), 27 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index 75152fb..e2a147d 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -17,6 +17,7 @@ class Config: else pathlib.Path.home() / ".config" ) self._missing_config = False + self.logging_path: str | None = None @property def config(self) -> dict[str, Any]: @@ -28,50 +29,41 @@ class Config: self._missing_config = True return {} - def get_value( - self, key: str, default: Any, mask: bool, coerce: bool = False - ) -> Any: + def get_value(self, key: str, default: Any, coerce: bool = False) -> Any: env_key = f"{self.header_variable.upper()}_{key.upper().replace('.', '_')}" if not self._missing_config: # look in the config file - return self._get_config_value(env_key, key, default, mask, coerce) + return self._get_config_value(env_key, key, default, coerce) # no config file, look for env vars return self._get_env_value(env_key, default, coerce) def _get_config_value( - self, env_key: str, key: str, default: Any, mask: bool, coerce: bool = False + self, env_key: str, key: str, default: Any, coerce: bool = False ) -> Any: try: # look under top header + # REVIEW: could this be auto handled for a key of arbitrary length? if len(key.split(".")) == 1: - return self.__get_config_value_key_split_once(key, mask) + return self.__get_config_value_key_split_once(key) if len(key.split(".")) == 2: - return self.__get_config_value_key_split_twice(key, mask) + return self.__get_config_value_key_split_twice(key) + raise KeyError except (KeyError, TypeError): value = os.environ.get(env_key.replace("-", "_")) if value is None: return self.__get_config_value_missing_key_value_is_none(default) # if env var, coerce value if flag is set, else return a TOML string - value = self.__get_config_value_missing_key_value_is_not_none(value, coerce) return self.__get_config_value_missing_key_value_is_not_none(value, coerce) - def __get_config_value_key_split_once(self, key: str, mask: bool) -> Any: - if mask: - # TODO: write mask logic here - pass - else: - name = key.lower() - return self.config[self.header_variable][name] + def __get_config_value_key_split_once(self, key: str) -> Any: + name = key.lower() + return self.config[self.header_variable][name] - def __get_config_value_key_split_twice(self, key: str, mask: bool) -> Any: - if mask: - # TODO: write mask logic here - pass - else: - section, name = key.lower().split(".") - return self.config[self.header_variable][section][name] + def __get_config_value_key_split_twice(self, key: str) -> Any: + section, name = key.lower().split(".") + return self.config[self.header_variable][section][name] def __get_config_value_missing_key_value_is_none(self, default: Any) -> Any: return self.__load_default_value(default) @@ -91,6 +83,7 @@ class Config: return self.__load_value(value, coerce) def __load_value(self, value: str, coerce: bool) -> Any: + value = str(value).lower() if isinstance(value, bool) else value return ( toml.loads(f"value = {value}")["value"] if coerce diff --git a/panaetius/library.py b/panaetius/library.py index d01fc0f..2f76417 100644 --- a/panaetius/library.py +++ b/panaetius/library.py @@ -9,8 +9,7 @@ def set_config( config_inst: Config, key: str, default: Any = None, - mask: bool = False, coerce: bool = False, ): config_var = key.lower().replace(".", "_") - setattr(config_inst, config_var, config_inst.get_value(key, default, mask, coerce)) + setattr(config_inst, config_var, config_inst.get_value(key, default, coerce)) diff --git a/panaetius/logging.py b/panaetius/logging.py index f050abf..181fe78 100644 --- a/panaetius/logging.py +++ b/panaetius/logging.py @@ -1,2 +1,53 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod import logging -from logging.handlers import RotatingFileHandler +import pathlib +import sys + +from panaetius import Config + + +def set_logger(config_inst: Config, logging_format: Logger): + logger = logging.getLogger(config_inst.header_variable) + loghandler_sys = logging.StreamHandler(sys.stdout) + + # check if log path is set + if config_inst.logging_path is not None: + logging_file = pathlib.Path(config_inst.logging_path) + + +class Logger(ABC): + @property + @abstractmethod + def format(self): + pass + + +class SimpleLogger(Logger): + @property + def format(self): + return ( + '{\n\t"time": "%(asctime)s",\n\t"logging_level":' + '"%(levelname)s",\n\t"message": "%(message)s"\n}', + ) + + +class AdvancedLogger(Logger): + @property + def format(self): + return ( + '{\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}', + ) + + +class CustomLogger(Logger): + @property + def format(self): + return self._format + + def __init__(self, logging_format: str): + self._format = logging_format diff --git a/rewrite.todo b/rewrite.todo index 6214ac2..69d7948 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -8,9 +8,10 @@ Coding: needs to be lower case in the toml, need a check for this Logging: - ☐ Create SimpleLogger, AdvancedLogger, CustomLogger classes + ✔ Create SimpleLogger, AdvancedLogger, CustomLogger classes @done(21-10-16 16:22) should simply have the different logging strings to output should both specify whether to save to file or not + ☐ Logging path should take by default the config path unless overwritten? Documentation: ☐ Rewrite documentation using `mkdocs` and using `.md`. @2h diff --git a/tests/test_panaetius.py b/tests/test_panaetius.py index 954bc31..42d5cb6 100644 --- a/tests/test_panaetius.py +++ b/tests/test_panaetius.py @@ -26,7 +26,6 @@ if __name__ == "__main__": set_config(c, key="logging.path", default="some logging path") set_config(c, key="nonexistent.item", default="some nonexistent item") set_config(c, key="nonexistent.item") - # FIXME: this isn't working after the refactor... set_config(c, key="toml.points_config") set_config(c, key="float", default=2.0) set_config(c, key="float_str", default="2.0") From c31804525873b785553532c30e9059163640c73c Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sun, 17 Oct 2021 06:51:41 +0100 Subject: [PATCH 13/33] adding latest working --- panaetius/__init__.py | 1 + panaetius/config.py | 8 +++-- panaetius/logging.py | 72 +++++++++++++++++++++++++++++++++---------- rewrite.todo | 25 ++++++++++++++- 4 files changed, 86 insertions(+), 20 deletions(-) diff --git a/panaetius/__init__.py b/panaetius/__init__.py index 866a529..500f8d6 100644 --- a/panaetius/__init__.py +++ b/panaetius/__init__.py @@ -1,2 +1,3 @@ from panaetius.config import Config from panaetius.library import set_config +from panaetius.logging import set_logger, SimpleLogger, AdvancedLogger, CustomLogger diff --git a/panaetius/config.py b/panaetius/config.py index e2a147d..14fd6a6 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -17,7 +17,11 @@ class Config: else pathlib.Path.home() / ".config" ) self._missing_config = False + + # default logging options self.logging_path: str | None = None + self.logging_rotate_bytes: int = 0 + self.logging_backup_count: int = 0 @property def config(self) -> dict[str, Any]: @@ -82,7 +86,7 @@ class Config: return self.__load_default_value(default) return self.__load_value(value, coerce) - def __load_value(self, value: str, coerce: bool) -> Any: + def __load_value(self, value: str, coerce: bool) -> Any: # noqa value = str(value).lower() if isinstance(value, bool) else value return ( toml.loads(f"value = {value}")["value"] @@ -90,7 +94,7 @@ class Config: else toml.loads(f'value = "{value}"')["value"] ) - def __load_default_value(self, default: Any) -> Any: + def __load_default_value(self, default: Any) -> Any: # noqa if isinstance(default, str): return toml.loads(f'value = "{default}"')["value"] # if default is bool convert to lower case toml syntax diff --git a/panaetius/logging.py b/panaetius/logging.py index 181fe78..574db26 100644 --- a/panaetius/logging.py +++ b/panaetius/logging.py @@ -1,53 +1,91 @@ from __future__ import annotations -from abc import ABC, abstractmethod +from abc import ABCMeta, abstractmethod import logging +from logging.handlers import RotatingFileHandler import pathlib import sys from panaetius import Config +from panaetius.library import set_config -def set_logger(config_inst: Config, logging_format: Logger): +def set_logger(config_inst: Config, logging_format_inst: LoggingData) -> logging.Logger: logger = logging.getLogger(config_inst.header_variable) - loghandler_sys = logging.StreamHandler(sys.stdout) + log_handler_sys = logging.StreamHandler(sys.stdout) - # check if log path is set + # configure file handler if config_inst.logging_path is not None: - logging_file = pathlib.Path(config_inst.logging_path) + logging_file = ( + pathlib.Path(config_inst.logging_path) + / config_inst.header_variable + / f"{config_inst.header_variable}.log" + ).expanduser() + + if config_inst.logging_rotate_bytes == 0: + set_config(config_inst, "logging.rotate_bytes", 512000) + if config_inst.logging_backup_count == 0: + set_config(config_inst, "logging.backup_count", 3) + + log_handler_file = RotatingFileHandler( + str(logging_file), + "a", + config_inst.logging_rotate_bytes, + config_inst.logging_backup_count, + ) + + log_handler_file.setFormatter(logging.Formatter(logging_format_inst.format)) + logger.addHandler(log_handler_file) + + # configure stdout handler + log_handler_sys.setFormatter(logging.Formatter(logging_format_inst.format)) + logger.addHandler(log_handler_sys) + logger.setLevel(logging_format_inst.logging_level) + return logger -class Logger(ABC): +class LoggingData(metaclass=ABCMeta): @property @abstractmethod - def format(self): + def format(self) -> str: pass + @abstractmethod + def __init__(self, logging_level: str): + self.logging_level = logging_level -class SimpleLogger(Logger): + +class SimpleLogger(LoggingData): @property - def format(self): - return ( + def format(self) -> str: + return str( '{\n\t"time": "%(asctime)s",\n\t"logging_level":' '"%(levelname)s",\n\t"message": "%(message)s"\n}', ) + def __init__(self, logging_level: str = "INFO"): + self.logging_level = logging_level -class AdvancedLogger(Logger): + +class AdvancedLogger(LoggingData): @property - def format(self): - return ( + def format(self) -> str: + return str( '{\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}', ) + def __init__(self, logging_level: str = "INFO"): + self.logging_level = logging_level -class CustomLogger(Logger): + +class CustomLogger(LoggingData): @property - def format(self): - return self._format + def format(self) -> str: + return str(self._format) - def __init__(self, logging_format: str): + def __init__(self, logging_format: str, logging_level: str = "INFO"): + self.logging_level = logging_level self._format = logging_format diff --git a/rewrite.todo b/rewrite.todo index 69d7948..b8e5604 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -11,10 +11,33 @@ Coding: ✔ Create SimpleLogger, AdvancedLogger, CustomLogger classes @done(21-10-16 16:22) should simply have the different logging strings to output should both specify whether to save to file or not - ☐ Logging path should take by default the config path unless overwritten? + ✔ Logging path should take by default the config path unless overwritten? @done(21-10-16 23:49) + + Errors: + ☐ Check logging path + config path are valid, if not raise error. + ☐ Add tests for these. + + Linting: + ☐ Check all functions and annotations. + + Docstrings: + ☐ Write the docstrings for public functions/methods. Documentation: ☐ Rewrite documentation using `mkdocs` and using `.md`. @2h Tests: + Config File: + ☐ + + Environment Variable: + ☐ + + __init__: + ☐ Test default config path set to "~/.config" + ☐ Test config path is set when passed in + + config property: + ☐ Check testing config file is returned as dict + ☐ Check _self.missing_config and empty dict is returned From 4ae4eb085c468659c6900dabe41676144981104d Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sun, 17 Oct 2021 06:51:47 +0100 Subject: [PATCH 14/33] adding initial tests --- poetry.lock | 146 +++++++++++++++--- prospector.yaml | 3 + pyproject.toml | 6 +- pytest.ini | 3 + tests/__header__.py | 1 - tests/conftest.py | 6 + tests/{test_panaetius.py => scratchpad.py} | 17 +- tests/test.py | 33 ---- tests/test_config.py | 36 +++++ .../panaetius_testing/config.toml | 12 ++ 10 files changed, 196 insertions(+), 67 deletions(-) create mode 100644 pytest.ini delete mode 100644 tests/__header__.py create mode 100644 tests/conftest.py rename tests/{test_panaetius.py => scratchpad.py} (79%) delete mode 100644 tests/test.py create mode 100644 tests/test_config.py create mode 100644 tests/test_config/without_logging/panaetius_testing/config.toml diff --git a/poetry.lock b/poetry.lock index 63d24c2..e936ba2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -65,6 +65,17 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "execnet" +version = "1.9.0" +description = "execnet: rapid multi-Python deployment" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +testing = ["pre-commit"] + [[package]] name = "flake8" version = "2.3.0" @@ -129,6 +140,14 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] perf = ["ipython"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "isort" version = "5.9.3" @@ -159,14 +178,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "more-itertools" -version = "8.10.0" -description = "More routines for operating on iterables, beyond itertools" -category = "dev" -optional = false -python-versions = ">=3.5" - [[package]] name = "mypy" version = "0.910" @@ -193,6 +204,17 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "packaging" +version = "21.0" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2" + [[package]] name = "pbr" version = "5.6.0" @@ -396,22 +418,76 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + [[package]] name = "pytest" -version = "3.10.1" +version = "6.2.5" description = "pytest: simple powerful testing with Python" category = "dev" optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "pytest-datadir" +version = "1.3.1" +description = "pytest plugin for test data directories and files" +category = "dev" +optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] -atomicwrites = ">=1.0" -attrs = ">=17.4.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -more-itertools = ">=4.0.0" -pluggy = ">=0.7" -py = ">=1.5.0" -six = ">=1.10.0" +pytest = ">=2.7.0" + +[[package]] +name = "pytest-forked" +version = "1.3.0" +description = "run tests in isolated forked subprocesses" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +py = "*" +pytest = ">=3.10" + +[[package]] +name = "pytest-xdist" +version = "2.4.0" +description = "pytest xdist plugin for distributed testing and loop-on-failing modes" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +execnet = ">=1.1" +pytest = ">=6.0.0" +pytest-forked = "*" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] [[package]] name = "pyyaml" @@ -534,7 +610,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "09c1545b730544eb1d67b233f4446b9175031db78a91cd662e0c84af56bf8023" +content-hash = "bc75d0878aaf4033c2d9520333a559d29e81962a8a0138ed8861014d2fc77eac" [metadata.files] astroid = [ @@ -561,6 +637,10 @@ dodgy = [ {file = "dodgy-0.2.1-py3-none-any.whl", hash = "sha256:51f54c0fd886fa3854387f354b19f429d38c04f984f38bc572558b703c0542a6"}, {file = "dodgy-0.2.1.tar.gz", hash = "sha256:28323cbfc9352139fdd3d316fa17f325cc0e9ac74438cbba51d70f9b48f86c3a"}, ] +execnet = [ + {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, + {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, +] flake8 = [ {file = "flake8-2.3.0-py2.py3-none-any.whl", hash = "sha256:c99cc9716d6655d9c8bcb1e77632b8615bf0abd282d7abd9f5c2148cad7fc669"}, {file = "flake8-2.3.0.tar.gz", hash = "sha256:5ee1a43ccd0716d6061521eec6937c983efa027793013e572712c4da55c7c83e"}, @@ -581,6 +661,10 @@ importlib-metadata = [ {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, ] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] isort = [ {file = "isort-5.9.3-py3-none-any.whl", hash = "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2"}, {file = "isort-5.9.3.tar.gz", hash = "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899"}, @@ -613,10 +697,6 @@ mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] -more-itertools = [ - {file = "more-itertools-8.10.0.tar.gz", hash = "sha256:1debcabeb1df793814859d64a81ad7cb10504c24349368ccf214c664c474f41f"}, - {file = "more_itertools-8.10.0-py3-none-any.whl", hash = "sha256:56ddac45541718ba332db05f464bebfb0768110111affd27f66e0051f276fa43"}, -] mypy = [ {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, @@ -646,6 +726,10 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] +packaging = [ + {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, + {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, +] pbr = [ {file = "pbr-5.6.0-py2.py3-none-any.whl", hash = "sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"}, {file = "pbr-5.6.0.tar.gz", hash = "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"}, @@ -708,9 +792,25 @@ pylite = [ {file = "pylite-0.1.0-py3-none-any.whl", hash = "sha256:eb46f5beb1f2102672fd4355c013ac2feebc0df284d65f7711f2041a0a410141"}, {file = "pylite-0.1.0.tar.gz", hash = "sha256:e338d20d3f8f72dd84d1e58f2fd6dba008d593e0cfacfb5fbdd5a297b830628e"}, ] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] pytest = [ - {file = "pytest-3.10.1-py2.py3-none-any.whl", hash = "sha256:3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec"}, - {file = "pytest-3.10.1.tar.gz", hash = "sha256:e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"}, + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] +pytest-datadir = [ + {file = "pytest-datadir-1.3.1.tar.gz", hash = "sha256:d3af1e738df87515ee509d6135780f25a15959766d9c2b2dbe02bf4fb979cb18"}, + {file = "pytest_datadir-1.3.1-py2.py3-none-any.whl", hash = "sha256:1847ed0efe0bc54cac40ab3fba6d651c2f03d18dd01f2a582979604d32e7621e"}, +] +pytest-forked = [ + {file = "pytest-forked-1.3.0.tar.gz", hash = "sha256:6aa9ac7e00ad1a539c41bec6d21011332de671e938c7637378ec9710204e37ca"}, + {file = "pytest_forked-1.3.0-py2.py3-none-any.whl", hash = "sha256:dc4147784048e70ef5d437951728825a131b81714b398d5d52f17c7c144d8815"}, +] +pytest-xdist = [ + {file = "pytest-xdist-2.4.0.tar.gz", hash = "sha256:89b330316f7fc475f999c81b577c2b926c9569f3d397ae432c0c2e2496d61ff9"}, + {file = "pytest_xdist-2.4.0-py3-none-any.whl", hash = "sha256:7b61ebb46997a0820a263553179d6d1e25a8c50d8a8620cd1aa1e20e3be99168"}, ] pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, diff --git a/prospector.yaml b/prospector.yaml index 9e44522..0e99e41 100644 --- a/prospector.yaml +++ b/prospector.yaml @@ -103,6 +103,9 @@ dodgy: bandit: run: true + options: + # ignore assert warning + B101 mypy: run: true diff --git a/pyproject.toml b/pyproject.toml index 62c0a8a..8bf09bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,11 +28,11 @@ toml = "^0.10.0" pylite = "^0.1.0" [tool.poetry.dev-dependencies] -pytest = "^3.0" # TODO: Update to latest -# sphinx = "^2.3" -# sphinx_rtd_theme = "^0.4.3" prospector = {extras = ["with_bandit", "with_mypy"], version = "^1.5.1"} types-toml = "^0.10.1" +pytest = "^6.2.5" +pytest-datadir = "^1.3.1" +pytest-xdist = "^2.4.0" [build-system] requires = ["poetry>=0.12"] diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..bd07e3a --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +; parallel tests with pytest-xdist +[pytest] +addopts=-n4 diff --git a/tests/__header__.py b/tests/__header__.py deleted file mode 100644 index 17c5fdb..0000000 --- a/tests/__header__.py +++ /dev/null @@ -1 +0,0 @@ -__header__ = 'panaetius_test' diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..4a173d5 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,6 @@ +import pytest + + +@pytest.fixture() +def header(): + return "panaetius_testing" diff --git a/tests/test_panaetius.py b/tests/scratchpad.py similarity index 79% rename from tests/test_panaetius.py rename to tests/scratchpad.py index 42d5cb6..28949dc 100644 --- a/tests/test_panaetius.py +++ b/tests/scratchpad.py @@ -1,6 +1,7 @@ import os -from panaetius import Config, set_config +from panaetius import Config, set_config, set_logger, SimpleLogger +# from panaetius.logging import AdvancedLogger if __name__ == "__main__": os.environ["PANAETIUS_TEST_PATH"] = "/usr/local" @@ -17,17 +18,17 @@ if __name__ == "__main__": os.environ["PANAETIUS_TEST_NOC_EMBEDDED_FLOAT"] = "2.0" os.environ["PANAETIUS_TEST_NOC_EMBEDDED_BOOL"] = "true" - c = Config("panaetius_test") - # c = Config("panaetius_test_noc") + # c = Config("panaetius_test") + c = Config("panaetius_test_noc") set_config(c, key="toml.points", coerce=True) set_config(c, key="path", default="some path") set_config(c, key="top", default="some top") - set_config(c, key="logging.path", default="some logging path") + set_config(c, key="logging.path") set_config(c, key="nonexistent.item", default="some nonexistent item") set_config(c, key="nonexistent.item") set_config(c, key="toml.points_config") - set_config(c, key="float", default=2.0) + set_config(c, key="float", coerce=True) set_config(c, key="float_str", default="2.0") set_config(c, key="bool", coerce=True) set_config(c, key="noexistbool", default=False) @@ -47,5 +48,7 @@ if __name__ == "__main__": set_config(c, key="embedded.noexistfloat", default=2.0) set_config(c, key="embedded.noexistbool", default=False) - print(c) - pass + logger = set_logger(c, SimpleLogger()) + # logger = set_logger(c, AdvancedLogger(logging_level="INFO")) + logger.info("test logging message") + logger.debug("debugging message") diff --git a/tests/test.py b/tests/test.py deleted file mode 100644 index 6b602a1..0000000 --- a/tests/test.py +++ /dev/null @@ -1,33 +0,0 @@ -import panaetius - -# from panaetius import CONFIG as CONFIG -# from panaetius import logger as logger - -print(panaetius.__header__) - -panaetius.set_config(panaetius.CONFIG, 'logging.level') - -# print(panaetius.CONFIG.logging_format) -print(panaetius.CONFIG.logging_path) -print(panaetius.config_inst.CONFIG_PATH) - -# panaetius.logger.info('test event') - - -panaetius.logger.info('setting foo.bar value') -panaetius.set_config(panaetius.CONFIG, 'foo.bar', mask=True) - -panaetius.logger.info(f'foo.bar set to {panaetius.CONFIG.foo_bar}') - -# print((panaetius.CONFIG.path)) -# print(panaetius.CONFIG.logging_level) - -panaetius.set_config(panaetius.CONFIG, 'test', mask=True) -panaetius.logger.info(f'test_root={panaetius.CONFIG.test}') - -print(panaetius.CONFIG.config_file) - -# for i in panaetius.CONFIG.deferred_messages: -# panaetius.logger.debug(i) - -panaetius.logger.info('some logging message') diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..caa2cde --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,36 @@ +import pathlib + +import toml + +import panaetius + + +def test_default_config_path_set(header): + # act + config = panaetius.Config(header) + + # assert + assert str(config.config_path) == str(pathlib.Path.home() / ".config") + + +def test_user_config_path_set(header, datadir): + # arrange + config_path = str(datadir / "without_logging") + + # act + config = panaetius.Config(header, config_path) + + # assert + assert str(config.config_path) == config_path + + +def test_config_file_exists(header, datadir): + # arrange + config_path = str(datadir / "without_logging") + + # act + config = panaetius.Config(header, config_path) + config_contents = config.config + + # assert + assert config._missing_config == False diff --git a/tests/test_config/without_logging/panaetius_testing/config.toml b/tests/test_config/without_logging/panaetius_testing/config.toml new file mode 100644 index 0000000..02a7118 --- /dev/null +++ b/tests/test_config/without_logging/panaetius_testing/config.toml @@ -0,0 +1,12 @@ +[panaetius_testing] +some_top_string = "some_top_value" + +[panaetius_testing.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"] } + +# [panaetius_testing.logging] +# path = "" From f73a6d24415d0d6d261943cddcca5ff763609f4e Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Mon, 18 Oct 2021 00:12:20 +0100 Subject: [PATCH 15/33] adding latest tests --- .vscode/settings.json | 15 -- TODO | 3 + panaetius/config.py | 65 +++--- panaetius/exceptions.py | 6 + panaetius/library.py | 3 +- panaetius/logging.py | 4 + poetry.lock | 48 ++++- prospector.yaml | 4 +- pyproject.toml | 1 + pytest.ini | 6 +- rewrite.todo | 47 +++-- tests/conftest.py | 17 ++ .../panaetius_testing/config.toml | 4 +- tests/scratchpad.py | 41 ++-- tests/test_config.py | 186 +++++++++++++++++- tests/test_library.py | 13 ++ tests/test_logging.py | 34 ++++ 17 files changed, 402 insertions(+), 95 deletions(-) create mode 100644 TODO create mode 100644 panaetius/exceptions.py rename tests/{test_config => data}/without_logging/panaetius_testing/config.toml (83%) create mode 100644 tests/test_library.py create mode 100644 tests/test_logging.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 3525082..9f07c66 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,20 +4,5 @@ "python.linting.enabled": true, "python.pythonPath": ".venv/bin/python", "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" } diff --git a/TODO b/TODO new file mode 100644 index 0000000..a0814df --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ + +Todo: + ☐ Item diff --git a/panaetius/config.py b/panaetius/config.py index 14fd6a6..0f9f44a 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -1,10 +1,14 @@ from __future__ import annotations + +import ast import os import pathlib from typing import Any import toml +from panaetius.exceptions import KeyErrorTooDeepException + class Config: """docstring for Config().""" @@ -16,7 +20,7 @@ class Config: if config_path else pathlib.Path.home() / ".config" ) - self._missing_config = False + self._missing_config = self._check_config_file_exists() # default logging options self.logging_path: str | None = None @@ -24,42 +28,52 @@ class Config: self.logging_backup_count: int = 0 @property - def config(self) -> dict[str, Any]: + def config(self) -> dict: config_file_location = self.config_path / self.header_variable / "config.toml" try: with open(config_file_location, "r", encoding="utf-8") as config_file: return dict(toml.load(config_file)) except FileNotFoundError: - self._missing_config = True 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('.', '_')}" if not self._missing_config: # 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 - return self._get_env_value(env_key, default, coerce) + return self._get_env_value(env_key, default) - def _get_config_value( - self, env_key: str, key: str, default: Any, coerce: bool = False - ) -> Any: + def _check_config_file_exists(self) -> bool: + config_file_location = self.config_path / self.header_variable / "config.toml" + 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: # look under top header # 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: return self.__get_config_value_key_split_once(key) if len(key.split(".")) == 2: return self.__get_config_value_key_split_twice(key) - raise KeyError + raise KeyError() except (KeyError, TypeError): value = os.environ.get(env_key.replace("-", "_")) if value is None: return self.__get_config_value_missing_key_value_is_none(default) # if env var, coerce value if flag is set, else return a TOML string - 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: name = key.lower() @@ -72,33 +86,18 @@ class Config: def __get_config_value_missing_key_value_is_none(self, default: Any) -> Any: return self.__load_default_value(default) - def __get_config_value_missing_key_value_is_not_none( - self, value: str, coerce: bool - ) -> Any: - return self.__load_value(value, coerce) + def __get_config_value_missing_key_value_is_not_none(self, value: str) -> Any: + return self.__load_value(value) - def _get_env_value( # noqa - self, env_key: str, default: Any, coerce: bool = False - ) -> Any: + def _get_env_value(self, env_key: str, default: Any) -> Any: # noqa # look for an environment variable, fallback to default value = os.environ.get(env_key.replace("-", "_")) if value is None: 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 - value = str(value).lower() if isinstance(value, bool) else value - return ( - toml.loads(f"value = {value}")["value"] - if coerce - else toml.loads(f'value = "{value}"')["value"] - ) + def __load_value(self, value: str) -> Any: # noqa + return ast.literal_eval(value) def __load_default_value(self, default: Any) -> Any: # noqa - if isinstance(default, str): - 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 - ) + return default diff --git a/panaetius/exceptions.py b/panaetius/exceptions.py new file mode 100644 index 0000000..a7a6333 --- /dev/null +++ b/panaetius/exceptions.py @@ -0,0 +1,6 @@ +class KeyErrorTooDeepException(Exception): + pass + + +class LoggingDirectoryDoesNotExistException(Exception): + pass diff --git a/panaetius/library.py b/panaetius/library.py index 2f76417..f624159 100644 --- a/panaetius/library.py +++ b/panaetius/library.py @@ -9,7 +9,6 @@ def set_config( config_inst: Config, key: str, default: Any = None, - coerce: bool = False, ): 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)) diff --git a/panaetius/logging.py b/panaetius/logging.py index 574db26..4b86b3a 100644 --- a/panaetius/logging.py +++ b/panaetius/logging.py @@ -8,6 +8,7 @@ import sys from panaetius import Config from panaetius.library import set_config +from panaetius.exceptions import LoggingDirectoryDoesNotExistException 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" ).expanduser() + if not logging_file.parents[0].exists(): + raise LoggingDirectoryDoesNotExistException() + if config_inst.logging_rotate_bytes == 0: set_config(config_inst, "logging.rotate_bytes", 512000) if config_inst.logging_backup_count == 0: diff --git a/poetry.lock b/poetry.lock index e936ba2..7580b27 100644 --- a/poetry.lock +++ b/poetry.lock @@ -57,6 +57,17 @@ category = "dev" optional = false 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]] name = "dodgy" version = "0.2.1" @@ -610,7 +621,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "bc75d0878aaf4033c2d9520333a559d29e81962a8a0138ed8861014d2fc77eac" +content-hash = "468d1aa5e0c440262f6041ad859358a84ef32462941aa6f3ba71838a52cc1ced" [metadata.files] astroid = [ @@ -633,6 +644,41 @@ colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {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 = [ {file = "dodgy-0.2.1-py3-none-any.whl", hash = "sha256:51f54c0fd886fa3854387f354b19f429d38c04f984f38bc572558b703c0542a6"}, {file = "dodgy-0.2.1.tar.gz", hash = "sha256:28323cbfc9352139fdd3d316fa17f325cc0e9ac74438cbba51d70f9b48f86c3a"}, diff --git a/prospector.yaml b/prospector.yaml index 0e99e41..f7479e1 100644 --- a/prospector.yaml +++ b/prospector.yaml @@ -103,9 +103,9 @@ dodgy: bandit: run: true - options: + # options: # ignore assert warning - B101 + # - B101 mypy: run: true diff --git a/pyproject.toml b/pyproject.toml index 8bf09bb..3be3c93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ types-toml = "^0.10.1" pytest = "^6.2.5" pytest-datadir = "^1.3.1" pytest-xdist = "^2.4.0" +coverage = "^6.0.2" [build-system] requires = ["poetry>=0.12"] diff --git a/pytest.ini b/pytest.ini index bd07e3a..8661e8e 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,3 @@ -; parallel tests with pytest-xdist -[pytest] -addopts=-n4 +; ; parallel tests with pytest-xdist +; [pytest] +; addopts=-n4 diff --git a/rewrite.todo b/rewrite.todo index b8e5604..b28f883 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -14,8 +14,9 @@ Coding: ✔ Logging path should take by default the config path unless overwritten? @done(21-10-16 23:49) Errors: - ☐ Check logging path + config path are valid, if not raise error. - ☐ Add tests for these. + ✔ Check logging path + config path are valid, if not raise error. @done(21-10-18 00:04) + ✔ 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: ☐ Check all functions and annotations. @@ -23,21 +24,45 @@ Coding: Docstrings: ☐ Write the docstrings for public functions/methods. + Functionality: + ☐ When both a config file and a env var is found, use the env var. + Documentation: ☐ 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: - Config File: - ☐ - - Environment Variable: - ☐ + 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) + 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__: - ☐ Test default config path set to "~/.config" - ☐ Test config path is set when passed in + ✔ Test default config path set to "~/.config" @done(21-10-17 17:25) + ✔ Test config path is set when passed in @done(21-10-17 17:25) config property: - ☐ Check testing config file is returned as dict - ☐ Check _self.missing_config and empty dict is returned + ✔ Check testing config file is returned as dict @done(21-10-17 17:25) + ✔ 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) diff --git a/tests/conftest.py b/tests/conftest.py index 4a173d5..a2b59da 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,3 +4,20 @@ import pytest @pytest.fixture() def header(): 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]}, + }, + } + } diff --git a/tests/test_config/without_logging/panaetius_testing/config.toml b/tests/data/without_logging/panaetius_testing/config.toml similarity index 83% rename from tests/test_config/without_logging/panaetius_testing/config.toml rename to tests/data/without_logging/panaetius_testing/config.toml index 02a7118..b4aa99e 100644 --- a/tests/test_config/without_logging/panaetius_testing/config.toml +++ b/tests/data/without_logging/panaetius_testing/config.toml @@ -7,6 +7,4 @@ some_second_int = 1 some_second_float = 1.0 some_second_list = ["some", "second", "value"] some_second_table = { "first" = ["some", "first", "value"] } - -# [panaetius_testing.logging] -# path = "" +some_second_table_bools = { "bool" = [true, false] } diff --git a/tests/scratchpad.py b/tests/scratchpad.py index 28949dc..8153517 100644 --- a/tests/scratchpad.py +++ b/tests/scratchpad.py @@ -1,54 +1,57 @@ import os from panaetius import Config, set_config, set_logger, SimpleLogger -# from panaetius.logging import AdvancedLogger + +from panaetius.logging import AdvancedLogger if __name__ == "__main__": - os.environ["PANAETIUS_TEST_PATH"] = "/usr/local" - os.environ["PANAETIUS_TEST_BOOL"] = "true" - print(os.environ.get("PANAETIUS_TEST_PATH")) + os.environ["PANAETIUS_TEST_PATH"] = '"/usr/local"' + os.environ["PANAETIUS_TEST_BOOL"] = "True" + # print(os.environ.get("PANAETIUS_TEST_PATH")) # os.environ[ # "PANAETIUS_TEST_TOML_POINTS" # ] = "[ { 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_BOOL"] = "true" - os.environ["PANAETIUS_TEST_NOC_EMBEDDED_PATH"] = "/usr/local" + os.environ["PANAETIUS_TEST_NOC_BOOL"] = "True" + os.environ["PANAETIUS_TEST_NOC_EMBEDDED_PATH"] = '"/usr/local"' 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_noc") + c = Config("panaetius_test") + # 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="top", default="some top") set_config(c, key="logging.path") set_config(c, key="nonexistent.item", default="some nonexistent item") set_config(c, key="nonexistent.item") 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="bool", coerce=True) + set_config(c, key="bool") set_config(c, key="noexistbool", default=False) set_config(c, key="middle.middle") # set_config(c, key="path") - # set_config(c, key="float", coerce=True) - # set_config(c, key="bool", coerce=True) + # set_config(c, key="float") + # set_config(c, key="bool") # set_config(c, key="noexiststr", default="2.0") # set_config(c, key="noexistfloat", default=2.0) # set_config(c, key="noexistbool", default=False) set_config(c, key="embedded.path") - set_config(c, key="embedded.float", coerce=True) - set_config(c, key="embedded.bool", coerce=True) + set_config(c, key="embedded.float") + set_config(c, key="embedded.bool") set_config(c, key="embedded.noexiststr", default="2.0") set_config(c, key="embedded.noexistfloat", default=2.0) set_config(c, key="embedded.noexistbool", default=False) - logger = set_logger(c, SimpleLogger()) - # logger = set_logger(c, AdvancedLogger(logging_level="INFO")) + # logger = set_logger(c, SimpleLogger()) + logger = set_logger(c, AdvancedLogger(logging_level="DEBUG")) logger.info("test logging message") logger.debug("debugging message") + for i in dir(c): + logger.debug(i + ": " + str(getattr(c, i)) + " - " + str(type(getattr(c, i)))) diff --git a/tests/test_config.py b/tests/test_config.py index caa2cde..d9bb8ec 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,8 +1,13 @@ +import os import pathlib +from uuid import uuid4 -import toml +import pytest import panaetius +from panaetius.exceptions import KeyErrorTooDeepException + +# test config paths 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") -def test_user_config_path_set(header, datadir): +def test_user_config_path_set(header, shared_datadir): # arrange - config_path = str(datadir / "without_logging") + config_path = str(shared_datadir / "without_logging") # act 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 -def test_config_file_exists(header, datadir): +# test config files + + +def test_config_file_exists(header, shared_datadir): # 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 config = panaetius.Config(header, config_path) config_contents = config.config # 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 diff --git a/tests/test_library.py b/tests/test_library.py new file mode 100644 index 0000000..2152e8f --- /dev/null +++ b/tests/test_library.py @@ -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" diff --git a/tests/test_logging.py b/tests/test_logging.py new file mode 100644 index 0000000..b1344ff --- /dev/null +++ b/tests/test_logging.py @@ -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) From 9299a12eb60e73a7b2b8fe94d5ef2f1e749607ec Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Mon, 18 Oct 2021 01:03:49 +0100 Subject: [PATCH 16/33] adding latest tests --- panaetius/config.py | 19 +++++++++++---- panaetius/exceptions.py | 3 +++ rewrite.todo | 6 +++-- tests/test_config.py | 52 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index 0f9f44a..193f287 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -7,7 +7,7 @@ from typing import Any import toml -from panaetius.exceptions import KeyErrorTooDeepException +from panaetius.exceptions import KeyErrorTooDeepException, InvalidPythonException class Config: @@ -57,6 +57,12 @@ class Config: try: # look under top header # REVIEW: could this be auto handled for a key of arbitrary length? + + # check for env variable and have it take priority + value = os.environ.get(env_key.replace("-", "_")) + if value is not None: + return self.__get_config_value_env_var_override(value) + if len(key.split(".")) > 2: raise KeyErrorTooDeepException( f"Your key of {key} can only be 2 levels deep maximum. " @@ -69,10 +75,9 @@ class Config: raise KeyError() except (KeyError, TypeError): - value = os.environ.get(env_key.replace("-", "_")) if value is None: return self.__get_config_value_missing_key_value_is_none(default) - # if env var, coerce value if flag is set, else return a TOML string + # if env var is present, load it return self.__get_config_value_missing_key_value_is_not_none(value) def __get_config_value_key_split_once(self, key: str) -> Any: @@ -89,6 +94,9 @@ class Config: def __get_config_value_missing_key_value_is_not_none(self, value: str) -> Any: return self.__load_value(value) + def __get_config_value_env_var_override(self, value: str) -> Any: + return self.__load_value(value) + def _get_env_value(self, env_key: str, default: Any) -> Any: # noqa # look for an environment variable, fallback to default value = os.environ.get(env_key.replace("-", "_")) @@ -97,7 +105,10 @@ class Config: return self.__load_value(value) def __load_value(self, value: str) -> Any: # noqa - return ast.literal_eval(value) + try: + return ast.literal_eval(value) + except (ValueError, SyntaxError): + raise InvalidPythonException(f"{value} is not valid Python.") # noqa def __load_default_value(self, default: Any) -> Any: # noqa return default diff --git a/panaetius/exceptions.py b/panaetius/exceptions.py index a7a6333..fbaea3a 100644 --- a/panaetius/exceptions.py +++ b/panaetius/exceptions.py @@ -4,3 +4,6 @@ class KeyErrorTooDeepException(Exception): class LoggingDirectoryDoesNotExistException(Exception): pass + +class InvalidPythonException(Exception): + pass diff --git a/rewrite.todo b/rewrite.todo index b28f883..f3f3a44 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -25,13 +25,14 @@ Coding: ☐ Write the docstrings for public functions/methods. Functionality: - ☐ When both a config file and a env var is found, use the env var. + ✔ When both a config file and a env var is found, use the env var. @done(21-10-18 00:38) Documentation: ☐ Rewrite documentation using `mkdocs` and using `.md`. @2h Misc: - ☐ Use the python runner to build the docs & run the tests + ☐ Use the python runner to build the docs & run the tests (including coverage html) + coverage run -m pytest && coverage report && coverage html ☐ document this in trilium Tests: @@ -63,6 +64,7 @@ Tests: ✔ check if env key is missing the default is read in @done(21-10-17 20:55) ✔ check if env key is present the values are read in @done(21-10-17 22:24) ✔ parametrise a test to read in values form env vars and they're set correctly @done(21-10-17 22:24) + ✔ test that the env var is valid python @done(21-10-18 01:03) library: ✔ test set_config works @done(21-10-17 23:29) diff --git a/tests/test_config.py b/tests/test_config.py index d9bb8ec..3137e22 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -5,7 +5,7 @@ from uuid import uuid4 import pytest import panaetius -from panaetius.exceptions import KeyErrorTooDeepException +from panaetius.exceptions import InvalidPythonException, KeyErrorTooDeepException # test config paths @@ -44,7 +44,9 @@ def test_config_file_exists(header, shared_datadir): assert config._missing_config is False -def test_config_file_contents_read_success(header, shared_datadir, testing_config_contents): +def test_config_file_contents_read_success( + header, shared_datadir, testing_config_contents +): # arrange config_path = str(shared_datadir / "without_logging") @@ -102,6 +104,23 @@ def test_get_value_from_key( assert config_value == expected_value +def test_get_value_environment_var_override(header, shared_datadir): + # arrange + os.environ[f"{header.upper()}_SOME_TOP_STRING"] = '"some_overridden_value"' + config_path = str(shared_datadir / "without_logging") + config = panaetius.Config(header, config_path) + panaetius.set_config(config, "some_top_string") + + # act + config_value = getattr(config, "some_top_string") + + # assert + assert config_value == "some_overridden_value" + + # cleanup + del os.environ[f"{header.upper()}_SOME_TOP_STRING"] + + def test_key_level_too_deep(header, shared_datadir): # arrange config_path = str(shared_datadir / "without_logging") @@ -151,6 +170,9 @@ def test_get_value_missing_key_from_env(header, shared_datadir): # assert assert value_from_key == "some missing key" + # cleanup + del os.environ[f"{header.upper()}_MISSING_KEY"] + # test env vars @@ -197,7 +219,9 @@ def test_missing_config_read_from_default(header, shared_datadir): ), ], ) -def test_missing_config_read_from_env_var(env_value, expected_value, header, shared_datadir): +def test_missing_config_read_from_env_var( + env_value, expected_value, header, shared_datadir +): # arrange config_path = str(shared_datadir / str(uuid4())) os.environ[f"{header.upper()}_MISSING_KEY_READ_FROM_ENV_VAR"] = env_value @@ -208,3 +232,25 @@ def test_missing_config_read_from_env_var(env_value, expected_value, header, sha # assert assert getattr(config, "missing_key_read_from_env_var") == expected_value + + # cleanup + del os.environ[f"{header.upper()}_MISSING_KEY_READ_FROM_ENV_VAR"] + + +def test_missing_config_read_from_env_var_invalid_python(header): + # arrange + os.environ[f"{header.upper()}_INVALID_PYTHON"] = "a string without quotes" + config = panaetius.Config(header) + + # act + with pytest.raises(InvalidPythonException) as invalid_python_exception: + panaetius.set_config(config, "invalid_python") + + # assert + assert ( + str(invalid_python_exception.value) + == "a string without quotes is not valid Python." + ) + + # cleanup + del os.environ[f"{header.upper()}_INVALID_PYTHON"] From ad840e6b27d392ba8364dd426ce2db4e75f2eaee Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Mon, 18 Oct 2021 02:31:17 +0100 Subject: [PATCH 17/33] adding latest testing + docstrings --- panaetius/__init__.py | 6 +++++ panaetius/config.py | 57 ++++++++++++++++++++++++++++++++++++++++- panaetius/exceptions.py | 4 +++ panaetius/library.py | 31 +++++++++++++++++++++- panaetius/logging.py | 46 +++++++++++++++++++++++++++++++++ prospector.yaml | 25 +++++++++--------- rewrite.todo | 4 +-- tests/scratchpad.py | 8 +++--- 8 files changed, 161 insertions(+), 20 deletions(-) diff --git a/panaetius/__init__.py b/panaetius/__init__.py index 500f8d6..6a0c396 100644 --- a/panaetius/__init__.py +++ b/panaetius/__init__.py @@ -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.library import set_config from panaetius.logging import set_logger, SimpleLogger, AdvancedLogger, CustomLogger diff --git a/panaetius/config.py b/panaetius/config.py index 193f287..d91aa4e 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -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 import ast @@ -11,9 +18,21 @@ from panaetius.exceptions import KeyErrorTooDeepException, InvalidPythonExceptio class Config: - """docstring for Config().""" + """The configuration class to access variables.""" 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.config_path = ( pathlib.Path(config_path) @@ -29,6 +48,12 @@ class Config: @property 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" try: with open(config_file_location, "r", encoding="utf-8") as config_file: @@ -37,6 +62,36 @@ class Config: return {} 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('.', '_')}" if not self._missing_config: diff --git a/panaetius/exceptions.py b/panaetius/exceptions.py index fbaea3a..0e03564 100644 --- a/panaetius/exceptions.py +++ b/panaetius/exceptions.py @@ -1,3 +1,6 @@ +"""Exceptions for the module.""" + + class KeyErrorTooDeepException(Exception): pass @@ -5,5 +8,6 @@ class KeyErrorTooDeepException(Exception): class LoggingDirectoryDoesNotExistException(Exception): pass + class InvalidPythonException(Exception): pass diff --git a/panaetius/library.py b/panaetius/library.py index f624159..ffea4c5 100644 --- a/panaetius/library.py +++ b/panaetius/library.py @@ -1,3 +1,5 @@ +"""Module to provide functionality when interacting with variables.""" + from __future__ import annotations from typing import Any @@ -9,6 +11,33 @@ def set_config( config_inst: Config, key: str, 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(".", "_") setattr(config_inst, config_var, config_inst.get_value(key, default)) diff --git a/panaetius/logging.py b/panaetius/logging.py index 4b86b3a..08f5221 100644 --- a/panaetius/logging.py +++ b/panaetius/logging.py @@ -1,3 +1,5 @@ +"""Module to define a convenient logger instance with json formatted output.""" + from __future__ import annotations 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: + """ + 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) log_handler_sys = logging.StreamHandler(sys.stdout) diff --git a/prospector.yaml b/prospector.yaml index f7479e1..03a75e7 100644 --- a/prospector.yaml +++ b/prospector.yaml @@ -12,9 +12,9 @@ pylint: # disables TODO warnings - fixme # !doc docstrings - - missing-module-docstring - - missing-class-docstring - - missing-function-docstring + # - missing-module-docstring + # - missing-class-docstring + # - missing-function-docstring # ! doc end of docstrings # disables warnings about abstract methods not overridden - abstract-method @@ -67,23 +67,24 @@ pep257: disable: # !doc docstrings # Missing docstring in __init__ - - D107 + # - D107 # Missing docstring in public module - - D100 + # - D100 # Missing docstring in public class - - D101 + # - D101 # Missing docstring in public method - - D102 + # - D102 # 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 - D203 # Multi-line docstring summary should start at the first line - 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 # Section name should end with a newline - D406 diff --git a/rewrite.todo b/rewrite.todo index f3f3a44..c432a16 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -19,10 +19,10 @@ Coding: ✔ Check for a key > 2 levels, raise custom error, write test @done(21-10-17 23:30) Linting: - ☐ Check all functions and annotations. + ✔ Check all functions and annotations. @done(21-10-18 01:07) Docstrings: - ☐ Write the docstrings for public functions/methods. + ✔ Write the docstrings for public functions/methods. @done(21-10-18 02:29) Functionality: ✔ When both a config file and a env var is found, use the env var. @done(21-10-18 00:38) diff --git a/tests/scratchpad.py b/tests/scratchpad.py index 8153517..6141857 100644 --- a/tests/scratchpad.py +++ b/tests/scratchpad.py @@ -50,8 +50,8 @@ if __name__ == "__main__": set_config(c, key="embedded.noexistbool", default=False) # logger = set_logger(c, SimpleLogger()) - logger = set_logger(c, AdvancedLogger(logging_level="DEBUG")) - logger.info("test logging message") + logger = set_logger(c, SimpleLogger(logging_level="DEBUG")) + logger.info("some logging message") logger.debug("debugging message") - for i in dir(c): - logger.debug(i + ": " + str(getattr(c, i)) + " - " + str(type(getattr(c, i)))) + # for i in dir(c): + # logger.debug(i + ": " + str(getattr(c, i)) + " - " + str(type(getattr(c, i)))) From 31fe9b1afc318b2fa1997784b391d22106959d5e Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Mon, 18 Oct 2021 02:32:15 +0100 Subject: [PATCH 18/33] removing old source code --- old_src/panaetius/__init__.py | 6 - old_src/panaetius/__version__.py | 1 - old_src/panaetius/config.py | 178 --------------------- old_src/panaetius/config_inst.py | 9 -- old_src/panaetius/db.py | 256 ------------------------------- old_src/panaetius/header.py | 26 ---- old_src/panaetius/library.py | 112 -------------- old_src/panaetius/logging.py | 54 ------- 8 files changed, 642 deletions(-) delete mode 100644 old_src/panaetius/__init__.py delete mode 100644 old_src/panaetius/__version__.py delete mode 100644 old_src/panaetius/config.py delete mode 100644 old_src/panaetius/config_inst.py delete mode 100644 old_src/panaetius/db.py delete mode 100644 old_src/panaetius/header.py delete mode 100644 old_src/panaetius/library.py delete mode 100644 old_src/panaetius/logging.py diff --git a/old_src/panaetius/__init__.py b/old_src/panaetius/__init__.py deleted file mode 100644 index 11348a3..0000000 --- a/old_src/panaetius/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from panaetius.config_inst import CONFIG -from .config import Config -from .library import set_config -from panaetius.header import __header__ -import panaetius.logging -from panaetius.logging import logger as logger diff --git a/old_src/panaetius/__version__.py b/old_src/panaetius/__version__.py deleted file mode 100644 index a6221b3..0000000 --- a/old_src/panaetius/__version__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = '1.0.2' diff --git a/old_src/panaetius/config.py b/old_src/panaetius/config.py deleted file mode 100644 index be8bfd0..0000000 --- a/old_src/panaetius/config.py +++ /dev/null @@ -1,178 +0,0 @@ -from typing import Callable, Union -import os -import toml - -from panaetius.library import export -from panaetius.header import __header__ -from panaetius.db import Mask - -# __all__ = ['Config'] - - -class Config: - - """Handles the config options for the module and stores config variables - to be shared. - - Attributes - ---------- - config_file : dict - Contains the config options. See - :meth:`~panaetius.config.Config.read_config` - for the data structure. - deferred_messages : list - A list containing the messages to be logged once the logger has been - instantiated. - Mask : panaetius.db.Mask - Class to mask values in a config file. - module_name : str - A string representing the module name. This is added in front of all - envrionment variables and is the title of the `config.toml`. - path : str - Path to config file - - Parameters - ---------- - path : str - Path to config file - """ - - def __init__(self, path: str, header: str = __header__) -> None: - """ - See :class:`~panaetius.config.Config` for parameters. - """ - self.path = os.path.expanduser(path) - self.header = header - self.deferred_messages = [] - self.config_file = self.read_config(path) - self.module_name = self.header.lower() - self.Mask = Mask - - def read_config(self, path: str, write: bool = False) -> Union[dict, None]: - """Reads the toml config file from `path` if it exists. - - """ - - path += 'config.toml' if path[-1] == '/' else '/config.toml' - path = os.path.expanduser(path) - if not write: - try: - with open(path, 'r+') as config_file: - config_file = toml.load(config_file) - self.defer_log(f'Config file found at {path}') - return config_file - except FileNotFoundError: - self.defer_log(f'Config file not found at {path}') - else: - try: - with open(path, 'w+') as config_file: - config_file = toml.load(config_file) - self.defer_log(f'Config file found at {path}') - return config_file - except FileNotFoundError: - self.defer_log(f'Config file not found at {path}') - - def get( - self, - key: str, - default: str = None, - cast: Callable = None, - mask: bool = False, - ) -> Union[str, None]: - """Retrives the config variable from either the `config.toml` or an - environment variable. Will default to the default value if nothing - is found - - Parameters - ---------- - key : str - Key to the configuration variable. Should be in the form - `panaetius.variable` or `panaetius.header.variable`. - When loaded, it will be accessable at - `Config.panaetius_variable` or - `Config.panaetius_header_variable`. - default : str, optional - The default value if nothing is found. Defaults to `None`. - cast : Callable, optional - The type of the variable. E.g `int` or `float`. Should reference - the type object and not as string. Defaults to `None`. - - Returns - ------- - Any - Will return the config variable if found, or the default. - """ - env_key = f"{self.header.upper()}_{key.upper().replace('.', '_')}" - - try: - # look in the config.toml - if len(key.split('.')) == 2: - # look for subsections - # print(mask) - if mask: - # print('mask', key) - value = self.Mask( - self.path, self.config_file, key - ).get_value() - else: - # print('no-mask') - section, name = key.lower().split('.') - value = self.config_file[self.module_name][section][name] - self.defer_log(f'{env_key} found in config.toml') - else: - # print('valueerror') - # look under top level module self.header - # key = f'{self.module_name}.key' - if mask: - # key = f'{self.header}.{key}' - # print(f'mask key={key}') - value = self.Mask( - self.path, self.config_file, key - ).get_value() - else: - name = key.lower() - value = self.config_file[self.module_name][name] - self.defer_log(f'{env_key} found in config.toml') - # finally: - try: - # return if found in config.toml - return cast(value) if cast else value - except UnboundLocalError: - # pass if nothing was found - # print('unbound error') - pass - except KeyError: - # print('key error') - self.defer_log(f'{env_key} not found in config.toml') - except TypeError: - # print('type error') - self.defer_log(f'{env_key} not found in config.toml') - - # look for an environment variable - value = os.environ.get(env_key.replace("-", "_")) - - if value is not None: - self.defer_log(f'{env_key} found in an environment variable') - else: - # fall back to default - self.defer_log(f'{env_key} not found in an environment variable.') - value = default - self.defer_log(f'{env_key} set to default {default}') - return cast(value) if cast else value - - def defer_log(self, msg: str) -> None: - """Populates a list `Config.deferred_messages` with all the events to - be passed to the logger later if required. - - Parameters - ---------- - msg : str - The message to be logged. - """ - self.deferred_messages.append(msg) - - def reset_log(self) -> None: - """Empties the list `Config.deferred_messages`. - """ - del self.deferred_messages - self.deferred_messages = [] diff --git a/old_src/panaetius/config_inst.py b/old_src/panaetius/config_inst.py deleted file mode 100644 index 43f0d1b..0000000 --- a/old_src/panaetius/config_inst.py +++ /dev/null @@ -1,9 +0,0 @@ -import os - -from panaetius.header import __header__ -from panaetius.config import Config - - -DEFAULT_CONFIG_PATH = f"~/.config/{__header__.lower()}" -CONFIG_PATH = os.environ.get(f"{__header__.upper()}_CONFIG_PATH", DEFAULT_CONFIG_PATH) -CONFIG = Config(CONFIG_PATH) diff --git a/old_src/panaetius/db.py b/old_src/panaetius/db.py deleted file mode 100644 index 198b095..0000000 --- a/old_src/panaetius/db.py +++ /dev/null @@ -1,256 +0,0 @@ -from os import path, urandom -import hashlib -from typing import Tuple -import toml -import io - -from pylite.simplite import Pylite - -from panaetius.header import __header__ as __header__ -import panaetius - - -class Mask: - - """Class to handle masking sensitive values in a config file - - Attributes - ---------- - config_contents : dict - A dict containing the contents of the config file. - config_path : str - The path to the config file. - config_var : str - The key corresponding to the config entry. - database : Pylite - A Pylite instance for the datbase. - entry : str - The result from the config file. Could either be a hash or the raw - value. - header : str - The __header__ which denotes where the config file is stored. - name : str - The key of the entry in the config file. - result : str - The value of the entry in the config file. - table_name : str - The sqlite table name. Defaults to the __header__ value. - """ - - @property - def hash(self): - """Property to determine the hash of a config entry. - - Returns - ------- - bytes - The hash as a bytes object. - """ - try: - if not self._hash_exists: - pass - except AttributeError: - self._hash = hashlib.pbkdf2_hmac( - 'sha256', - self.entry[self.name].encode('utf-8'), - self.salt, - 100000, - dklen=12, - ) - self._hash_exists = True - finally: - return self._hash - - @property - def salt(self): - """Property to detemine a random salt to use in creation of the hash. - - Returns - ------- - bytes - The salt as a bytes object. - """ - self._salt = urandom(32) - return self._salt - - @staticmethod - def as_string(obj: bytes) -> str: - """Static method to return a string from a bytes object. - - Parameters - ---------- - obj : bytes - Bytes object to be converted to a string. - - Returns - ------- - str - The bytes object as a string. - """ - return bytes.hex(obj) - - @staticmethod - def fromhex(obj: str) -> bytes: - """Static method to create a bytes object from a string. - - Parameters - ---------- - obj : str - String object to be converted to bytes. - - Returns - ------- - bytes - The string object as bytes. - """ - return bytes.fromhex(obj) - - @staticmethod - def _from_key(config_var) -> Tuple[str, str]: - try: - header, name = config_var.split('.') - except ValueError: - header = '' - name = config_var - return (header, name) - - def __init__( - self, config_path: str, config_contents: dict, config_var: str - ): - """Summary - See :class:`~Mask` for parameters. - """ - self.table: str = __header__ - self.config_path = config_path - self.config_contents = config_contents - self.config_var = config_var.replace('.', '_') - self.header = self._from_key(config_var)[0] - self.name = self._from_key(config_var)[1] - try: - # If value is under a subsection - self.entry = self.config_contents[self.table][self.header] - except KeyError: - # If value is under the main header - self.entry = self.config_contents[self.table] - - def _get_database_file(self): - self.database = self.config_path - self.database += ( - f'.{self.table}.db' - if self.config_path[-1] == '/' - else f'/.{self.table}.db' - ) - self.database = path.expanduser(self.database) - return self - - def _open_database(self): - self.database = Pylite(self.database) - - def _get_table(self): - tables = [i[0] for i in self.database.get_tables()] - if self.table not in tables: - # panaetius.logger.debug( - # 'Table not present in the database;' - # f'creating the table {self.table} now' - # ) - self.database.add_table( - f'{self.table}', - Name='text', - Hash='text', - Salt='text', - Value='text', - ) - else: - # panaetius.logger.debug('Table already exists in the database') - pass - self.table_name = self.table - - def _check_entries(self): - var = self.database.get_items(self.table, f'Name="{self.config_var}"') - if len(var) == 0: - return False - else: - return True - - def _insert_entries(self): - self.database.insert( - self.table, - self.config_var, - self.as_string(self.hash), - self.as_string(self.salt), - self.entry[self.name], - ) - - def _update_entries_in_db(self): - self.database.remove(self.table, f'Name="{self.config_var}"') - self._insert_entries() - - def _run_query(self, query: str): - cur = self.database.db.cursor() - cur.execute(query) - self.database.db.commit() - self.result = cur.fetchall() - return self - - def _get_all_items(self, where_clause: str = None): - if where_clause is not None: - self.result = self.database.get_items(self.table, where_clause) - else: - self.result = self.database.get_items(self.table) - return self - - def _process(self): - if not self._check_entries(): - # panaetius.logger.debug('does not exist') - self._insert_entries() - self._update_entries_in_config() - self._get_all_items() - # panaetius.logger.debug(f'returning: {self.result[0][3]}') - return self.entry[self.name] - else: - self._get_all_items(f'Name="{self.config_var}"') - if self.result[0][1] == self.entry[self.name]: - # panaetius.logger.debug('exists and hash matches') - # panaetius.logger.debug(f'returning: {self.result[0][3]}') - return self.result - else: - # panaetius.logger.debug('exists and hash doesnt match') - # panaetius.logger.debug( - # f'file_hash={self.entry[self.name]}, {self.result[0][1]}' - # ) - self._update_entries_in_db() - self._update_entries_in_config() - self._get_all_items(f'Name="{self.config_var}"') - # panaetius.logger.debug(f'returning: {self.result[0][3]}') - return self.entry[self.name] - - def _open_config_file(self) -> io.TextIOWrapper: - self.config_path += ( - '/config.toml' if self.config_path[-1] != '/' else 'config.toml' - ) - c = open(path.expanduser(self.config_path), 'w') - return c - - def _update_entries_in_config(self): - self.entry.update({self.name: self.as_string(self.hash)}) - # panaetius.logger.debug(self.config_contents) - # panaetius.logger.debug(self.entry) - c = self._open_config_file() - toml.dump(self.config_contents, c) - c.close() - - def get_value(self): - """Get the true value from the database if it exists, create if it' - ' doesn't exist or update if the hash has changed. - - Returns - ------- - str - The result from the database. - """ - # print(f'key in db {self.config_var}') - self._get_database_file() - self._open_database() - self._get_table() - self._process() - return self.result[0][3] diff --git a/old_src/panaetius/header.py b/old_src/panaetius/header.py deleted file mode 100644 index 4d365fa..0000000 --- a/old_src/panaetius/header.py +++ /dev/null @@ -1,26 +0,0 @@ -import os -from importlib import util - -__path = os.getcwd() - -try: - __spec = util.spec_from_file_location( - '__header__', f'{os.getcwd()}/__header__.py' - ) - __header__ = util.module_from_spec(__spec) - __spec.loader.exec_module(__header__) - __header__ = __header__.__header__ -except FileNotFoundError: - try: - venv = os.environ.get('VIRTUAL_ENV').split('/')[-1] - __header__ = venv - except AttributeError: - print( - f'Cannot find a __header__.py file in {os.getcwd()} containing the' - ' __header__ value of your project name and you are not working' - ' from a virtual environment. Either make sure this file ' - 'exists and the value is set or create and work from a virtual ' - 'environment and try again. \n The __header__ value has been ' - 'set to the default of panaetius.' - ) - __header__ = 'panaetius' diff --git a/old_src/panaetius/library.py b/old_src/panaetius/library.py deleted file mode 100644 index 9aa5b1e..0000000 --- a/old_src/panaetius/library.py +++ /dev/null @@ -1,112 +0,0 @@ -from __future__ import annotations -import sys -from typing import Any, TypeVar, Type, TYPE_CHECKING, Union, List -import ast - - -if TYPE_CHECKING: - import logging - - -config_inst_t = TypeVar('config_inst_t', bound='panaetius.config.Config') - - -def export(fn: callable) -> callable: - mod = sys.modules[fn.__module__] - if hasattr(mod, '__all__'): - mod.__all__.append(fn.__name__) - else: - mod.__all__ = [fn.__name__] - return fn - - -def set_config( - config_inst: Type[config_inst_t], - key: str, - default: str = None, - cast: Any = None, - check: Union[None, List] = None, - mask: bool = False, -) -> None: - """Sets the config variable on the instance of a class. - - Parameters - ---------- - config_inst : Type[config_inst_t] - Instance of the :class:`~panaetius.config.Config` class. - key : str - The key referencing the config variable. - default : str, optional - The default value. - mask : bool, optional - Boolean to indiciate if a value in the `config.toml` should be masked. - If this is set to True then the first time the variable is read from - the config file the value will be replaced with a hash. Any time that - value is then read the hash will be compared to the one stored and if - they match the true value will be returned. This is stored in a sqlite - `.db` next to the config file and is hidden by default. If the hash - provided doesn't match the default behaviour is to update the `.db` - with the new value and hash the value again. If you delete the - database file then you will need to set the value again in the - `config.toml`. - cast : Any, optional - The type of the variable. - check : Union[None, List], optional - Type of object to check against. This is useful if you want to use TOML - to define a list, but want to make sure that a string representation - of a list will be loaded properly if it set as an environment variable. - - Example: - - *config.toml* has the following attribute set:: - - [package.users] - auth = ['user1', 'user2'] - - If set as an environment variable you can pass this list as a string - and set :code:`check=list`:: - - Environment variable: - PACKAGE_USERS_AUTH = "['user1', 'user2']" - - Usage in code:: - - set_config(CONFIG, 'users.auth', check=list) - """ - config_var = key.lower().replace('.', '_') - if check is None: - setattr( - config_inst, config_var, config_inst.get(key, default, cast, mask) - ) - else: - if type(config_inst.get(key, default, cast, mask)) is not check: - if check is list: - var = ast.literal_eval( - config_inst.get(key, default, cast, mask) - ) - setattr(config_inst, config_var, var) - else: - setattr( - config_inst, - config_var, - config_inst.get(key, default, cast, mask), - ) - - -# Create function to print cached logged messages and reset -def process_cached_logs( - config_inst: Type[config_inst_t], logger: logging.Logger -): - """Prints the cached messages from :class:`~panaetius.config.Config` - and resets the cache. - - Parameters - ---------- - config_inst : Type[config_inst_t] - Instance of :class:`~panaetius.config.Config`. - logger : logging.Logger - Instance of the logger. - """ - for msg in config_inst.deferred_messages: - logger.info(msg) - config_inst.reset_log() diff --git a/old_src/panaetius/logging.py b/old_src/panaetius/logging.py deleted file mode 100644 index 4236665..0000000 --- a/old_src/panaetius/logging.py +++ /dev/null @@ -1,54 +0,0 @@ -import logging -from logging.handlers import RotatingFileHandler -import os -import sys - -import panaetius -from panaetius import CONFIG as CONFIG -from panaetius import __header__ as __header__ -from panaetius import set_config as set_config - - -panaetius.set_config(CONFIG, 'logging.path') -panaetius.set_config( - CONFIG, - 'logging.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}', - cast=str, -) -set_config(CONFIG, 'logging.level', 'INFO') - -# Logging Configuration -logger = logging.getLogger(__header__) -loghandler_sys = logging.StreamHandler(sys.stdout) - -# Checking if log path is set -if CONFIG.logging_path: - CONFIG.logging_path += ( - f'{__header__}.log' - if CONFIG.logging_path[-1] == '/' - else f'/{__header__}.log' - ) - # Set default log file options - set_config(CONFIG, 'logging.backup_count', 3, int) - set_config(CONFIG, 'logging.rotate_bytes', 512000, int) - - # Configure file handler - loghandler_file = RotatingFileHandler( - os.path.expanduser(CONFIG.logging_path), - 'a', - CONFIG.logging_rotate_bytes, - CONFIG.logging_backup_count, - ) - - # Add to file formatter - loghandler_file.setFormatter(logging.Formatter(CONFIG.logging_format)) - logger.addHandler(loghandler_file) - -# Configure and add to stdout formatter -loghandler_sys.setFormatter(logging.Formatter(CONFIG.logging_format)) -logger.addHandler(loghandler_sys) -logger.setLevel(CONFIG.logging_level) From d604179cbf6815bfc06ec4c86ffa5ad9c113c2f7 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Mon, 18 Oct 2021 02:36:27 +0100 Subject: [PATCH 19/33] updating todos --- rewrite.todo | 97 +++++++++++++++++++--------------------------------- 1 file changed, 35 insertions(+), 62 deletions(-) diff --git a/rewrite.todo b/rewrite.todo index c432a16..19142e5 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -1,70 +1,43 @@ -Coding: - No Config File: - ✔ Handle if a bool is passed in as a default @done(21-10-16 05:25) - needs to be lower case in the toml, need a check for this - - Config File: - ✔ Handle if a bool is passed in as a default @done(21-10-16 05:25) - needs to be lower case in the toml, need a check for this - - Logging: - ✔ Create SimpleLogger, AdvancedLogger, CustomLogger classes @done(21-10-16 16:22) - should simply have the different logging strings to output - should both specify whether to save to file or not - ✔ Logging path should take by default the config path unless overwritten? @done(21-10-16 23:49) - - Errors: - ✔ Check logging path + config path are valid, if not raise error. @done(21-10-18 00:04) - ✔ 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: - ✔ Check all functions and annotations. @done(21-10-18 01:07) - - Docstrings: - ✔ Write the docstrings for public functions/methods. @done(21-10-18 02:29) - - Functionality: - ✔ When both a config file and a env var is found, use the env var. @done(21-10-18 00:38) - Documentation: - ☐ Rewrite documentation using `mkdocs` and using `.md`. @2h + ☐ 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 -Tests: - 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) - 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__: - ✔ Test default config path set to "~/.config" @done(21-10-17 17:25) - ✔ Test config path is set when passed in @done(21-10-17 17:25) - - config property: - ✔ Check testing config file is returned as dict @done(21-10-17 17:25) - ✔ 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) - ✔ test that the env var is valid python @done(21-10-18 01:03) - - library: - ✔ test set_config works @done(21-10-17 23:29) +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) From 525107ad634c0ec6a7d41ccd420ee8d630b281fb Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Tue, 19 Oct 2021 21:46:41 +0100 Subject: [PATCH 20/33] adding a config.yml instead of config.toml --- .vscode/settings.json | 17 +++++++++++++- panaetius/config.py | 22 ++++++++++--------- poetry.lock | 16 ++------------ pyproject.toml | 2 +- .../panaetius_testing/config.yml | 9 ++++++++ 5 files changed, 40 insertions(+), 26 deletions(-) create mode 100644 tests/data/without_logging/panaetius_testing/config.yml diff --git a/.vscode/settings.json b/.vscode/settings.json index 9f07c66..7c32b0d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,20 @@ "python.linting.enabled": true, "python.pythonPath": ".venv/bin/python", "restructuredtext.confPath": "${workspaceFolder}/docs/source", - "peacock.color": "#307E6A" + "peacock.color": "#307E6A", + "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" + } } diff --git a/panaetius/config.py b/panaetius/config.py index d91aa4e..727f2c1 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -2,7 +2,7 @@ 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. +`config.yml` or an environment variable. """ from __future__ import annotations @@ -12,7 +12,8 @@ import os import pathlib from typing import Any -import toml +# import toml +import yaml from panaetius.exceptions import KeyErrorTooDeepException, InvalidPythonException @@ -31,7 +32,7 @@ class Config: Example: A header of `data_analysis` with a config_path of `~/myapps` will define - a config file in `~/myapps/data_analysis/config.toml`. + a config file in `~/myapps/data_analysis/config.yml`. """ self.header_variable = header_variable self.config_path = ( @@ -52,12 +53,13 @@ class Config: 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. + dict: The contents of the `.yml` 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.yml" try: with open(config_file_location, "r", encoding="utf-8") as config_file: - return dict(toml.load(config_file)) + # return dict(toml.load(config_file)) + return dict(yaml.load(stream=config_file, Loader=yaml.SafeLoader)) except FileNotFoundError: return {} @@ -67,8 +69,8 @@ class Config: 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: + A key of `value` (with a header of `data_analysis`) would refer to a + `config.yml` of: ``` [data_analysis] @@ -77,7 +79,7 @@ class Config: or an environment variable of `DATA_ANALYSIS_VALUE="'some value'"`. - A key of (`data.value`) would refer to a `config.toml` of: + A key of `data.value` would refer to a `config.yml` of: ``` [data_analysis.data] value = "some value" @@ -101,7 +103,7 @@ class Config: return self._get_env_value(env_key, default) def _check_config_file_exists(self) -> bool: - config_file_location = self.config_path / self.header_variable / "config.toml" + config_file_location = self.config_path / self.header_variable / "config.yml" try: with open(config_file_location, "r", encoding="utf-8"): return False diff --git a/poetry.lock b/poetry.lock index 7580b27..3302112 100644 --- a/poetry.lock +++ b/poetry.lock @@ -421,14 +421,6 @@ python-versions = "*" [package.dependencies] pylint = ">=1.7" -[[package]] -name = "pylite" -version = "0.1.0" -description = "Intract with sqlite3 in python as simple as it can be." -category = "main" -optional = false -python-versions = "*" - [[package]] name = "pyparsing" version = "2.4.7" @@ -504,7 +496,7 @@ testing = ["filelock"] name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "dev" +category = "main" optional = false python-versions = ">=3.6" @@ -621,7 +613,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "468d1aa5e0c440262f6041ad859358a84ef32462941aa6f3ba71838a52cc1ced" +content-hash = "f68e5ab35a155ce5ea567b670f93f4678b6c65d1335017b21431b315297a0410" [metadata.files] astroid = [ @@ -834,10 +826,6 @@ pylint-plugin-utils = [ {file = "pylint-plugin-utils-0.6.tar.gz", hash = "sha256:57625dcca20140f43731311cd8fd879318bf45a8b0fd17020717a8781714a25a"}, {file = "pylint_plugin_utils-0.6-py3-none-any.whl", hash = "sha256:2f30510e1c46edf268d3a195b2849bd98a1b9433229bb2ba63b8d776e1fc4d0a"}, ] -pylite = [ - {file = "pylite-0.1.0-py3-none-any.whl", hash = "sha256:eb46f5beb1f2102672fd4355c013ac2feebc0df284d65f7711f2041a0a410141"}, - {file = "pylite-0.1.0.tar.gz", hash = "sha256:e338d20d3f8f72dd84d1e58f2fd6dba008d593e0cfacfb5fbdd5a297b830628e"}, -] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, diff --git a/pyproject.toml b/pyproject.toml index 3be3c93..7f5c53a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.7" toml = "^0.10.0" -pylite = "^0.1.0" +PyYAML = "^6.0" [tool.poetry.dev-dependencies] prospector = {extras = ["with_bandit", "with_mypy"], version = "^1.5.1"} diff --git a/tests/data/without_logging/panaetius_testing/config.yml b/tests/data/without_logging/panaetius_testing/config.yml new file mode 100644 index 0000000..377f992 --- /dev/null +++ b/tests/data/without_logging/panaetius_testing/config.yml @@ -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] } From 948bc65e769c81912d469dbab19422253236d869 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Wed, 20 Oct 2021 22:26:46 +0100 Subject: [PATCH 21/33] removing old files --- README.md.old | 33 --------------------------------- TODO | 3 --- 2 files changed, 36 deletions(-) delete mode 100644 README.md.old delete mode 100644 TODO diff --git a/README.md.old b/README.md.old deleted file mode 100644 index ccd7563..0000000 --- a/README.md.old +++ /dev/null @@ -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 - diff --git a/TODO b/TODO deleted file mode 100644 index a0814df..0000000 --- a/TODO +++ /dev/null @@ -1,3 +0,0 @@ - -Todo: - ☐ Item From d7700c4863851c676250ca73f637d007f71c645f Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Wed, 20 Oct 2021 22:29:08 +0100 Subject: [PATCH 22/33] adding squasher utility --- panaetius/utilities/__init__.py | 3 + panaetius/utilities/squasher.py | 64 ++++++++++++++ tests/test_utilities/__init__.py | 0 tests/test_utilities/test_squasher.py | 119 ++++++++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 panaetius/utilities/__init__.py create mode 100644 panaetius/utilities/squasher.py create mode 100644 tests/test_utilities/__init__.py create mode 100644 tests/test_utilities/test_squasher.py diff --git a/panaetius/utilities/__init__.py b/panaetius/utilities/__init__.py new file mode 100644 index 0000000..5dc9cbc --- /dev/null +++ b/panaetius/utilities/__init__.py @@ -0,0 +1,3 @@ +"""General utilities.""" + +from panaetius.utilities import squasher diff --git a/panaetius/utilities/squasher.py b/panaetius/utilities/squasher.py new file mode 100644 index 0000000..b0f81a9 --- /dev/null +++ b/panaetius/utilities/squasher.py @@ -0,0 +1,64 @@ +"""Squash a json object or Python dictionary into a single level dictionary.""" + +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 diff --git a/tests/test_utilities/__init__.py b/tests/test_utilities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_utilities/test_squasher.py b/tests/test_utilities/test_squasher.py new file mode 100644 index 0000000..8b5817e --- /dev/null +++ b/tests/test_utilities/test_squasher.py @@ -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", + } From 8c18d01f052d8a4794c872a4e1393b7a684d8c92 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Wed, 20 Oct 2021 22:29:19 +0100 Subject: [PATCH 23/33] bumping version to 2.1 --- pyproject.toml | 2 +- setup.py | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7f5c53a..da50202 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "panaetius" -version = "1.1" +version = "2.1.0" 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 "] diff --git a/setup.py b/setup.py index 921f12b..f8e332c 100644 --- a/setup.py +++ b/setup.py @@ -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.1.0', + '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': '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 ``panaetius`` the following ways:\n\nPython\n-------\n\n.. Attention:: You should install in a python virtual environment\n\nFrom pypi using pip\n~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: bash\n\n pip install panaetius\n\nFrom local wheel\n~~~~~~~~~~~~~~~~~\n\nDownload the latest verion from the `releases`_ page.\n\n.. _releases: https://github.com/dtomlinson91/panaetius/releases\n\nInstall with pip:\n\n.. code-block:: bash\n\n pip install -U panaetius-1.0.2-py3-none-any.whl\n\n\nFrom source\n~~~~~~~~~~~~\n\nClone the repo and install using ``setup.py``:\n\n.. code-block:: bash\n\n python setup.py\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, From 6e24f9d70baf19ccd1236fa8d8d3018319622e46 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Fri, 22 Oct 2021 22:44:50 +0100 Subject: [PATCH 24/33] adding Squash to __init__.py --- panaetius/utilities/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panaetius/utilities/__init__.py b/panaetius/utilities/__init__.py index 5dc9cbc..7e54de0 100644 --- a/panaetius/utilities/__init__.py +++ b/panaetius/utilities/__init__.py @@ -1,3 +1,3 @@ """General utilities.""" -from panaetius.utilities import squasher +from panaetius.utilities.squasher import Squash From 9cc6f2483d1cde848c8723a4e648d66de753497e Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Fri, 22 Oct 2021 22:45:13 +0100 Subject: [PATCH 25/33] bumping to 2.2.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index da50202..2a43b63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "panaetius" -version = "2.1.0" +version = "2.2.0" 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 "] From 441a26127ffad21f46297dd6301232213952a0b6 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 23 Oct 2021 05:05:35 +0100 Subject: [PATCH 26/33] remove DS_Store --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 3 +++ 2 files changed, 3 insertions(+) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index c88a062b05be4fd1d362b3e4c6a7481e718b69d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~O$x$5422WzLU7Zi%h`AUZ!n0Spcima5J4*Vx1OW>k_m#k8KJGkN^pg011%5 z4-v3?8#bF)Wh4O-Ab}?V`#vPNX$~z_{nLTqBLK8P*$r!-C7{U)&>UK-q5{*H9yD6j z#}KP~J2b_)99pW@cF`C;crrWIXQgOGwy`I%~QMGk}L;X0y%TE9jyNVZZH|!@{KyzrRiVBQB0*--!1inh( E0rZ6u#{d8T diff --git a/.gitignore b/.gitignore index 3f62bda..c639aa9 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,6 @@ dmypy.json # Pyre type checker .pyre/ + +# custom +.DS_Store From 70911f98b01cb0b6c7bf3718ed1971c2e9ccd776 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 23 Oct 2021 05:05:46 +0100 Subject: [PATCH 27/33] add expand_user to Config --- panaetius/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panaetius/config.py b/panaetius/config.py index 727f2c1..ab14ae2 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -36,7 +36,7 @@ class Config: """ self.header_variable = header_variable self.config_path = ( - pathlib.Path(config_path) + pathlib.Path(config_path).expanduser() if config_path else pathlib.Path.home() / ".config" ) From 9f1caf79fff186d2f9acf0a8e4fe0dcba63e94f3 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 23 Oct 2021 05:06:10 +0100 Subject: [PATCH 28/33] patch - v2.2.1 --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2a43b63..dfbd548 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "panaetius" -version = "2.2.0" +version = "2.2.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." license = "MIT" authors = ["dtomlinson "] diff --git a/setup.py b/setup.py index f8e332c..c61e662 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ install_requires = \ setup_kwargs = { 'name': 'panaetius', - 'version': '2.1.0', + 'version': '2.2.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': '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 ``panaetius`` the following ways:\n\nPython\n-------\n\n.. Attention:: You should install in a python virtual environment\n\nFrom pypi using pip\n~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: bash\n\n pip install panaetius\n\nFrom local wheel\n~~~~~~~~~~~~~~~~~\n\nDownload the latest verion from the `releases`_ page.\n\n.. _releases: https://github.com/dtomlinson91/panaetius/releases\n\nInstall with pip:\n\n.. code-block:: bash\n\n pip install -U panaetius-1.0.2-py3-none-any.whl\n\n\nFrom source\n~~~~~~~~~~~~\n\nClone the repo and install using ``setup.py``:\n\n.. code-block:: bash\n\n python setup.py\n', 'author': 'dtomlinson', From 16f753fdf34e4ec41d85914d0d04a4ee743261fe Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 23 Oct 2021 21:05:36 +0100 Subject: [PATCH 29/33] updating todo --- rewrite.todo | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/rewrite.todo b/rewrite.todo index 19142e5..bb99cef 100644 --- a/rewrite.todo +++ b/rewrite.todo @@ -1,16 +1,26 @@ +Testing: + To Write: + ☐ Test the Config file skipping header with `skip_header_init` + ☐ Document coverage commands + `coverage run --source=./panaetius -m pytest` + `coverage report` & `coverage html` > gives ./htmlcov/index.html + ☐ Document for abstract methods should raise NotImplementedError + ☐ Document https://stackoverflow.com/a/9212387 Documentation: ☐ Rewrite documentation using `mkdocs` and using `.md`. ☐ Update the metadata in the `pyproject.toml`. ☐ Create a new `Readme.md` and remove the `.rst`. + ☐ Document the logging strategy + CLI tools should use `logger.critical` and raise SystemExit(1) + Libraries should raise custom errors and have a `logger.critical(exec_info=1)` 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: + ✘ Bump the version to release 2.0 @cancelled(21-10-23 05:36) @project(Misc) ✔ 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) From 2092245dadfeff8585a2b938b9bc407d4b5d0fca Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 23 Oct 2021 21:07:07 +0100 Subject: [PATCH 30/33] adding skip header directory option --- panaetius/config.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index ab14ae2..0c64d92 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -21,14 +21,24 @@ from panaetius.exceptions import KeyErrorTooDeepException, InvalidPythonExceptio class 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 = 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 @@ -37,9 +47,10 @@ class Config: self.header_variable = header_variable self.config_path = ( pathlib.Path(config_path).expanduser() - if config_path + 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 @@ -55,7 +66,12 @@ class Config: Returns: dict: The contents of the `.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 +119,10 @@ class Config: return self._get_env_value(env_key, default) def _check_config_file_exists(self) -> bool: - config_file_location = self.config_path / self.header_variable / "config.yml" + 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 From 844a2f6f3fb004ddce39bb1fb080ab4996528ea4 Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 23 Oct 2021 21:07:31 +0100 Subject: [PATCH 31/33] changing env var to use strings without extra quotes --- panaetius/config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/panaetius/config.py b/panaetius/config.py index 0c64d92..34fa52a 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -15,7 +15,7 @@ from typing import Any # import toml import yaml -from panaetius.exceptions import KeyErrorTooDeepException, InvalidPythonException +from panaetius.exceptions import KeyErrorTooDeepException class Config: @@ -184,7 +184,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 From 485ab9ef091e3982e4f177e36fa897a41c84bd4b Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 23 Oct 2021 21:08:07 +0100 Subject: [PATCH 32/33] change abc to raise NotImplementedError for tests --- .coveragerc | 7 +++++++ panaetius/logging.py | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..ff6415d --- /dev/null +++ b/.coveragerc @@ -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 diff --git a/panaetius/logging.py b/panaetius/logging.py index 08f5221..a307af3 100644 --- a/panaetius/logging.py +++ b/panaetius/logging.py @@ -98,11 +98,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): From 1af790f01a0ca254277d99dd14917bd8bf284f2d Mon Sep 17 00:00:00 2001 From: Daniel Tomlinson Date: Sat, 23 Oct 2021 21:08:16 +0100 Subject: [PATCH 33/33] updating tests --- panaetius/config.py | 1 - tests/data/without_header/config.yml | 9 +++++++++ tests/test_config.py | 30 +++++++++++++++++++++++++--- tests/test_logging.py | 3 +++ 4 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 tests/data/without_header/config.yml diff --git a/panaetius/config.py b/panaetius/config.py index 34fa52a..3677acf 100644 --- a/panaetius/config.py +++ b/panaetius/config.py @@ -40,7 +40,6 @@ class Config: 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`. """ diff --git a/tests/data/without_header/config.yml b/tests/data/without_header/config.yml new file mode 100644 index 0000000..377f992 --- /dev/null +++ b/tests/data/without_header/config.yml @@ -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] } diff --git a/tests/test_config.py b/tests/test_config.py index 3137e22..21b282c 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -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" diff --git a/tests/test_logging.py b/tests/test_logging.py index b1344ff..46ccc78 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -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