configaroo 0.1.3__py3-none-any.whl → 0.2.1__py3-none-any.whl
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/__init__.py +10 -3
- configaroo/configuration.py +23 -18
- {configaroo-0.1.3.dist-info → configaroo-0.2.1.dist-info}/METADATA +1 -1
- configaroo-0.2.1.dist-info/RECORD +12 -0
- {configaroo-0.1.3.dist-info → configaroo-0.2.1.dist-info}/WHEEL +1 -1
- configaroo-0.1.3.dist-info/RECORD +0 -12
- {configaroo-0.1.3.dist-info → configaroo-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {configaroo-0.1.3.dist-info → configaroo-0.2.1.dist-info}/top_level.txt +0 -0
configaroo/__init__.py
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
"""Bouncy configuration handling"""
|
2
2
|
|
3
|
-
from configaroo.configuration import Configuration
|
4
|
-
from configaroo.exceptions import (
|
3
|
+
from configaroo.configuration import Configuration
|
4
|
+
from configaroo.exceptions import (
|
5
5
|
ConfigarooException,
|
6
6
|
MissingEnvironmentVariableError,
|
7
7
|
UnsupportedLoaderError,
|
8
8
|
)
|
9
9
|
|
10
|
-
|
10
|
+
__all__ = [
|
11
|
+
"Configuration",
|
12
|
+
"ConfigarooException",
|
13
|
+
"MissingEnvironmentVariableError",
|
14
|
+
"UnsupportedLoaderError",
|
15
|
+
]
|
16
|
+
|
17
|
+
__version__ = "0.2.1"
|
configaroo/configuration.py
CHANGED
@@ -5,13 +5,15 @@ import os
|
|
5
5
|
import re
|
6
6
|
from collections import UserDict
|
7
7
|
from pathlib import Path
|
8
|
-
from typing import Any, Self, Type
|
8
|
+
from typing import Any, Self, Type, TypeVar
|
9
9
|
|
10
10
|
from pydantic import BaseModel
|
11
11
|
|
12
12
|
from configaroo import loaders
|
13
13
|
from configaroo.exceptions import MissingEnvironmentVariableError
|
14
14
|
|
15
|
+
ModelT = TypeVar("ModelT", bound=BaseModel)
|
16
|
+
|
15
17
|
|
16
18
|
class Configuration(UserDict):
|
17
19
|
"""A Configuration is a dict-like structure with some conveniences"""
|
@@ -24,12 +26,11 @@ class Configuration(UserDict):
|
|
24
26
|
envs: dict[str, str] | None = None,
|
25
27
|
env_prefix: str = "",
|
26
28
|
extra_dynamic: dict[str, Any] | None = None,
|
27
|
-
model: Type[BaseModel] | None = None,
|
28
29
|
) -> Self:
|
29
30
|
"""Read a Configuration from a file"""
|
30
31
|
config_dict = loaders.from_file(file_path, loader=loader)
|
31
32
|
return cls(config_dict).initialize(
|
32
|
-
envs=envs, env_prefix=env_prefix, extra_dynamic=extra_dynamic
|
33
|
+
envs=envs, env_prefix=env_prefix, extra_dynamic=extra_dynamic
|
33
34
|
)
|
34
35
|
|
35
36
|
def initialize(
|
@@ -37,18 +38,17 @@ class Configuration(UserDict):
|
|
37
38
|
envs: dict[str, str] | None = None,
|
38
39
|
env_prefix: str = "",
|
39
40
|
extra_dynamic: dict[str, Any] | None = None,
|
40
|
-
model: Type[BaseModel] | None = None,
|
41
41
|
) -> Self:
|
42
42
|
"""Initialize a configuration.
|
43
43
|
|
44
|
-
The initialization adds environment variables
|
45
|
-
validates against a Pydantic model, and converts value types using the
|
46
|
-
same model.
|
44
|
+
The initialization adds environment variables and parses dynamic values.
|
47
45
|
"""
|
48
46
|
self = self if envs is None else self.add_envs(envs, prefix=env_prefix)
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
return self.parse_dynamic(extra_dynamic)
|
48
|
+
|
49
|
+
def with_model(self, model: Type[ModelT]) -> ModelT:
|
50
|
+
"""Apply a pydantic model to a configuration."""
|
51
|
+
return self.validate_model(model).convert_model(model)
|
52
52
|
|
53
53
|
def __getitem__(self, key: str) -> Any:
|
54
54
|
"""Make sure nested sections have type Configuration"""
|
@@ -108,18 +108,20 @@ class Configuration(UserDict):
|
|
108
108
|
)
|
109
109
|
return self
|
110
110
|
|
111
|
-
def parse_dynamic(
|
111
|
+
def parse_dynamic(
|
112
|
+
self, extra: dict[str, Any] | None = None, _include_self: bool = True
|
113
|
+
) -> Self:
|
112
114
|
"""Parse dynamic values of the form {section.key}"""
|
113
115
|
cls = type(self)
|
114
116
|
variables = (
|
115
|
-
self.to_flat_dict()
|
117
|
+
(self.to_flat_dict() if _include_self else {})
|
116
118
|
| {"project_path": _find_pyproject_toml()}
|
117
119
|
| ({} if extra is None else extra)
|
118
120
|
)
|
119
|
-
|
121
|
+
parsed = cls(
|
120
122
|
{
|
121
123
|
key: (
|
122
|
-
value.parse_dynamic(extra=variables)
|
124
|
+
value.parse_dynamic(extra=variables, _include_self=False)
|
123
125
|
if isinstance(value, Configuration)
|
124
126
|
else _incomplete_format(value, variables)
|
125
127
|
if isinstance(value, str)
|
@@ -128,16 +130,19 @@ class Configuration(UserDict):
|
|
128
130
|
for key, value in self.items()
|
129
131
|
}
|
130
132
|
)
|
133
|
+
if parsed == self:
|
134
|
+
return parsed
|
135
|
+
# Continue parsing until no more replacements are made.
|
136
|
+
return parsed.parse_dynamic(extra=extra, _include_self=_include_self)
|
131
137
|
|
132
|
-
def
|
138
|
+
def validate_model(self, model: Type[BaseModel]) -> Self:
|
133
139
|
"""Validate the configuration against the given model."""
|
134
140
|
model.model_validate(self.data)
|
135
141
|
return self
|
136
142
|
|
137
|
-
def
|
143
|
+
def convert_model(self, model: Type[ModelT]) -> ModelT:
|
138
144
|
"""Convert data types to match the given model"""
|
139
|
-
|
140
|
-
return cls(model(**self.data).model_dump())
|
145
|
+
return model(**self.data)
|
141
146
|
|
142
147
|
def to_dict(self) -> dict[str, Any]:
|
143
148
|
"""Dump the configuration into a Python dictionary"""
|
@@ -0,0 +1,12 @@
|
|
1
|
+
configaroo/__init__.py,sha256=-rsRqALc9-loy4qWJQHYE6QG0rZ1gwYjIUPy72DbsZg,371
|
2
|
+
configaroo/configuration.py,sha256=a39w6uHJMrc8pDbHZJX0dr8DVZ6gMFbFSLUlEPCMCdw,7779
|
3
|
+
configaroo/exceptions.py,sha256=1h-6hV7VqqKaRFnu539pjsO0ESSNDibt2kUeF7jAM2M,374
|
4
|
+
configaroo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
configaroo/loaders/__init__.py,sha256=-6GAR4g7vYW7Zagq9Ev479kpkfY7kbvDl4JPXd4wQQ0,756
|
6
|
+
configaroo/loaders/json.py,sha256=6gs_ZegUEe70vb6uSJfKBos7UjfuJnyWgPE_kjDQISw,258
|
7
|
+
configaroo/loaders/toml.py,sha256=ZVbHutZ7V-Z_jf2aHjV18jwaqCXumlr06j48rojHcBM,264
|
8
|
+
configaroo-0.2.1.dist-info/licenses/LICENSE,sha256=2cbzsB4lU8qOO8fO1XVNJD7XPxzPhaWXm6U9DenSc0s,1089
|
9
|
+
configaroo-0.2.1.dist-info/METADATA,sha256=0Od55T9MEvRBhi4JHllAU2S0U9NBl0T99yA2Axsf0wk,1789
|
10
|
+
configaroo-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
11
|
+
configaroo-0.2.1.dist-info/top_level.txt,sha256=JVYICl1cWSjvSOZuZMYm976z9lnZaWtHVRSt373QCxg,11
|
12
|
+
configaroo-0.2.1.dist-info/RECORD,,
|
@@ -1,12 +0,0 @@
|
|
1
|
-
configaroo/__init__.py,sha256=aiOAkxMR9x3VgmQI9ppQ8uzx-gHGvZ-AjQabE1HpZ2E,255
|
2
|
-
configaroo/configuration.py,sha256=PJLWR2mA_bYM7iuRXqfBQ_rnZoughNCFA5kAEjjZz8A,7591
|
3
|
-
configaroo/exceptions.py,sha256=1h-6hV7VqqKaRFnu539pjsO0ESSNDibt2kUeF7jAM2M,374
|
4
|
-
configaroo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
configaroo/loaders/__init__.py,sha256=-6GAR4g7vYW7Zagq9Ev479kpkfY7kbvDl4JPXd4wQQ0,756
|
6
|
-
configaroo/loaders/json.py,sha256=6gs_ZegUEe70vb6uSJfKBos7UjfuJnyWgPE_kjDQISw,258
|
7
|
-
configaroo/loaders/toml.py,sha256=ZVbHutZ7V-Z_jf2aHjV18jwaqCXumlr06j48rojHcBM,264
|
8
|
-
configaroo-0.1.3.dist-info/licenses/LICENSE,sha256=2cbzsB4lU8qOO8fO1XVNJD7XPxzPhaWXm6U9DenSc0s,1089
|
9
|
-
configaroo-0.1.3.dist-info/METADATA,sha256=kv0NQFHsskjUQBuDGe56FdZOUh4FtUWYzbZ3Y5fvju4,1789
|
10
|
-
configaroo-0.1.3.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
11
|
-
configaroo-0.1.3.dist-info/top_level.txt,sha256=JVYICl1cWSjvSOZuZMYm976z9lnZaWtHVRSt373QCxg,11
|
12
|
-
configaroo-0.1.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|