31 Commits

Author SHA1 Message Date
dependabot[bot]
7eab3bb2cf Bump certifi from 2021.10.8 to 2022.12.7
Bumps [certifi](https://github.com/certifi/python-certifi) from 2021.10.8 to 2022.12.7.
- [Release notes](https://github.com/certifi/python-certifi/releases)
- [Commits](https://github.com/certifi/python-certifi/compare/2021.10.08...2022.12.07)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-08 13:02:43 +00:00
528f11c8eb chore: release v2.3.5 2022-01-30 17:32:29 +00:00
4681d98863 chore: prepare release v2.3.5 2022-01-30 02:54:04 +00:00
a0583b5f0a build: relax PyYAML version constraint for better compatibility 2022-01-30 02:50:56 +00:00
ed0da2e0bf chore: update changelog compare url 2022-01-30 02:49:42 +00:00
d45176accc chore: update CHANGELOG.md for new cliff.toml 2022-01-06 23:42:24 +00:00
19e578b0ea chore: update .gitignore 2022-01-06 23:42:12 +00:00
4cc55874c7 chore: update cliff.toml 2022-01-06 23:41:38 +00:00
Daniel Tomlinson
709f1ae997 chore: release v2.3.4 2022-01-03 02:34:51 +00:00
dd4c5950b3 chore: fix CHANGELOG.md 2022-01-03 02:34:22 +00:00
34c526015f chore: update cliff.toml 2022-01-03 02:31:17 +00:00
04162ea392 chore: prepare release v2.3.4 2022-01-03 02:18:45 +00:00
9bc89fd2ce build: update dependencies 2022-01-03 02:16:46 +00:00
acf956bf0f chore: add cliff.toml 2022-01-03 02:14:06 +00:00
156af46855 tests: add tests for f5ea19e 2022-01-03 02:03:22 +00:00
e7602ced32 chore: update duties.py 2022-01-03 02:00:20 +00:00
f5ea19e7d2 feat: add ability to retrieve keys 3 levels deep 2022-01-03 01:58:45 +00:00
e6cfded87d docs: update README.md with script quickstart logging 2021-12-31 16:24:56 +00:00
79bd1cab31 Merge branch 'develop' 2021-12-29 04:46:34 +00:00
255b7d57f5 Merge remote-tracking branch 'origin/develop' into develop 2021-12-29 04:46:18 +00:00
1790071741 docs: update README.md with script boilerplate 2021-12-29 04:46:10 +00:00
96e1e4c596 chore: prepare release 2.3.3 2021-11-20 22:06:40 +00:00
2dffd289eb adding isort mypy safety 2021-11-20 18:37:52 +00:00
8e11733762 updating dev dependencies 2021-11-20 18:36:07 +00:00
24d5588987 Merge branch 'typing/workaround_1' into develop 2021-11-20 18:34:52 +00:00
a1fa22cbe9 Merge branch 'typing/workaround_2' into typing/workaround_1 2021-11-20 18:34:48 +00:00
22935237be updating latest 2021-11-20 18:34:35 +00:00
e98f1ad80d workaround #1 2021-11-20 16:33:16 +00:00
df2318aaaf adding pyyaml types 2021-11-20 16:32:46 +00:00
4ec095b65f adding py.typed 2021-11-20 16:32:30 +00:00
9b0d0ec42d linting 2021-11-20 16:32:24 +00:00
16 changed files with 577 additions and 310 deletions

1
.gitignore vendored
View File

@@ -130,3 +130,4 @@ dmypy.json
# custom # custom
.DS_Store .DS_Store
.vscode/*

View File

@@ -1,8 +0,0 @@
{
"python.linting.pylintEnabled": false,
"python.linting.prospectorEnabled": true,
"python.linting.enabled": true,
"python.pythonPath": ".venv/bin/python",
"restructuredtext.confPath": "${workspaceFolder}/docs/source",
"peacock.color": "#307E6A"
}

30
CHANGELOG.md Normal file
View File

@@ -0,0 +1,30 @@
# Changelog
All notable changes to this project will be documented in this file.
<!-- marker -->
## [v2.3.5](https://github.com/dtomlinson91/panaetius/commits/v2.3.5) - 2022-01-30
<small>[Compare with v2.3.4](https://github.com/dtomlinson91/panaetius/compare/v2.3.4...v2.3.5)</small>
### 🧱 Build
- Relax PyYAML version constraint for better compatibility ([a0583b5](https://github.com/dtomlinson91/panaetius/commit/a0583b5f0aa3068139827ff46f8a7aa16cd6b424))
## [v2.3.4](https://github.com/dtomlinson91/panaetius/commits/v2.3.4) - 2022-01-03
<small>[Compare with 2.3.3](https://github.com/dtomlinson91/panaetius/compare/2.3.3...v2.3.4)</small>
### ✨ Features
- Add ability to retrieve keys 3 levels deep ([f5ea19e](https://github.com/dtomlinson91/panaetius/commit/f5ea19e7d2f977244594b378c6b7633f02f6048a))
### 📘 Documentation
- Update README.md with script boilerplate ([1790071](https://github.com/dtomlinson91/panaetius/commit/1790071741207de13330ba75d7bf090106290d72))
- Update README.md with script quickstart logging ([e6cfded](https://github.com/dtomlinson91/panaetius/commit/e6cfded87dcfc5d2bf62d36bc7b4dbbdeb94b0b8))
### 🧪 Testing
- Add tests for f5ea19e ([156af46](https://github.com/dtomlinson91/panaetius/commit/156af4685510bac97a850b83d63f8337635db199))
### 🧱 Build
- Update dependencies ([9bc89fd](https://github.com/dtomlinson91/panaetius/commit/9bc89fd2ce9ddf8dcd6a3ca84ef9b72ee183efd3))

View File

@@ -78,6 +78,39 @@ import tembo.cli
tembo.cli.CONFIG tembo.cli.CONFIG
``` ```
### Script
Create `./config/config.yml` in the same directory as the script.
In the script initialise a `CONFIG` object:
```python
import pathlib
import panaetius
CONFIG = panaetius.Config(
"teenagers_scraper", str(pathlib.Path(__file__).parents[0] / ".config"), skip_header_init=True
)
```
Set variables in the same way as the module above.
#### quickstart logging
```python
import panaetius
def get_logger():
logging_dir = pathlib.Path(__file__).parents[0] / "logs"
logging_dir.mkdir(parents=True, exist_ok=True)
CONFIG = panaetius.Config("training_data_into_gcp", skip_header_init=True)
panaetius.set_config(CONFIG, "logging.level", "DEBUG")
panaetius.set_config(CONFIG, "logging.path", logging_dir)
return panaetius.set_logger(CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level))
```
## Utility Functions ## Utility Functions

58
cliff.toml Normal file
View File

@@ -0,0 +1,58 @@
# configuration file for git-cliff (0.1.0)
[changelog]
# changelog header
header = """
# Changelog
All notable changes to this project will be documented in this file.\n
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n
"""
# template for the changelog body
# https://tera.netlify.app/docs/#introduction
body = """
{% if version %}\
## [{{ version }}](https://github.com/dtomlinson91/panaetius/commits/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{% if previous.version %}\
<small>[Compare with {{ previous.version }}](https://github.com/dtomlinson91/panaetius/compare/{{ previous.version }}...{{ version }})</small>
{% endif %}\
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}](https://github.com/dtomlinson91/panaetius/commit/{{ commit.id }}))\
{% endfor %}
{% endfor %}\n
"""
# remove the leading and trailing whitespaces from the template
trim = true
# changelog footer
footer = """
"""
[git]
# allow only conventional commits
# https://www.conventionalcommits.org
conventional_commits = true
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "✨ Features"},
{ message = "^fix", group = "🐛 Bug Fixes"},
{ message = "^doc", group = "📘 Documentation"},
{ message = "^perf", group = "🏎 Performance"},
{ message = "^refactor", group = "🛠 Refactor/Improvement"},
{ message = "^style", group = "🎨 Styling"},
{ message = "^test", group = "🧪 Testing"},
{ message = "^build", group = "🧱 Build"},
{ body = ".*security", group = "🔐 Security"},
{ message = "^chore\\(release\\): prepare for", skip = true},
{ message = "^chore", group = "🥱 Miscellaneous Tasks", skip = true},
]
# filter out the commits that are not matched by commit parsers
filter_commits = false
# glob pattern for matching git tags
tag_pattern = "v[0-9]*"
# regex for skipping tags
skip_tags = "v0.1.0-beta.1"

View File

@@ -7,13 +7,16 @@ import re
import shutil import shutil
import sys import sys
from io import StringIO from io import StringIO
from typing import List, Optional, Pattern
from urllib.request import urlopen
from duty import duty from duty import duty
PACKAGE_NAME = "panaetius" PACKAGE_NAME = "panaetius"
REPO_URL = "https://github.com/dtomlinson91/panaetius"
@duty @duty(post=["export"])
def update_deps(ctx, dry: bool = False): def update_deps(ctx, dry: bool = False):
""" """
Update the dependencies using Poetry. Update the dependencies using Poetry.
@@ -64,22 +67,23 @@ def coverage(ctx):
@duty @duty
def version(ctx, bump: str = "patch"): def bump(ctx, version: str = "patch"):
""" """
Bump the version using Poetry and update _version.py. Bump the version using Poetry and update _version.py.
This duty is ran as part of `duty release`.
Args: Args:
ctx: The context instance (passed automatically). ctx: The context instance (passed automatically).
bump (str, optional) = poetry version flag. Available options are: version (str, optional) = poetry version flag. Available options are:
patch, minor, major, prepatch, preminor, premajor, prerelease. patch, minor, major. Defaults to patch.
Defaults to patch.
Example: Example:
`duty version bump=major` `duty bump version=major`
""" """
# bump with poetry # bump with poetry
result = ctx.run(["poetry", "version", bump]) result = ctx.run(["poetry", "version", version])
new_version = re.search(r"(?:.*)(?:\s)(\d+\.\d+\.\d+)$", result) new_version = re.search(r"(?:.*)(?:\s)(\d+\.\d+\.\d+)$", result)
print(new_version.group(0)) print(new_version.group(0))
@@ -124,6 +128,26 @@ def build(ctx):
shutil.rmtree(extracted_path) shutil.rmtree(extracted_path)
@duty
def release(ctx, version: str = "patch") -> None:
"""
Prepare package for a new release.
Will run bump, build, export. Manual running of publish is required afterwards.
Args:
ctx: The context instance (passed automatically).
version (str): poetry version flag. Available options are: patch, minor, major.
"""
print(ctx.run(["duty", "bump", f"version={version}"]))
ctx.run(["duty", "build"])
ctx.run(["duty", "export"])
print(
"✔ Check generated files. Run `duty changelog planned_release= previous_release=` and `duty publish password=`"
" when ready to publish."
)
@duty @duty
def export(ctx): def export(ctx):
""" """
@@ -306,6 +330,38 @@ def check_dependencies(ctx):
) )
@duty
def changelog(ctx, planned_release: Optional[str] = None, previous_release: Optional[str] = None):
"""
Generate a changelog with git-cliff.
Args:
ctx: The context instance (passed automatically).
planned_release (str, optional): The planned release version. Example: v1.0.2
previous_release (str, optional): The previous release version. Example: v1.0.1
"""
generated_changelog: str = ctx.run(["git", "cliff", "-u", "-t", planned_release, "-s", "header"])[:-1]
if previous_release is not None:
generated_changelog: list = generated_changelog.splitlines()
generated_changelog.insert(
1,
f"<small>[Compare with {previous_release}]({REPO_URL}/compare/{previous_release}...{planned_release})</small>",
)
generated_changelog: str = "\n".join([line for line in generated_changelog]) + "\n"
new_changelog = []
changelog_file = pathlib.Path(".") / "CHANGELOG.md"
with changelog_file.open("r", encoding="utf-8") as changelog_contents:
all_lines = changelog_contents.readlines()
for line_string in all_lines:
regex_string = re.search(r"(<!-- marker -->)", line_string)
new_changelog.append(line_string)
if isinstance(regex_string, re.Match):
new_changelog.append(generated_changelog)
with changelog_file.open("w", encoding="utf-8") as changelog_contents:
changelog_contents.writelines(new_changelog)
def rm_tree(directory: pathlib.Path): def rm_tree(directory: pathlib.Path):
""" """
Recursively delete a directory and all its contents. Recursively delete a directory and all its contents.

View File

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

View File

@@ -147,15 +147,16 @@ class Config:
if value is not None: if value is not None:
return self.__get_config_value_env_var_override(value) return self.__get_config_value_env_var_override(value)
if len(key.split(".")) > 2: if len(key.split(".")) > 3:
raise KeyErrorTooDeepException( raise KeyErrorTooDeepException(
f"Your key of {key} can only be 2 levels deep maximum. " f"Your key of {key} can only be 3 levels deep maximum."
f"You have {len(key.split('.'))}"
) )
if len(key.split(".")) == 1: if len(key.split(".")) == 1:
return self.__get_config_value_key_split_once(key) return self.__get_config_value_key_split_once(key)
if len(key.split(".")) == 2: if len(key.split(".")) == 2:
return self.__get_config_value_key_split_twice(key) return self.__get_config_value_key_split_twice(key)
if len(key.split(".")) == 3:
return self.__get_config_value_key_split_thrice(key)
raise KeyError() raise KeyError()
except (KeyError, TypeError): except (KeyError, TypeError):
@@ -172,6 +173,10 @@ class Config:
section, name = key.lower().split(".") section, name = key.lower().split(".")
return self.config[self.header_variable][section][name] return self.config[self.header_variable][section][name]
def __get_config_value_key_split_thrice(self, key: str) -> Any:
section, name_0, name_1 = key.lower().split(".")
return self.config[self.header_variable][section][name_0][name_1]
def __get_config_value_missing_key_value_is_none(self, default: Any) -> Any: def __get_config_value_missing_key_value_is_none(self, default: Any) -> Any:
return self.__load_default_value(default) return self.__load_default_value(default)

530
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "panaetius" name = "panaetius"
version = "2.3.3" version = "2.3.5"
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." 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" license = "MIT"
authors = ["dtomlinson <dtomlinson@panaetius.co.uk>"] authors = ["dtomlinson <dtomlinson@panaetius.co.uk>"]
@@ -24,18 +24,16 @@ classifiers = [
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.7" python = "^3.7"
toml = "^0.10.0" PyYAML = "*"
PyYAML = "^6.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
prospector = {extras = ["with_bandit", "with_mypy"], version = "^1.5.1"} prospector = {extras = ["with_bandit", "with_mypy"], version = "^1.5.1"}
types-toml = "^0.10.1"
pytest = "^6.2.5" pytest = "^6.2.5"
pytest-datadir = "^1.3.1" pytest-datadir = "^1.3.1"
pytest-xdist = "^2.4.0" pytest-xdist = "^2.4.0"
coverage = "^6.0.2" coverage = "^6.0.2"
duty = "^0.7.0" duty = "^0.7.0"
types-PyYAML = "^6.0.1" types-PyYAML = "*"
isort = "^5.10.1" isort = "^5.10.1"
mypy = "^0.910" mypy = "^0.910"
safety = "^1.10.3" safety = "^1.10.3"

View File

@@ -1,2 +1 @@
pyyaml==6.0; python_version >= "3.6" pyyaml==6.0; python_version >= "3.6"
toml==0.10.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")

View File

@@ -1,61 +1,67 @@
ansimarkup==1.5.0; python_version >= "3.6" ansimarkup==1.5.0; python_version >= "3.6"
astroid==2.8.2; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1" astroid==2.9.3; python_full_version >= "3.6.2" and python_version < "4.0"
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
attrs==21.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" attrs==21.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
bandit==1.7.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.5" bandit==1.7.2; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.7"
cached-property==1.5.2; python_version < "3.8" and python_version >= "3.6" cached-property==1.5.2; python_version < "3.8" and python_version >= "3.6"
colorama==0.4.4; sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.6.1" and python_version < "4.0" and platform_system == "Windows" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") certifi==2021.10.8; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.5"
coverage==6.0.2; python_version >= "3.6" charset-normalizer==2.0.10; python_full_version >= "3.6.0" and python_version >= "3.5"
dodgy==0.2.1; python_full_version >= "3.6.1" and python_version < "4.0" click==8.0.3; python_version >= "3.6"
colorama==0.4.4; sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.6.2" and python_version < "4.0" and platform_system == "Windows" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
coverage==6.3; python_version >= "3.7"
dodgy==0.2.1; python_full_version >= "3.6.2" and python_version < "4.0"
dparse==0.5.1; python_version >= "3.5"
duty==0.7.0; python_version >= "3.6" duty==0.7.0; python_version >= "3.6"
execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
failprint==0.8.0; python_version >= "3.6" failprint==0.8.0; python_version >= "3.6"
flake8-polyfill==1.0.2; python_full_version >= "3.6.1" and python_version < "4.0" flake8-polyfill==1.0.2; python_full_version >= "3.6.2" and python_version < "4.0"
flake8==2.3.0; python_full_version >= "3.6.1" and python_version < "4.0" flake8==2.3.0; python_full_version >= "3.6.2" and python_version < "4.0"
gitdb==4.0.7; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.7" gitdb==4.0.9; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.7"
gitpython==3.1.24; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.7" gitpython==3.1.26; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.7"
importlib-metadata==4.8.1; python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") and python_full_version >= "3.6.1" idna==3.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.5"
importlib-metadata==4.10.1; python_version < "3.8" and python_version >= "3.7" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") and python_full_version >= "3.6.2"
iniconfig==1.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" iniconfig==1.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
isort==5.9.3; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6" isort==5.10.1; python_full_version >= "3.6.1" and python_version < "4.0"
jinja2==3.0.3; python_version >= "3.6" jinja2==3.0.3; python_version >= "3.6"
lazy-object-proxy==1.6.0; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1" lazy-object-proxy==1.7.1; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.6"
markupsafe==2.0.1; python_version >= "3.6" markupsafe==2.0.1; python_version >= "3.6"
mccabe==0.6.1; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6" mccabe==0.6.1; python_full_version >= "3.6.2" and python_version < "4.0"
mypy-extensions==0.4.3; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.5" mypy-extensions==0.4.3; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.5"
mypy==0.910; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.5" mypy==0.910; python_version >= "3.5"
packaging==21.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" packaging==21.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pbr==5.6.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6" pbr==5.8.0; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.7"
pep8-naming==0.10.0; python_full_version >= "3.6.1" and python_version < "4.0" pep8-naming==0.10.0; python_full_version >= "3.6.2" and python_version < "4.0"
pep8==1.7.1; python_full_version >= "3.6.1" and python_version < "4.0" pep8==1.7.1; python_full_version >= "3.6.2" and python_version < "4.0"
platformdirs==2.4.0; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1" platformdirs==2.4.1; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.7"
pluggy==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" pluggy==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
prospector==1.5.1; python_full_version >= "3.6.1" and python_version < "4.0" prospector==1.6.0; python_full_version >= "3.6.2" and python_version < "4.0"
ptyprocess==0.7.0; sys_platform != "win32" and python_version >= "3.6" ptyprocess==0.7.0; sys_platform != "win32" and python_version >= "3.6"
py==1.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" py==1.11.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pycodestyle==2.8.0; python_full_version >= "3.6.1" and python_version < "4.0" pycodestyle==2.8.0; python_full_version >= "3.6.2" and python_version < "4.0"
pydocstyle==6.1.1; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6" pydocstyle==6.1.1; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.6"
pyflakes==2.3.1; python_full_version >= "3.6.1" and python_version < "4.0" pyflakes==2.3.1; python_full_version >= "3.6.2" and python_version < "4.0"
pylint-celery==0.3; python_full_version >= "3.6.1" and python_version < "4.0" pylint-celery==0.3; python_full_version >= "3.6.2" and python_version < "4.0"
pylint-django==2.4.4; python_full_version >= "3.6.1" and python_version < "4.0" pylint-django==2.5.0; python_full_version >= "3.6.2" and python_version < "4.0"
pylint-flask==0.6; python_full_version >= "3.6.1" and python_version < "4.0" pylint-flask==0.6; python_full_version >= "3.6.2" and python_version < "4.0"
pylint-plugin-utils==0.6; python_full_version >= "3.6.1" and python_version < "4.0" pylint-plugin-utils==0.7; python_full_version >= "3.6.2" and python_version < "4.0"
pylint==2.11.1; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1" pylint==2.12.2; python_full_version >= "3.6.2" and python_version < "4.0"
pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" pyparsing==3.0.7; python_version >= "3.6"
pytest-datadir==1.3.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") pytest-datadir==1.3.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
pytest-forked==1.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" pytest-forked==1.4.0; python_version >= "3.6"
pytest-xdist==2.4.0; python_version >= "3.6" pytest-xdist==2.5.0; python_version >= "3.6"
pytest==6.2.5; python_version >= "3.6" pytest==6.2.5; python_version >= "3.6"
pyyaml==6.0; python_version >= "3.6" pyyaml==6.0; python_version >= "3.6"
requirements-detector==0.7; python_full_version >= "3.6.1" and python_version < "4.0" requests==2.27.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.5"
setoptconf-tmp==0.3.1; python_full_version >= "3.6.1" and python_version < "4.0" requirements-detector==0.7; python_full_version >= "3.6.2" and python_version < "4.0"
six==1.16.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.5" safety==1.10.3; python_version >= "3.5"
smmap==4.0.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.7" setoptconf-tmp==0.3.1; python_full_version >= "3.6.2" and python_version < "4.0"
snowballstemmer==2.1.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6" smmap==5.0.0; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.7"
stevedore==3.4.0; python_full_version >= "3.6.1" and python_version < "4.0" and python_version >= "3.6" snowballstemmer==2.2.0; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.6"
toml==0.10.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") stevedore==3.5.0; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.7"
typed-ast==1.4.3; python_full_version >= "3.6.1" and python_version < "3.8" and python_version >= "3.6" and implementation_name == "cpython" toml==0.10.2; python_full_version >= "3.6.2" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and python_version >= "3.5" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5")
types-pyyaml==6.0.1 typed-ast==1.4.3; python_version < "3.8" and python_version >= "3.5" and python_full_version >= "3.6.2" and implementation_name == "cpython"
types-toml==0.10.1 types-pyyaml==6.0.3
typing-extensions==3.10.0.2; python_full_version >= "3.6.1" and python_version < "3.8" and python_version >= "3.7" typing-extensions==4.0.1; python_full_version >= "3.6.2" and python_version < "3.8" and python_version >= "3.7"
wrapt==1.12.1; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.1" urllib3==1.26.8; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.5"
zipp==3.6.0; python_version < "3.8" and python_version >= "3.6" wrapt==1.13.3; python_full_version >= "3.6.2" and python_version < "4.0"
zipp==3.7.0; python_version < "3.8" and python_version >= "3.7"

View File

@@ -8,13 +8,13 @@ package_data = \
{'': ['*']} {'': ['*']}
install_requires = \ install_requires = \
['PyYAML>=6.0,<7.0', 'toml>=0.10.0,<0.11.0'] ['PyYAML']
setup_kwargs = { setup_kwargs = {
'name': 'panaetius', 'name': 'panaetius',
'version': '2.3.3', 'version': '2.3.5',
'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.', 'description': 'Python module to gracefully handle a .config file/environment variables for scripts, with built in masking for sensitive options. Provides a Splunk friendly formatted logger instance.',
'long_description': '# Panaetius\n\nThis package provides:\n\n- Functionality to read user variables from a `config.yml` or environment variables.\n- A convenient default logging formatter printing `json` that can save to disk and rotate.\n- Utility functions.\n\n## Config\n\n### options\n\n#### skip_header_init\n\nIf `skip_header_init=True` then the `config_path` will not use the `header_variable` as the\nsub-directory in the `config_path`.\n\nE.g\n\n`CONFIG = panaetius.Config("tembo", "~/tembo/.config", skip_header_init=True)`\n\nWill look in `~/tembo/config/config.yml`.\n\nIf `skip_header_init=False` then would look in `~/tembo/config/tembo/config.yml`.\n\n### Module\n\nConvenient to place in a package/sub-package `__init__.py`.\n\nSee Tembo for an example: <https://github.com/tembo-pages/tembo-core/blob/main/tembo/cli/__init__.py>\n\nExample snippet to use in a module:\n\n```python\n"""Subpackage that contains the CLI application."""\n\nimport os\nfrom typing import Any\n\nimport panaetius\nfrom panaetius.exceptions import LoggingDirectoryDoesNotExistException\n\n\nif (config_path := os.environ.get("TEMBO_CONFIG")) is not None:\n CONFIG: Any = panaetius.Config("tembo", config_path, skip_header_init=True)\nelse:\n CONFIG = panaetius.Config(\n "tembo", "~/tembo/.config", skip_header_init=True\n )\n\n\npanaetius.set_config(CONFIG, "base_path", "~/tembo")\npanaetius.set_config(CONFIG, "template_path", "~/tembo/.templates")\npanaetius.set_config(CONFIG, "scopes", {})\npanaetius.set_config(CONFIG, "logging.level", "DEBUG")\npanaetius.set_config(CONFIG, "logging.path")\n\ntry:\n logger = panaetius.set_logger(\n CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level)\n )\nexcept LoggingDirectoryDoesNotExistException:\n _LOGGING_PATH = CONFIG.logging_path\n CONFIG.logging_path = ""\n logger = panaetius.set_logger(\n CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level)\n )\n logger.warning("Logging directory %s does not exist", _LOGGING_PATH)\n\n```\n\nThis means in `./tembo/cli/cli.py` you can\n\n```python\nimport tembo.cli\n\n# access the CONFIG instance + variables from the config.yml\ntembo.cli.CONFIG\n```\n\n\n## Utility Functions\n\n### Squasher\n\nSquashes a json object or Python dictionary into a single level dictionary.\n', 'long_description': '# Panaetius\n\nThis package provides:\n\n- Functionality to read user variables from a `config.yml` or environment variables.\n- A convenient default logging formatter printing `json` that can save to disk and rotate.\n- Utility functions.\n\n## Config\n\n### options\n\n#### skip_header_init\n\nIf `skip_header_init=True` then the `config_path` will not use the `header_variable` as the\nsub-directory in the `config_path`.\n\nE.g\n\n`CONFIG = panaetius.Config("tembo", "~/tembo/.config", skip_header_init=True)`\n\nWill look in `~/tembo/config/config.yml`.\n\nIf `skip_header_init=False` then would look in `~/tembo/config/tembo/config.yml`.\n\n### Module\n\nConvenient to place in a package/sub-package `__init__.py`.\n\nSee Tembo for an example: <https://github.com/tembo-pages/tembo-core/blob/main/tembo/cli/__init__.py>\n\nExample snippet to use in a module:\n\n```python\n"""Subpackage that contains the CLI application."""\n\nimport os\nfrom typing import Any\n\nimport panaetius\nfrom panaetius.exceptions import LoggingDirectoryDoesNotExistException\n\n\nif (config_path := os.environ.get("TEMBO_CONFIG")) is not None:\n CONFIG: Any = panaetius.Config("tembo", config_path, skip_header_init=True)\nelse:\n CONFIG = panaetius.Config(\n "tembo", "~/tembo/.config", skip_header_init=True\n )\n\n\npanaetius.set_config(CONFIG, "base_path", "~/tembo")\npanaetius.set_config(CONFIG, "template_path", "~/tembo/.templates")\npanaetius.set_config(CONFIG, "scopes", {})\npanaetius.set_config(CONFIG, "logging.level", "DEBUG")\npanaetius.set_config(CONFIG, "logging.path")\n\ntry:\n logger = panaetius.set_logger(\n CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level)\n )\nexcept LoggingDirectoryDoesNotExistException:\n _LOGGING_PATH = CONFIG.logging_path\n CONFIG.logging_path = ""\n logger = panaetius.set_logger(\n CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level)\n )\n logger.warning("Logging directory %s does not exist", _LOGGING_PATH)\n\n```\n\nThis means in `./tembo/cli/cli.py` you can\n\n```python\nimport tembo.cli\n\n# access the CONFIG instance + variables from the config.yml\ntembo.cli.CONFIG\n```\n\n### Script\n\nCreate `./config/config.yml` in the same directory as the script.\n\nIn the script initialise a `CONFIG` object:\n\n```python\nimport pathlib\n\nimport panaetius\n\nCONFIG = panaetius.Config(\n "teenagers_scraper", str(pathlib.Path(__file__).parents[0] / ".config"), skip_header_init=True\n)\n```\n\nSet variables in the same way as the module above.\n\n#### quickstart logging\n\n```python\nimport panaetius\n\n\ndef get_logger():\n logging_dir = pathlib.Path(__file__).parents[0] / "logs"\n logging_dir.mkdir(parents=True, exist_ok=True)\n\n CONFIG = panaetius.Config("training_data_into_gcp", skip_header_init=True)\n panaetius.set_config(CONFIG, "logging.level", "DEBUG")\n panaetius.set_config(CONFIG, "logging.path", logging_dir)\n return panaetius.set_logger(CONFIG, panaetius.SimpleLogger(logging_level=CONFIG.logging_level))\n```\n\n## Utility Functions\n\n### Squasher\n\nSquashes a json object or Python dictionary into a single level dictionary.\n',
'author': 'dtomlinson', 'author': 'dtomlinson',
'author_email': 'dtomlinson@panaetius.co.uk', 'author_email': 'dtomlinson@panaetius.co.uk',
'maintainer': None, 'maintainer': None,

View File

@@ -18,6 +18,9 @@ def testing_config_contents():
"some_second_list": ["some", "second", "value"], "some_second_list": ["some", "second", "value"],
"some_second_table": {"first": ["some", "first", "value"]}, "some_second_table": {"first": ["some", "first", "value"]},
"some_second_table_bools": {"bool": [True, False]}, "some_second_table_bools": {"bool": [True, False]},
"third": {
"some_third_string": "some_third_value",
},
}, },
} }
} }

View File

@@ -7,3 +7,5 @@
some_second_list: ["some", "second", "value"] some_second_list: ["some", "second", "value"]
some_second_table: { "first": ["some", "first", "value"] } some_second_table: { "first": ["some", "first", "value"] }
some_second_table_bools: { "bool": [true, false] } some_second_table_bools: { "bool": [true, false] }
third:
some_third_string: some_third_value

View File

@@ -67,9 +67,7 @@ def test_config_file_without_header_dir_exists(header, shared_datadir):
assert config._missing_config is False assert config._missing_config is False
def test_config_file_contents_read_success( def test_config_file_contents_read_success(header, shared_datadir, testing_config_contents):
header, shared_datadir, testing_config_contents
):
# arrange # arrange
config_path = str(shared_datadir / "without_logging") config_path = str(shared_datadir / "without_logging")
@@ -101,11 +99,10 @@ def test_config_file_contents_read_success(
"second_some_second_table_bools", "second_some_second_table_bools",
{"bool": [True, False]}, {"bool": [True, False]},
), ),
("second.third.some_third_string", "second_third_some_third_string", "some_third_value"),
], ],
) )
def test_get_value_from_key( def test_get_value_from_key(set_config_key, get_config_key, expected_value, header, shared_datadir):
set_config_key, get_config_key, expected_value, header, shared_datadir
):
""" """
Test the following: Test the following:
@@ -155,11 +152,7 @@ def test_key_level_too_deep(header, shared_datadir):
panaetius.set_config(config, key) panaetius.set_config(config, key)
# assert # assert
assert ( assert str(key_error_too_deep.value) == f"Your key of {key} can only be 3 levels deep maximum."
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): def test_get_value_missing_key_from_default(header, shared_datadir):
@@ -242,9 +235,7 @@ def test_missing_config_read_from_default(header, shared_datadir):
), ),
], ],
) )
def test_missing_config_read_from_env_var( def test_missing_config_read_from_env_var(env_value, expected_value, header, shared_datadir):
env_value, expected_value, header, shared_datadir
):
# arrange # arrange
config_path = str(shared_datadir / str(uuid4())) config_path = str(shared_datadir / str(uuid4()))
os.environ[f"{header.upper()}_MISSING_KEY_READ_FROM_ENV_VAR"] = env_value os.environ[f"{header.upper()}_MISSING_KEY_READ_FROM_ENV_VAR"] = env_value
@@ -271,10 +262,7 @@ def test_missing_config_read_from_env_var_invalid_python(header):
panaetius.set_config(config, "invalid_python") panaetius.set_config(config, "invalid_python")
# assert # assert
assert ( assert str(invalid_python_exception.value) == "a string without quotes is not valid Python."
str(invalid_python_exception.value)
== "a string without quotes is not valid Python."
)
# cleanup # cleanup
del os.environ[f"{header.upper()}_INVALID_PYTHON"] del os.environ[f"{header.upper()}_INVALID_PYTHON"]