configaroo 0.2.1__py3-none-any.whl → 0.2.3__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 +6 -5
- configaroo/configuration.py +95 -37
- configaroo/exceptions.py +17 -7
- configaroo/loaders/__init__.py +18 -6
- configaroo/loaders/json.py +7 -4
- configaroo/loaders/toml.py +3 -3
- {configaroo-0.2.1.dist-info → configaroo-0.2.3.dist-info}/METADATA +10 -3
- configaroo-0.2.3.dist-info/RECORD +12 -0
- {configaroo-0.2.1.dist-info → configaroo-0.2.3.dist-info}/licenses/LICENSE +1 -1
- configaroo-0.2.1.dist-info/RECORD +0 -12
- {configaroo-0.2.1.dist-info → configaroo-0.2.3.dist-info}/WHEEL +0 -0
- {configaroo-0.2.1.dist-info → configaroo-0.2.3.dist-info}/top_level.txt +0 -0
configaroo/__init__.py
CHANGED
@@ -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"
|
configaroo/configuration.py
CHANGED
@@ -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,8 +16,21 @@ 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."""
|
21
|
+
|
22
|
+
@classmethod
|
23
|
+
def from_dict(cls, data: dict[str, Any] | UserDict[str, Any] | Self) -> Self:
|
24
|
+
"""Construct a Configuration from a dictionary.
|
25
|
+
|
26
|
+
The dictionary is referenced directly, a copy isn't made
|
27
|
+
"""
|
28
|
+
configuration = cls()
|
29
|
+
if isinstance(data, UserDict | Configuration):
|
30
|
+
configuration.data = data.data
|
31
|
+
else:
|
32
|
+
configuration.data = data
|
33
|
+
return configuration
|
20
34
|
|
21
35
|
@classmethod
|
22
36
|
def from_file(
|
@@ -27,7 +41,7 @@ class Configuration(UserDict):
|
|
27
41
|
env_prefix: str = "",
|
28
42
|
extra_dynamic: dict[str, Any] | None = None,
|
29
43
|
) -> Self:
|
30
|
-
"""Read a Configuration from a file"""
|
44
|
+
"""Read a Configuration from a file."""
|
31
45
|
config_dict = loaders.from_file(file_path, loader=loader)
|
32
46
|
return cls(config_dict).initialize(
|
33
47
|
envs=envs, env_prefix=env_prefix, extra_dynamic=extra_dynamic
|
@@ -43,42 +57,44 @@ class Configuration(UserDict):
|
|
43
57
|
|
44
58
|
The initialization adds environment variables and parses dynamic values.
|
45
59
|
"""
|
46
|
-
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
|
47
61
|
return self.parse_dynamic(extra_dynamic)
|
48
62
|
|
49
|
-
def with_model(self, model:
|
63
|
+
def with_model(self, model: type[ModelT]) -> ModelT:
|
50
64
|
"""Apply a pydantic model to a configuration."""
|
51
65
|
return self.validate_model(model).convert_model(model)
|
52
66
|
|
53
|
-
def __getitem__(self, key: str) -> Any:
|
54
|
-
"""Make sure nested sections have type Configuration"""
|
67
|
+
def __getitem__(self, key: str) -> Any: # noqa: ANN401
|
68
|
+
"""Make sure nested sections have type Configuration."""
|
55
69
|
value = self.data[key]
|
56
70
|
if isinstance(value, dict | UserDict | Configuration):
|
57
|
-
return Configuration(value)
|
58
|
-
|
59
|
-
|
71
|
+
return Configuration.from_dict(value)
|
72
|
+
|
73
|
+
return value
|
60
74
|
|
61
|
-
def __getattr__(self, key: str) -> Any:
|
62
|
-
"""Create attribute access for config keys for convenience"""
|
75
|
+
def __getattr__(self, key: str) -> Any: # noqa: ANN401
|
76
|
+
"""Create attribute access for config keys for convenience."""
|
63
77
|
try:
|
64
78
|
return self[key]
|
65
79
|
except KeyError:
|
66
|
-
|
67
|
-
|
68
|
-
|
80
|
+
message = f"'{type(self).__name__}' has no attribute or key '{key}'"
|
81
|
+
raise AttributeError(message) from None
|
82
|
+
|
83
|
+
def __contains__(self, key: object) -> bool:
|
84
|
+
"""Add support for dotted keys.
|
69
85
|
|
70
|
-
|
71
|
-
"""
|
86
|
+
The type hint for key is object to match the UserDict class.
|
87
|
+
"""
|
72
88
|
if key in self.data:
|
73
89
|
return True
|
74
|
-
prefix, _, rest = key.partition(".")
|
90
|
+
prefix, _, rest = str(key).partition(".")
|
75
91
|
try:
|
76
92
|
return rest in self[prefix]
|
77
93
|
except KeyError:
|
78
94
|
return False
|
79
95
|
|
80
|
-
def get(self, key: str, default: Any = None) -> Any:
|
81
|
-
"""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()."""
|
82
98
|
if key in self.data:
|
83
99
|
return self[key]
|
84
100
|
|
@@ -88,8 +104,8 @@ class Configuration(UserDict):
|
|
88
104
|
except KeyError:
|
89
105
|
return default
|
90
106
|
|
91
|
-
def add(self, key: str, value: Any) -> Self:
|
92
|
-
"""Add a value, allow dotted keys"""
|
107
|
+
def add(self, key: str, value: Any) -> Self: # noqa: ANN401
|
108
|
+
"""Add a value, allow dotted keys."""
|
93
109
|
prefix, _, rest = key.partition(".")
|
94
110
|
if not rest:
|
95
111
|
return self | {key: value}
|
@@ -97,21 +113,19 @@ class Configuration(UserDict):
|
|
97
113
|
return self | {prefix: cls(self.setdefault(prefix, {})).add(rest, value)}
|
98
114
|
|
99
115
|
def add_envs(self, envs: dict[str, str], prefix: str = "") -> Self:
|
100
|
-
"""Add environment variables to configuration"""
|
116
|
+
"""Add environment variables to configuration."""
|
101
117
|
for env, key in envs.items():
|
102
118
|
env_key = f"{prefix}{env}"
|
103
119
|
if env_value := os.getenv(env_key):
|
104
|
-
self = self.add(key, env_value)
|
120
|
+
self = self.add(key, env_value) # noqa: PLW0642
|
105
121
|
elif key not in self:
|
106
|
-
raise MissingEnvironmentVariableError(
|
107
|
-
f"required environment variable '{env_key}' not found"
|
108
|
-
)
|
122
|
+
raise MissingEnvironmentVariableError(env_key)
|
109
123
|
return self
|
110
124
|
|
111
125
|
def parse_dynamic(
|
112
|
-
self, extra: dict[str, Any] | None = None, _include_self: bool = True
|
126
|
+
self, extra: dict[str, Any] | None = None, *, _include_self: bool = True
|
113
127
|
) -> Self:
|
114
|
-
"""Parse dynamic values of the form {section.key}"""
|
128
|
+
"""Parse dynamic values of the form {section.key}."""
|
115
129
|
cls = type(self)
|
116
130
|
variables = (
|
117
131
|
(self.to_flat_dict() if _include_self else {})
|
@@ -135,17 +149,17 @@ class Configuration(UserDict):
|
|
135
149
|
# Continue parsing until no more replacements are made.
|
136
150
|
return parsed.parse_dynamic(extra=extra, _include_self=_include_self)
|
137
151
|
|
138
|
-
def validate_model(self, model:
|
152
|
+
def validate_model(self, model: type[BaseModel]) -> Self:
|
139
153
|
"""Validate the configuration against the given model."""
|
140
154
|
model.model_validate(self.data)
|
141
155
|
return self
|
142
156
|
|
143
|
-
def convert_model(self, model:
|
144
|
-
"""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."""
|
145
159
|
return model(**self.data)
|
146
160
|
|
147
161
|
def to_dict(self) -> dict[str, Any]:
|
148
|
-
"""Dump the configuration into a Python dictionary"""
|
162
|
+
"""Dump the configuration into a Python dictionary."""
|
149
163
|
return {
|
150
164
|
key: value.to_dict() if isinstance(value, Configuration) else value
|
151
165
|
for key, value in self.items()
|
@@ -170,6 +184,50 @@ class Configuration(UserDict):
|
|
170
184
|
}
|
171
185
|
|
172
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
|
+
|
173
231
|
def _find_pyproject_toml(
|
174
232
|
path: Path | None = None, _file_name: str = "pyproject.toml"
|
175
233
|
) -> Path:
|
@@ -182,8 +240,8 @@ def _find_pyproject_toml(
|
|
182
240
|
path = _get_foreign_path() if path is None else path
|
183
241
|
if (path / _file_name).exists() or path == path.parent:
|
184
242
|
return path.resolve()
|
185
|
-
|
186
|
-
|
243
|
+
|
244
|
+
return _find_pyproject_toml(path.parent, _file_name=_file_name)
|
187
245
|
|
188
246
|
|
189
247
|
def _get_foreign_path() -> Path:
|
configaroo/exceptions.py
CHANGED
@@ -1,13 +1,23 @@
|
|
1
|
-
"""Configaroo specific exceptions"""
|
1
|
+
"""Configaroo specific exceptions."""
|
2
2
|
|
3
3
|
|
4
|
-
class
|
5
|
-
"""Base exception for more specific Configaroo exceptions"""
|
4
|
+
class ConfigarooError(Exception):
|
5
|
+
"""Base exception for more specific Configaroo exceptions."""
|
6
6
|
|
7
7
|
|
8
|
-
class MissingEnvironmentVariableError(
|
9
|
-
"""A required environment variable is missing"""
|
8
|
+
class MissingEnvironmentVariableError(ConfigarooError, KeyError):
|
9
|
+
"""A required environment variable is missing."""
|
10
10
|
|
11
|
+
def __init__(self, name: str) -> None:
|
12
|
+
"""Set a consistent error message."""
|
13
|
+
super().__init__(f"required environment variable '{name}' not found")
|
11
14
|
|
12
|
-
|
13
|
-
|
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
|
+
)
|
configaroo/loaders/__init__.py
CHANGED
@@ -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
|
configaroo/loaders/json.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""Loader for JSON-files"""
|
1
|
+
"""Loader for JSON-files."""
|
2
2
|
|
3
3
|
import json
|
4
4
|
from pathlib import Path
|
@@ -8,6 +8,9 @@ import pyplugs
|
|
8
8
|
|
9
9
|
|
10
10
|
@pyplugs.register
|
11
|
-
def
|
12
|
-
"""Read a JSON-file
|
13
|
-
|
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")))
|
configaroo/loaders/toml.py
CHANGED
@@ -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`
|
@@ -0,0 +1,12 @@
|
|
1
|
+
configaroo/__init__.py,sha256=Ed1V019MUJ0Kac5NOY43u83axSsl7I1xE5j1DpAKXeI,412
|
2
|
+
configaroo/configuration.py,sha256=CzKS9zy3VCaCDexF5_DuYIhRIyBs8EVst6full9zM34,9728
|
3
|
+
configaroo/exceptions.py,sha256=GfLf3CLfHStiQjvdS7ZAtrKF9gmGL_8biFLayue6J0M,772
|
4
|
+
configaroo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
configaroo/loaders/__init__.py,sha256=l2pHeD9PJ3ZQA5xUCq9nfFqw2YhAHLeTe50wvhMmTYA,977
|
6
|
+
configaroo/loaders/json.py,sha256=fT2Lg4hPM2BuwqrDsP7bcJlepAdmEh2iKU-YVK4KmIA,306
|
7
|
+
configaroo/loaders/toml.py,sha256=jw9U78Lf-GMA8QmGIM8xMBqOhPaa8ITSMAhhN1ZNyng,256
|
8
|
+
configaroo-0.2.3.dist-info/licenses/LICENSE,sha256=rdeT6Y5bm0MUaERso7HRWpPj37Y1RD5li2lIQaMNJjc,1090
|
9
|
+
configaroo-0.2.3.dist-info/METADATA,sha256=Y9N_zcN5p1jO4pHt1sI_BPX2qT_w9S_2UmdhKj87uyM,2686
|
10
|
+
configaroo-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
11
|
+
configaroo-0.2.3.dist-info/top_level.txt,sha256=JVYICl1cWSjvSOZuZMYm976z9lnZaWtHVRSt373QCxg,11
|
12
|
+
configaroo-0.2.3.dist-info/RECORD,,
|
@@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
17
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
18
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
19
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -1,12 +0,0 @@
|
|
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,,
|
File without changes
|
File without changes
|