configaroo 0.2.2__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {configaroo-0.2.2/src/configaroo.egg-info → configaroo-0.2.3}/PKG-INFO +10 -3
- configaroo-0.2.3/README.md +18 -0
- {configaroo-0.2.2 → configaroo-0.2.3}/pyproject.toml +42 -13
- {configaroo-0.2.2 → configaroo-0.2.3}/src/configaroo/__init__.py +6 -5
- {configaroo-0.2.2 → configaroo-0.2.3}/src/configaroo/configuration.py +80 -35
- configaroo-0.2.3/src/configaroo/exceptions.py +23 -0
- {configaroo-0.2.2 → configaroo-0.2.3}/src/configaroo/loaders/__init__.py +18 -6
- configaroo-0.2.3/src/configaroo/loaders/json.py +16 -0
- {configaroo-0.2.2 → configaroo-0.2.3}/src/configaroo/loaders/toml.py +3 -3
- {configaroo-0.2.2 → configaroo-0.2.3/src/configaroo.egg-info}/PKG-INFO +10 -3
- {configaroo-0.2.2 → configaroo-0.2.3}/src/configaroo.egg-info/SOURCES.txt +1 -0
- configaroo-0.2.3/src/configaroo.egg-info/requires.txt +2 -0
- {configaroo-0.2.2 → configaroo-0.2.3}/tests/test_configuration.py +40 -38
- {configaroo-0.2.2 → configaroo-0.2.3}/tests/test_dynamic.py +17 -15
- {configaroo-0.2.2 → configaroo-0.2.3}/tests/test_environment.py +16 -14
- {configaroo-0.2.2 → configaroo-0.2.3}/tests/test_json.py +14 -13
- configaroo-0.2.3/tests/test_loaders.py +45 -0
- configaroo-0.2.3/tests/test_print.py +56 -0
- {configaroo-0.2.2 → configaroo-0.2.3}/tests/test_toml.py +14 -13
- configaroo-0.2.3/tests/test_validation.py +70 -0
- configaroo-0.2.2/README.md +0 -10
- configaroo-0.2.2/src/configaroo/exceptions.py +0 -13
- configaroo-0.2.2/src/configaroo/loaders/json.py +0 -13
- configaroo-0.2.2/src/configaroo.egg-info/requires.txt +0 -2
- configaroo-0.2.2/tests/test_loaders.py +0 -38
- configaroo-0.2.2/tests/test_validation.py +0 -61
- {configaroo-0.2.2 → configaroo-0.2.3}/LICENSE +0 -0
- {configaroo-0.2.2 → configaroo-0.2.3}/setup.cfg +0 -0
- {configaroo-0.2.2 → configaroo-0.2.3}/src/configaroo/py.typed +0 -0
- {configaroo-0.2.2 → configaroo-0.2.3}/src/configaroo.egg-info/dependency_links.txt +0 -0
- {configaroo-0.2.2 → configaroo-0.2.3}/src/configaroo.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: configaroo
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.3
|
4
4
|
Summary: Bouncy handling of configuration files
|
5
5
|
Author-email: Geir Arne Hjelle <geirarne@gmail.com>
|
6
6
|
Maintainer-email: Geir Arne Hjelle <geirarne@gmail.com>
|
@@ -17,7 +17,6 @@ Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
20
|
-
Classifier: Programming Language :: Python :: 3.14
|
21
20
|
Classifier: Programming Language :: Python :: 3
|
22
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
23
22
|
Classifier: Typing :: Typed
|
@@ -25,11 +24,19 @@ Requires-Python: >=3.11
|
|
25
24
|
Description-Content-Type: text/markdown
|
26
25
|
License-File: LICENSE
|
27
26
|
Requires-Dist: pydantic>=2.0
|
28
|
-
Requires-Dist: pyplugs>=0.4
|
27
|
+
Requires-Dist: pyplugs>=0.5.4
|
29
28
|
Dynamic: license-file
|
30
29
|
|
31
30
|
# Configaroo - Bouncy Configuration Handling
|
32
31
|
|
32
|
+
[](https://pypi.org/project/configaroo/)
|
33
|
+
[](https://pypi.org/project/configaroo/)
|
34
|
+
[](https://github.com/gahjelle/configaroo/blob/main/LICENSE)
|
35
|
+
[](https://github.com/astral-sh/ruff)
|
36
|
+
[](https://github.com/gahjelle/configaroo/actions/workflows/lint.yml)
|
37
|
+
[](https://github.com/gahjelle/configaroo/actions/workflows/test.yml)
|
38
|
+
[](http://mypy-lang.org/)
|
39
|
+
|
33
40
|
Configaroo is a light configuration package for Python that offers the following features:
|
34
41
|
|
35
42
|
- Access configuration settings with dotted keys: `config.nested.key`
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Configaroo - Bouncy Configuration Handling
|
2
|
+
|
3
|
+
[](https://pypi.org/project/configaroo/)
|
4
|
+
[](https://pypi.org/project/configaroo/)
|
5
|
+
[](https://github.com/gahjelle/configaroo/blob/main/LICENSE)
|
6
|
+
[](https://github.com/astral-sh/ruff)
|
7
|
+
[](https://github.com/gahjelle/configaroo/actions/workflows/lint.yml)
|
8
|
+
[](https://github.com/gahjelle/configaroo/actions/workflows/test.yml)
|
9
|
+
[](http://mypy-lang.org/)
|
10
|
+
|
11
|
+
Configaroo is a light configuration package for Python that offers the following features:
|
12
|
+
|
13
|
+
- Access configuration settings with dotted keys: `config.nested.key`
|
14
|
+
- Use different configuration file formats, including TOML and JSON
|
15
|
+
- Override key configuration settings with environment variables
|
16
|
+
- Validate a configuration based on a Pydantic model
|
17
|
+
- Convert the type of configuration values based on a Pydantic model
|
18
|
+
- Dynamically format certain configuration values
|
@@ -1,5 +1,5 @@
|
|
1
1
|
[build-system]
|
2
|
-
requires
|
2
|
+
requires = ["setuptools>=61"]
|
3
3
|
build-backend = "setuptools.build_meta"
|
4
4
|
|
5
5
|
|
@@ -21,18 +21,17 @@ classifiers = [
|
|
21
21
|
"Programming Language :: Python :: 3.11",
|
22
22
|
"Programming Language :: Python :: 3.12",
|
23
23
|
"Programming Language :: Python :: 3.13",
|
24
|
-
"Programming Language :: Python :: 3.14",
|
25
24
|
"Programming Language :: Python :: 3",
|
26
25
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
27
26
|
"Typing :: Typed",
|
28
27
|
]
|
29
|
-
dependencies = ["pydantic>=2.0", "pyplugs>=0.4
|
28
|
+
dependencies = ["pydantic>=2.0", "pyplugs>=0.5.4"]
|
30
29
|
dynamic = ["version"]
|
31
30
|
|
32
31
|
[project.urls]
|
33
|
-
homepage
|
34
|
-
github
|
35
|
-
issues
|
32
|
+
homepage = "https://github.com/gahjelle/configaroo"
|
33
|
+
github = "https://github.com/gahjelle/configaroo"
|
34
|
+
issues = "https://github.com/gahjelle/configaroo/issues"
|
36
35
|
changelog = "https://github.com/gahjelle/configaroo/blob/main/CHANGELOG.md"
|
37
36
|
|
38
37
|
[dependency-groups]
|
@@ -40,8 +39,10 @@ build = ["build>=1.2.2.post1", "twine>=6.1.0"]
|
|
40
39
|
dev = [
|
41
40
|
"bumpver>=2024.1130",
|
42
41
|
"ipython>=8.36.0",
|
42
|
+
"mypy>=1.17.1",
|
43
43
|
"pre-commit>=4.2.0",
|
44
44
|
"pytest>=8.3.5",
|
45
|
+
"rich>=14.1.0",
|
45
46
|
"ruff>=0.11.11",
|
46
47
|
"tomli-w>=1.2.0",
|
47
48
|
]
|
@@ -50,15 +51,43 @@ dev = [
|
|
50
51
|
[tool.setuptools.dynamic]
|
51
52
|
version = { attr = "configaroo.__version__" }
|
52
53
|
|
54
|
+
|
55
|
+
[tool.ruff]
|
56
|
+
target-version = "py311"
|
57
|
+
exclude = [".git", "venv", "migrations", "node_modules"]
|
58
|
+
|
59
|
+
[tool.ruff.lint]
|
60
|
+
select = ["ALL"]
|
61
|
+
ignore = [
|
62
|
+
"COM812", # Trailing comma missing (handled by formatter)
|
63
|
+
"D203", # Force blank line before class docstring (incompatible with D211)
|
64
|
+
"D213", # Start docstring on second line (incompatible with D212)
|
65
|
+
]
|
66
|
+
|
67
|
+
[tool.ruff.lint.per-file-ignores]
|
68
|
+
# D101: Missing docstring in public class (Basemodel classes may break this)
|
69
|
+
# PLR2004: Magic value used in comparison (OK for tests)
|
70
|
+
# S101: Use of `assert` (OK for tests)
|
71
|
+
# SLF001: Private member accessed (OK for tests)
|
72
|
+
# UP018: Replace with string/integer/... literal (OK for typed tests)
|
73
|
+
"examples/*/config.py" = ["D101"]
|
74
|
+
"tests/schema.py" = ["D101"]
|
75
|
+
"tests/test_*.py" = ["PLR2004", "S101", "SLF001", "UP018"]
|
76
|
+
"tests/*/test_*.py" = ["S101"]
|
77
|
+
|
78
|
+
[tool.mypy]
|
79
|
+
python_version = "3.11"
|
80
|
+
strict = true
|
81
|
+
|
53
82
|
[tool.bumpver]
|
54
|
-
current_version = "v0.2.
|
83
|
+
current_version = "v0.2.3"
|
55
84
|
version_pattern = "vMAJOR.MINOR.PATCH"
|
56
|
-
commit_message
|
57
|
-
tag_message
|
58
|
-
commit
|
59
|
-
tag
|
60
|
-
push
|
85
|
+
commit_message = "bump version {old_version} -> {new_version}"
|
86
|
+
tag_message = "{new_version}"
|
87
|
+
commit = true
|
88
|
+
tag = true
|
89
|
+
push = true
|
61
90
|
|
62
91
|
[tool.bumpver.file_patterns]
|
63
|
-
"pyproject.toml"
|
92
|
+
"pyproject.toml" = ['current_version = "{version}"']
|
64
93
|
"src/configaroo/__init__.py" = ['__version__ = "MAJOR.MINOR.PATCH"']
|
@@ -1,17 +1,18 @@
|
|
1
|
-
"""Bouncy configuration handling"""
|
1
|
+
"""Bouncy configuration handling."""
|
2
2
|
|
3
|
-
from configaroo.configuration import Configuration
|
3
|
+
from configaroo.configuration import Configuration, print_configuration
|
4
4
|
from configaroo.exceptions import (
|
5
|
-
|
5
|
+
ConfigarooError,
|
6
6
|
MissingEnvironmentVariableError,
|
7
7
|
UnsupportedLoaderError,
|
8
8
|
)
|
9
9
|
|
10
10
|
__all__ = [
|
11
|
+
"ConfigarooError",
|
11
12
|
"Configuration",
|
12
|
-
"ConfigarooException",
|
13
13
|
"MissingEnvironmentVariableError",
|
14
14
|
"UnsupportedLoaderError",
|
15
|
+
"print_configuration",
|
15
16
|
]
|
16
17
|
|
17
|
-
__version__ = "0.2.
|
18
|
+
__version__ = "0.2.3"
|
@@ -1,11 +1,12 @@
|
|
1
|
-
"""A dict-like
|
1
|
+
"""A dict-like config with support for envvars, validation and type conversion."""
|
2
2
|
|
3
3
|
import inspect
|
4
4
|
import os
|
5
5
|
import re
|
6
6
|
from collections import UserDict
|
7
|
+
from collections.abc import Callable
|
7
8
|
from pathlib import Path
|
8
|
-
from typing import Any, Self,
|
9
|
+
from typing import Any, Self, TypeVar
|
9
10
|
|
10
11
|
from pydantic import BaseModel
|
11
12
|
|
@@ -15,12 +16,12 @@ from configaroo.exceptions import MissingEnvironmentVariableError
|
|
15
16
|
ModelT = TypeVar("ModelT", bound=BaseModel)
|
16
17
|
|
17
18
|
|
18
|
-
class Configuration(UserDict):
|
19
|
-
"""A Configuration is a dict-like structure with some conveniences"""
|
19
|
+
class Configuration(UserDict[str, Any]):
|
20
|
+
"""A Configuration is a dict-like structure with some conveniences."""
|
20
21
|
|
21
22
|
@classmethod
|
22
23
|
def from_dict(cls, data: dict[str, Any] | UserDict[str, Any] | Self) -> Self:
|
23
|
-
"""Construct a Configuration from a dictionary
|
24
|
+
"""Construct a Configuration from a dictionary.
|
24
25
|
|
25
26
|
The dictionary is referenced directly, a copy isn't made
|
26
27
|
"""
|
@@ -40,7 +41,7 @@ class Configuration(UserDict):
|
|
40
41
|
env_prefix: str = "",
|
41
42
|
extra_dynamic: dict[str, Any] | None = None,
|
42
43
|
) -> Self:
|
43
|
-
"""Read a Configuration from a file"""
|
44
|
+
"""Read a Configuration from a file."""
|
44
45
|
config_dict = loaders.from_file(file_path, loader=loader)
|
45
46
|
return cls(config_dict).initialize(
|
46
47
|
envs=envs, env_prefix=env_prefix, extra_dynamic=extra_dynamic
|
@@ -56,32 +57,34 @@ class Configuration(UserDict):
|
|
56
57
|
|
57
58
|
The initialization adds environment variables and parses dynamic values.
|
58
59
|
"""
|
59
|
-
self = self if envs is None else self.add_envs(envs, prefix=env_prefix)
|
60
|
+
self = self if envs is None else self.add_envs(envs, prefix=env_prefix) # noqa: PLW0642
|
60
61
|
return self.parse_dynamic(extra_dynamic)
|
61
62
|
|
62
|
-
def with_model(self, model:
|
63
|
+
def with_model(self, model: type[ModelT]) -> ModelT:
|
63
64
|
"""Apply a pydantic model to a configuration."""
|
64
65
|
return self.validate_model(model).convert_model(model)
|
65
66
|
|
66
|
-
def __getitem__(self, key: str) -> Any:
|
67
|
-
"""Make sure nested sections have type Configuration"""
|
67
|
+
def __getitem__(self, key: str) -> Any: # noqa: ANN401
|
68
|
+
"""Make sure nested sections have type Configuration."""
|
68
69
|
value = self.data[key]
|
69
70
|
if isinstance(value, dict | UserDict | Configuration):
|
70
71
|
return Configuration.from_dict(value)
|
71
|
-
else:
|
72
|
-
return value
|
73
72
|
|
74
|
-
|
75
|
-
|
73
|
+
return value
|
74
|
+
|
75
|
+
def __getattr__(self, key: str) -> Any: # noqa: ANN401
|
76
|
+
"""Create attribute access for config keys for convenience."""
|
76
77
|
try:
|
77
78
|
return self[key]
|
78
79
|
except KeyError:
|
79
|
-
|
80
|
-
|
81
|
-
)
|
80
|
+
message = f"'{type(self).__name__}' has no attribute or key '{key}'"
|
81
|
+
raise AttributeError(message) from None
|
82
82
|
|
83
83
|
def __contains__(self, key: object) -> bool:
|
84
|
-
"""Add support for dotted keys
|
84
|
+
"""Add support for dotted keys.
|
85
|
+
|
86
|
+
The type hint for key is object to match the UserDict class.
|
87
|
+
"""
|
85
88
|
if key in self.data:
|
86
89
|
return True
|
87
90
|
prefix, _, rest = str(key).partition(".")
|
@@ -90,8 +93,8 @@ class Configuration(UserDict):
|
|
90
93
|
except KeyError:
|
91
94
|
return False
|
92
95
|
|
93
|
-
def get(self, key: str, default: Any = None) -> Any:
|
94
|
-
"""Allow dotted keys when using .get()"""
|
96
|
+
def get(self, key: str, default: Any = None) -> Any: # noqa: ANN401
|
97
|
+
"""Allow dotted keys when using .get()."""
|
95
98
|
if key in self.data:
|
96
99
|
return self[key]
|
97
100
|
|
@@ -101,8 +104,8 @@ class Configuration(UserDict):
|
|
101
104
|
except KeyError:
|
102
105
|
return default
|
103
106
|
|
104
|
-
def add(self, key: str, value: Any) -> Self:
|
105
|
-
"""Add a value, allow dotted keys"""
|
107
|
+
def add(self, key: str, value: Any) -> Self: # noqa: ANN401
|
108
|
+
"""Add a value, allow dotted keys."""
|
106
109
|
prefix, _, rest = key.partition(".")
|
107
110
|
if not rest:
|
108
111
|
return self | {key: value}
|
@@ -110,21 +113,19 @@ class Configuration(UserDict):
|
|
110
113
|
return self | {prefix: cls(self.setdefault(prefix, {})).add(rest, value)}
|
111
114
|
|
112
115
|
def add_envs(self, envs: dict[str, str], prefix: str = "") -> Self:
|
113
|
-
"""Add environment variables to configuration"""
|
116
|
+
"""Add environment variables to configuration."""
|
114
117
|
for env, key in envs.items():
|
115
118
|
env_key = f"{prefix}{env}"
|
116
119
|
if env_value := os.getenv(env_key):
|
117
|
-
self = self.add(key, env_value)
|
120
|
+
self = self.add(key, env_value) # noqa: PLW0642
|
118
121
|
elif key not in self:
|
119
|
-
raise MissingEnvironmentVariableError(
|
120
|
-
f"required environment variable '{env_key}' not found"
|
121
|
-
)
|
122
|
+
raise MissingEnvironmentVariableError(env_key)
|
122
123
|
return self
|
123
124
|
|
124
125
|
def parse_dynamic(
|
125
|
-
self, extra: dict[str, Any] | None = None, _include_self: bool = True
|
126
|
+
self, extra: dict[str, Any] | None = None, *, _include_self: bool = True
|
126
127
|
) -> Self:
|
127
|
-
"""Parse dynamic values of the form {section.key}"""
|
128
|
+
"""Parse dynamic values of the form {section.key}."""
|
128
129
|
cls = type(self)
|
129
130
|
variables = (
|
130
131
|
(self.to_flat_dict() if _include_self else {})
|
@@ -148,17 +149,17 @@ class Configuration(UserDict):
|
|
148
149
|
# Continue parsing until no more replacements are made.
|
149
150
|
return parsed.parse_dynamic(extra=extra, _include_self=_include_self)
|
150
151
|
|
151
|
-
def validate_model(self, model:
|
152
|
+
def validate_model(self, model: type[BaseModel]) -> Self:
|
152
153
|
"""Validate the configuration against the given model."""
|
153
154
|
model.model_validate(self.data)
|
154
155
|
return self
|
155
156
|
|
156
|
-
def convert_model(self, model:
|
157
|
-
"""Convert data types to match the given model"""
|
157
|
+
def convert_model(self, model: type[ModelT]) -> ModelT:
|
158
|
+
"""Convert data types to match the given model."""
|
158
159
|
return model(**self.data)
|
159
160
|
|
160
161
|
def to_dict(self) -> dict[str, Any]:
|
161
|
-
"""Dump the configuration into a Python dictionary"""
|
162
|
+
"""Dump the configuration into a Python dictionary."""
|
162
163
|
return {
|
163
164
|
key: value.to_dict() if isinstance(value, Configuration) else value
|
164
165
|
for key, value in self.items()
|
@@ -183,6 +184,50 @@ class Configuration(UserDict):
|
|
183
184
|
}
|
184
185
|
|
185
186
|
|
187
|
+
def print_configuration(config: Configuration | BaseModel, indent: int = 4) -> None:
|
188
|
+
"""Pretty print a configuration.
|
189
|
+
|
190
|
+
If rich is installed, then a rich console is used for the printing.
|
191
|
+
"""
|
192
|
+
return _print_dict_as_tree(
|
193
|
+
config.model_dump() if isinstance(config, BaseModel) else config,
|
194
|
+
indent=indent,
|
195
|
+
_print=_get_rich_print(),
|
196
|
+
)
|
197
|
+
|
198
|
+
|
199
|
+
def _get_rich_print() -> Callable[[str], None]:
|
200
|
+
"""Initialize a Rich console if Rich is installed, otherwise use built-in print."""
|
201
|
+
try:
|
202
|
+
from rich.console import Console # noqa: PLC0415
|
203
|
+
|
204
|
+
return Console().print
|
205
|
+
except ImportError:
|
206
|
+
import builtins # noqa: PLC0415
|
207
|
+
|
208
|
+
return builtins.print
|
209
|
+
|
210
|
+
|
211
|
+
def _print_dict_as_tree(
|
212
|
+
data: dict[str, Any] | UserDict[str, Any] | Configuration,
|
213
|
+
indent: int = 4,
|
214
|
+
current_indent: int = 0,
|
215
|
+
_print: Callable[[str], None] = print,
|
216
|
+
) -> None:
|
217
|
+
"""Print a nested dictionary as a tree."""
|
218
|
+
for key, value in data.items():
|
219
|
+
if isinstance(value, dict | UserDict | Configuration):
|
220
|
+
_print(" " * current_indent + f"- {key}")
|
221
|
+
_print_dict_as_tree(
|
222
|
+
value,
|
223
|
+
indent=indent,
|
224
|
+
current_indent=current_indent + indent,
|
225
|
+
_print=print,
|
226
|
+
)
|
227
|
+
else:
|
228
|
+
_print(" " * current_indent + f"- {key}: {value!r}")
|
229
|
+
|
230
|
+
|
186
231
|
def _find_pyproject_toml(
|
187
232
|
path: Path | None = None, _file_name: str = "pyproject.toml"
|
188
233
|
) -> Path:
|
@@ -195,8 +240,8 @@ def _find_pyproject_toml(
|
|
195
240
|
path = _get_foreign_path() if path is None else path
|
196
241
|
if (path / _file_name).exists() or path == path.parent:
|
197
242
|
return path.resolve()
|
198
|
-
|
199
|
-
|
243
|
+
|
244
|
+
return _find_pyproject_toml(path.parent, _file_name=_file_name)
|
200
245
|
|
201
246
|
|
202
247
|
def _get_foreign_path() -> Path:
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"""Configaroo specific exceptions."""
|
2
|
+
|
3
|
+
|
4
|
+
class ConfigarooError(Exception):
|
5
|
+
"""Base exception for more specific Configaroo exceptions."""
|
6
|
+
|
7
|
+
|
8
|
+
class MissingEnvironmentVariableError(ConfigarooError, KeyError):
|
9
|
+
"""A required environment variable is missing."""
|
10
|
+
|
11
|
+
def __init__(self, name: str) -> None:
|
12
|
+
"""Set a consistent error message."""
|
13
|
+
super().__init__(f"required environment variable '{name}' not found")
|
14
|
+
|
15
|
+
|
16
|
+
class UnsupportedLoaderError(ConfigarooError, ValueError):
|
17
|
+
"""An unsupported loader is called."""
|
18
|
+
|
19
|
+
def __init__(self, loader: str, available: list[str]) -> None:
|
20
|
+
"""Set a consistent error message."""
|
21
|
+
super().__init__(
|
22
|
+
f"file type '{loader}' isn't supported. Use one of: {', '.join(available)}"
|
23
|
+
)
|
@@ -7,8 +7,23 @@ import pyplugs
|
|
7
7
|
|
8
8
|
from configaroo.exceptions import UnsupportedLoaderError
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
PACKAGE = str(__package__)
|
11
|
+
|
12
|
+
|
13
|
+
def load(loader: str, path: Path) -> dict[str, Any]:
|
14
|
+
"""Load a file using the given loader."""
|
15
|
+
return pyplugs.call_typed(
|
16
|
+
PACKAGE,
|
17
|
+
plugin=loader,
|
18
|
+
func="load",
|
19
|
+
path=path,
|
20
|
+
_return_type=dict(), # noqa: C408
|
21
|
+
)
|
22
|
+
|
23
|
+
|
24
|
+
def loader_names() -> list[str]:
|
25
|
+
"""List names of available loaders."""
|
26
|
+
return sorted(pyplugs.names(PACKAGE))
|
12
27
|
|
13
28
|
|
14
29
|
def from_file(path: str | Path, loader: str | None = None) -> dict[str, Any]:
|
@@ -18,7 +33,4 @@ def from_file(path: str | Path, loader: str | None = None) -> dict[str, Any]:
|
|
18
33
|
try:
|
19
34
|
return load(loader, path=path)
|
20
35
|
except pyplugs.UnknownPluginError:
|
21
|
-
raise UnsupportedLoaderError(
|
22
|
-
f"file type '{loader}' isn't supported. "
|
23
|
-
f"Use one of: {', '.join(loader_names())}"
|
24
|
-
) from None
|
36
|
+
raise UnsupportedLoaderError(loader, loader_names()) from None
|
@@ -0,0 +1,16 @@
|
|
1
|
+
"""Loader for JSON-files."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
import pyplugs
|
8
|
+
|
9
|
+
|
10
|
+
@pyplugs.register
|
11
|
+
def load(path: Path) -> dict[str, Any]:
|
12
|
+
"""Read a JSON-file.
|
13
|
+
|
14
|
+
Enforce that the JSON is an array/dict.
|
15
|
+
"""
|
16
|
+
return dict(json.loads(path.read_text(encoding="utf-8")))
|
@@ -1,4 +1,4 @@
|
|
1
|
-
"""Loader for TOML-files"""
|
1
|
+
"""Loader for TOML-files."""
|
2
2
|
|
3
3
|
import tomllib
|
4
4
|
from pathlib import Path
|
@@ -8,6 +8,6 @@ import pyplugs
|
|
8
8
|
|
9
9
|
|
10
10
|
@pyplugs.register
|
11
|
-
def
|
12
|
-
"""Read a TOML-file"""
|
11
|
+
def load(path: Path) -> dict[str, Any]:
|
12
|
+
"""Read a TOML-file."""
|
13
13
|
return tomllib.loads(path.read_text(encoding="utf-8"))
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: configaroo
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.3
|
4
4
|
Summary: Bouncy handling of configuration files
|
5
5
|
Author-email: Geir Arne Hjelle <geirarne@gmail.com>
|
6
6
|
Maintainer-email: Geir Arne Hjelle <geirarne@gmail.com>
|
@@ -17,7 +17,6 @@ Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
20
|
-
Classifier: Programming Language :: Python :: 3.14
|
21
20
|
Classifier: Programming Language :: Python :: 3
|
22
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
23
22
|
Classifier: Typing :: Typed
|
@@ -25,11 +24,19 @@ Requires-Python: >=3.11
|
|
25
24
|
Description-Content-Type: text/markdown
|
26
25
|
License-File: LICENSE
|
27
26
|
Requires-Dist: pydantic>=2.0
|
28
|
-
Requires-Dist: pyplugs>=0.4
|
27
|
+
Requires-Dist: pyplugs>=0.5.4
|
29
28
|
Dynamic: license-file
|
30
29
|
|
31
30
|
# Configaroo - Bouncy Configuration Handling
|
32
31
|
|
32
|
+
[](https://pypi.org/project/configaroo/)
|
33
|
+
[](https://pypi.org/project/configaroo/)
|
34
|
+
[](https://github.com/gahjelle/configaroo/blob/main/LICENSE)
|
35
|
+
[](https://github.com/astral-sh/ruff)
|
36
|
+
[](https://github.com/gahjelle/configaroo/actions/workflows/lint.yml)
|
37
|
+
[](https://github.com/gahjelle/configaroo/actions/workflows/test.yml)
|
38
|
+
[](http://mypy-lang.org/)
|
39
|
+
|
33
40
|
Configaroo is a light configuration package for Python that offers the following features:
|
34
41
|
|
35
42
|
- Access configuration settings with dotted keys: `config.nested.key`
|