Skip to content
Snippets Groups Projects
Commit 61d9ef44 authored by David Huss's avatar David Huss :speech_balloon:
Browse files

Use ConfigDict for attribute access, switch to uv

parent 77b7236c
Branches
No related tags found
No related merge requests found
......@@ -4,16 +4,16 @@ This is a common configuration library for python based services
Add this dependency to your application e.g. using poetry:
Add this dependency to your application e.g. using uv:
```bash
poetry add git+https://code.hfbk.net/id/eigenservice/common-config.git
uv add git+https://code.hfbk.net/id/eigenservice/common-config.git
```
When something changes update it like this:
When something changes update it in your used project like this:
```bash
poetry update common-config
uv lock --upgrade-package common-config
```
......@@ -21,8 +21,7 @@ poetry update common-config
Then in the application use something like this as a starting point:
```python
#!/usr/bin/env python
#-*- coding: utf-8 -*-
# ./config.py
import common_config
from common_config import initialize_config, read_config
......@@ -34,10 +33,12 @@ SUFFIX = "config"
# Do not change here, just use an override instead
DEFAULT_CONFIG = """
[application]
loglevel = "info"
level = 9001
mood = "very"
[database]
path = "/dev/null"
"""
# Override the global variables in the common-config.common module
......@@ -45,10 +46,24 @@ common_config.common.APPLICATION_NAME = APPLICATION_NAME
common_config.common.SUFFIX = SUFFIX
common_config.common.DEFAULT_CONFIG = DEFAULT_CONFIG
# Initialize the database (add logger if needed, otherwise this just prints)
config = initialize_database(logger=None)
if __name__ == "__main__":
common_config.main()
```
If people now run that file, they will get an interactive configuration-tool
If people now run that file (or `common_config.main()`, they will get an interactive configuration-tool that helps them creating the config in the XDG-standardized paths
Within your application you can use it as follows
```python
import config
print(f"This application is called {config.APPLICATION_NAME}, the mood is {config.mood}")
```
......@@ -16,6 +16,49 @@ SUFFIX = ""
DEFAULT_CONFIG = ""
class ConfigDict(dict):
"""A dictionary that allows attribute-style access."""
def __init__(self, dictionary=None):
if dictionary is None:
dictionary = {}
super().__init__(dictionary)
for key, value in dictionary.items():
if isinstance(value, dict):
value = ConfigDict(value) # Recursively convert nested dicts
super().__setitem__(key, value) # Ensure dict behavior
object.__setattr__(self, key, value) # Ensure attribute behavior
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(f"'ConfigDict' object has no attribute '{key}'")
def __setattr__(self, key, value):
if isinstance(value, dict):
value = ConfigDict(value) # Convert dict to ConfigDict
self[key] = value # Set dictionary key
object.__setattr__(self, key, value) # Set as attribute
def __delattr__(self, key):
try:
del self[key]
except KeyError:
raise AttributeError(f"'ConfigDict' object has no attribute '{key}'")
def __dir__(self):
return list(self.keys()) + super().__dir__()
def copy(self):
"""Override copy method to return an ConfigDict instance."""
return ConfigDict(super().copy())
def __repr__(self):
return f"ConfigDict({super().__repr__()})"
def this_or_else(this: Optional[str], other: str) -> str:
"""
Return this appended with the application name
......@@ -147,7 +190,7 @@ def initialize_config(logger=None, suffix=None) -> MutableMapping[str, Any]:
print(
"Using default configuration, create an override by running config create"
)
return config
return ConfigDict(config)
if logger is not None:
logger.info("Reading Configs in this order:")
......@@ -168,7 +211,7 @@ def initialize_config(logger=None, suffix=None) -> MutableMapping[str, Any]:
if logger is not None:
logger = set_loglevel(config, logger)
return config
return ConfigDict(config)
def read_config(config_path: str) -> MutableMapping[str, Any]:
......@@ -178,7 +221,7 @@ def read_config(config_path: str) -> MutableMapping[str, Any]:
"""
with open(str(config_path), "r", encoding="utf-8") as f:
config = toml.load(f)
return config
return ConfigDict(config)
def set_loglevel(config, logger: logging.Logger) -> logging.Logger:
......
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "atomicwrites"
version = "1.4.1"
description = "Atomic file writes."
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
]
[[package]]
name = "attrs"
version = "24.3.0"
description = "Classes Without Boilerplate"
optional = false
python-versions = ">=3.8"
files = [
{file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"},
{file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"},
]
[package.extras]
benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "more-itertools"
version = "10.5.0"
description = "More routines for operating on iterables, beyond itertools"
optional = false
python-versions = ">=3.8"
files = [
{file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"},
{file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"},
]
[[package]]
name = "packaging"
version = "24.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
]
[[package]]
name = "pluggy"
version = "0.13.1"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
]
[package.extras]
dev = ["pre-commit", "tox"]
[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
[[package]]
name = "pytest"
version = "5.4.3"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.5"
files = [
{file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
{file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
]
[package.dependencies]
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=17.4.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
more-itertools = ">=4.0.0"
packaging = "*"
pluggy = ">=0.12,<1.0"
py = ">=1.5.0"
wcwidth = "*"
[package.extras]
checkqa-mypy = ["mypy (==v0.761)"]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
[[package]]
name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
[[package]]
name = "wcwidth"
version = "0.2.13"
description = "Measures the displayed width of unicode strings in a terminal"
optional = false
python-versions = "*"
files = [
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "8d053f67784f83194a2fc92816ce53a2962947428d27e1ccbdcd7b1fcba469e2"
[tool.poetry]
[project]
name = "common-config"
version = "0.1.22"
version = "0.2.0"
description = "A config library for python based services"
authors = ["David Huss <dh@atoav.com>"]
authors = [{ name = "David Huss", email = "dh@atoav.com" }]
requires-python = "~=3.8"
dependencies = ["toml>=0.10.2,<0.11"]
[tool.poetry.dependencies]
python = "^3.8"
toml = "^0.10.2"
[tool.poetry.dev-dependencies]
pytest = "^5.2"
[dependency-groups]
dev = ["pytest~=5.2"]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
requires = ["hatchling"]
build-backend = "hatchling.build"
uv.lock 0 → 100644
version = 1
revision = 1
requires-python = ">=3.8, <4"
[[package]]
name = "atomicwrites"
version = "1.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/87/c6/53da25344e3e3a9c01095a89f16dbcda021c609ddb42dd6d7c0528236fb2/atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11", size = 14227 }
[[package]]
name = "attrs"
version = "24.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "common-config"
version = "0.2.0"
source = { editable = "." }
dependencies = [
{ name = "toml" },
]
[package.dev-dependencies]
dev = [
{ name = "pytest" },
]
[package.metadata]
requires-dist = [{ name = "toml", specifier = ">=0.10.2,<0.11" }]
[package.metadata.requires-dev]
dev = [{ name = "pytest", specifier = "~=5.2" }]
[[package]]
name = "more-itertools"
version = "10.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/51/78/65922308c4248e0eb08ebcbe67c95d48615cc6f27854b6f2e57143e9178f/more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6", size = 121020 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/48/7e/3a64597054a70f7c86eb0a7d4fc315b8c1ab932f64883a297bdffeb5f967/more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", size = 60952 },
]
[[package]]
name = "packaging"
version = "24.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
]
[[package]]
name = "pluggy"
version = "0.13.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/04/7a8542bed4b16a65c2714bf76cf5a0b026157da7f75e87cc88774aa10b14/pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", size = 57962 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d", size = 18077 },
]
[[package]]
name = "py"
version = "1.11.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/98/ff/fec109ceb715d2a6b4c4a85a61af3b40c723a961e8828319fbcb15b868dc/py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", size = 207796 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378", size = 98708 },
]
[[package]]
name = "pytest"
version = "5.4.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "atomicwrites", marker = "sys_platform == 'win32'" },
{ name = "attrs" },
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "more-itertools" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "py" },
{ name = "wcwidth" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8f/c4/e4a645f8a3d6c6993cb3934ee593e705947dfafad4ca5148b9a0fde7359c/pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8", size = 1022353 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9f/f3/0a83558da436a081344aa6c8b85ea5b5f05071214106036ce341b7769b0b/pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1", size = 248101 },
]
[[package]]
name = "toml"
version = "0.10.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 },
]
[[package]]
name = "wcwidth"
version = "0.2.13"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 },
]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment