dataconf 3.5.0__tar.gz → 3.6.0__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.
- {dataconf-3.5.0 → dataconf-3.6.0}/PKG-INFO +3 -3
- {dataconf-3.5.0 → dataconf-3.6.0}/dataconf/utils.py +46 -15
- {dataconf-3.5.0 → dataconf-3.6.0}/dataconf/version.py +1 -1
- {dataconf-3.5.0 → dataconf-3.6.0}/pyproject.toml +8 -8
- {dataconf-3.5.0 → dataconf-3.6.0}/LICENSE +0 -0
- {dataconf-3.5.0 → dataconf-3.6.0}/README.md +0 -0
- {dataconf-3.5.0 → dataconf-3.6.0}/dataconf/__init__.py +0 -0
- {dataconf-3.5.0 → dataconf-3.6.0}/dataconf/cli.py +0 -0
- {dataconf-3.5.0 → dataconf-3.6.0}/dataconf/exceptions.py +0 -0
- {dataconf-3.5.0 → dataconf-3.6.0}/dataconf/main.py +0 -0
- {dataconf-3.5.0 → dataconf-3.6.0}/dataconf/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dataconf
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.0
|
|
4
4
|
Summary: Simple dataclasses configuration management for Python with hocon/json/yaml/properties/env-vars/dict support.
|
|
5
5
|
Home-page: https://github.com/zifeo/dataconf
|
|
6
6
|
License: MPL-2.0
|
|
@@ -15,9 +15,9 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
17
|
Requires-Dist: PyYAML (>=6.0.2,<7.0.0)
|
|
18
|
-
Requires-Dist: isodate (>=0.
|
|
18
|
+
Requires-Dist: isodate (>=0.7.2,<0.8.0)
|
|
19
19
|
Requires-Dist: pyhocon (>=0.3.61,<0.4.0)
|
|
20
|
-
Requires-Dist: pyparsing (>=3.2.
|
|
20
|
+
Requires-Dist: pyparsing (>=3.2.3,<4.0.0)
|
|
21
21
|
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
|
22
22
|
Project-URL: Repository, https://github.com/zifeo/dataconf
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
@@ -50,14 +50,16 @@ def __parse_type(value: Any, clazz: Type, path: str, check: bool):
|
|
|
50
50
|
except TypeError:
|
|
51
51
|
pass
|
|
52
52
|
|
|
53
|
-
raise TypeConfigException(
|
|
53
|
+
raise TypeConfigException(
|
|
54
|
+
f"expected type {clazz} at {path_to_str(path)}, got {type(value)}"
|
|
55
|
+
)
|
|
54
56
|
|
|
55
57
|
|
|
56
58
|
def is_union(origin):
|
|
57
59
|
return origin is Union or (PY310up and origin is UnionType)
|
|
58
60
|
|
|
59
61
|
|
|
60
|
-
def is_optional(type: Type):
|
|
62
|
+
def is_optional(type: Type | Any | str):
|
|
61
63
|
# Optional = Union[T, NoneType]
|
|
62
64
|
return is_union(get_origin(type)) and NoneType in get_args(type)
|
|
63
65
|
|
|
@@ -66,7 +68,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
66
68
|
if is_dataclass(clazz):
|
|
67
69
|
if not isinstance(value, ConfigTree):
|
|
68
70
|
raise TypeConfigException(
|
|
69
|
-
f"expected type {clazz} at {path}, got {type(value)}"
|
|
71
|
+
f"expected type {clazz} at {path_to_str(path)}, got {type(value)}"
|
|
70
72
|
)
|
|
71
73
|
|
|
72
74
|
fs = {}
|
|
@@ -96,15 +98,34 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
96
98
|
# Optional not found
|
|
97
99
|
fs[f.name] = None
|
|
98
100
|
|
|
101
|
+
elif is_dataclass(f.type):
|
|
102
|
+
if all(
|
|
103
|
+
not isinstance(field.default, _MISSING_TYPE)
|
|
104
|
+
or not isinstance(field.default_factory, _MISSING_TYPE)
|
|
105
|
+
or is_optional(field.type)
|
|
106
|
+
for field in fields(f.type)
|
|
107
|
+
):
|
|
108
|
+
fs[f.name] = f.type(
|
|
109
|
+
**{
|
|
110
|
+
field.name: None
|
|
111
|
+
for field in fields(f.type)
|
|
112
|
+
if is_optional(field.type)
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
else:
|
|
116
|
+
raise MalformedConfigException(
|
|
117
|
+
f'expected type {clazz} at {path_to_str(path)}, no field "{f.name}" found and {f.type} cannot be implicitly created'
|
|
118
|
+
)
|
|
119
|
+
|
|
99
120
|
else:
|
|
100
121
|
raise MalformedConfigException(
|
|
101
|
-
f
|
|
122
|
+
f'expected type {clazz} at {path_to_str(path)}, no field "{f.name}" found'
|
|
102
123
|
)
|
|
103
124
|
|
|
104
125
|
unexpected_keys = value.keys() - {renamings.get(k, k) for k in fs.keys()}
|
|
105
126
|
if len(unexpected_keys) > 0 and not ignore_unexpected:
|
|
106
127
|
raise UnexpectedKeysException(
|
|
107
|
-
f'unexpected key(s) "{", ".join(unexpected_keys)}" detected for type {clazz} at {path}'
|
|
128
|
+
f'unexpected key(s) "{", ".join(unexpected_keys)}" detected for type {clazz} at {path_to_str(path)}'
|
|
108
129
|
)
|
|
109
130
|
|
|
110
131
|
return clazz(**fs)
|
|
@@ -114,7 +135,9 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
114
135
|
|
|
115
136
|
if origin is list:
|
|
116
137
|
if value is None:
|
|
117
|
-
raise MalformedConfigException(
|
|
138
|
+
raise MalformedConfigException(
|
|
139
|
+
f"expected list at {path_to_str(path)} but received None"
|
|
140
|
+
)
|
|
118
141
|
|
|
119
142
|
if len(args) != 1:
|
|
120
143
|
raise MissingTypeException("expected list with type information: List[?]")
|
|
@@ -128,7 +151,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
128
151
|
if origin is tuple:
|
|
129
152
|
if value is None:
|
|
130
153
|
raise MalformedConfigException(
|
|
131
|
-
f"expected tuple at {path} but received None"
|
|
154
|
+
f"expected tuple at {path_to_str(path)} but received None"
|
|
132
155
|
)
|
|
133
156
|
|
|
134
157
|
if len(args) < 1:
|
|
@@ -181,7 +204,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
181
204
|
return None
|
|
182
205
|
|
|
183
206
|
raise TypeConfigException(
|
|
184
|
-
f"expected one of {', '.join(map(str, args))} at {path}, got {type(value)}"
|
|
207
|
+
f"expected one of {', '.join(map(str, args))} at {path_to_str(path)}, got {type(value)}"
|
|
185
208
|
)
|
|
186
209
|
|
|
187
210
|
if clazz is bool:
|
|
@@ -226,7 +249,9 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
226
249
|
return clazz(value)
|
|
227
250
|
elif isinstance(value, str):
|
|
228
251
|
return clazz.__getitem__(value)
|
|
229
|
-
raise TypeConfigException(
|
|
252
|
+
raise TypeConfigException(
|
|
253
|
+
f"expected str or int at {path_to_str(path)}, got {type(value)}"
|
|
254
|
+
)
|
|
230
255
|
|
|
231
256
|
if isclass(clazz) and issubclass(clazz, Path):
|
|
232
257
|
return clazz.__call__(value)
|
|
@@ -235,7 +260,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
235
260
|
if value in args:
|
|
236
261
|
return value
|
|
237
262
|
raise TypeConfigException(
|
|
238
|
-
f"expected one of {', '.join(map(str, args))} at {path}, got {value}"
|
|
263
|
+
f"expected one of {', '.join(map(str, args))} at {path_to_str(path)}, got {value}"
|
|
239
264
|
)
|
|
240
265
|
|
|
241
266
|
if clazz is datetime:
|
|
@@ -244,7 +269,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
244
269
|
return isoparse(dt)
|
|
245
270
|
except ValueError as e:
|
|
246
271
|
raise ParseException(
|
|
247
|
-
f"expected type {clazz} at {path}, cannot parse due to {e}"
|
|
272
|
+
f"expected type {clazz} at {path_to_str(path)}, cannot parse due to {e}"
|
|
248
273
|
)
|
|
249
274
|
|
|
250
275
|
if clazz is timedelta:
|
|
@@ -258,7 +283,7 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
258
283
|
return duration
|
|
259
284
|
except ValueError as e:
|
|
260
285
|
raise ParseException(
|
|
261
|
-
f"expected type {clazz} at {path}, cannot parse due to {e}"
|
|
286
|
+
f"expected type {clazz} at {path_to_str(path)}, cannot parse due to {e}"
|
|
262
287
|
)
|
|
263
288
|
|
|
264
289
|
if clazz is relativedelta:
|
|
@@ -292,17 +317,19 @@ def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected:
|
|
|
292
317
|
elif len(child_successes) > 1:
|
|
293
318
|
matching_classes = "\n- ".join(map(lambda x: x[0].__name__, child_successes))
|
|
294
319
|
raise AmbiguousSubclassException(
|
|
295
|
-
f"""multiple subtypes of {clazz} matched at {path}, use '_type' to disambiguate:\n- {matching_classes}"""
|
|
320
|
+
f"""multiple subtypes of {clazz} matched at {path_to_str(path)}, use '_type' to disambiguate:\n- {matching_classes}"""
|
|
296
321
|
)
|
|
297
322
|
|
|
298
323
|
# no need to check length; false if empty
|
|
299
324
|
if child_failures:
|
|
300
325
|
failures = "\n- ".join([str(c) for c in child_failures])
|
|
301
326
|
raise TypeConfigException(
|
|
302
|
-
f"expected type {clazz} at {path}, failed subclasses:\n- {failures}"
|
|
327
|
+
f"expected type {clazz} at {path_to_str(path)}, failed subclasses:\n- {failures}"
|
|
303
328
|
)
|
|
304
329
|
|
|
305
|
-
raise TypeConfigException(
|
|
330
|
+
raise TypeConfigException(
|
|
331
|
+
f"expected type {clazz} at {path_to_str(path)}, got {type(value)}"
|
|
332
|
+
)
|
|
306
333
|
|
|
307
334
|
|
|
308
335
|
def __generate(value: object, path: str):
|
|
@@ -399,6 +426,10 @@ def __env_vars_parse(prefix: str, obj: Dict[str, Any]):
|
|
|
399
426
|
return ret
|
|
400
427
|
|
|
401
428
|
|
|
429
|
+
def path_to_str(path: str) -> str:
|
|
430
|
+
return path if len(path) > 0 else "root"
|
|
431
|
+
|
|
432
|
+
|
|
402
433
|
def __cli_parse(argv: List[str]):
|
|
403
434
|
kvs = {}
|
|
404
435
|
i = 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "dataconf"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.6.0"
|
|
4
4
|
description = "Simple dataclasses configuration management for Python with hocon/json/yaml/properties/env-vars/dict support."
|
|
5
5
|
authors = []
|
|
6
6
|
license = "MPL-2.0"
|
|
@@ -35,20 +35,20 @@ dataconf = 'dataconf.cli:run'
|
|
|
35
35
|
python = "^3.9"
|
|
36
36
|
python-dateutil = "^2.9.0.post0"
|
|
37
37
|
PyYAML = "^6.0.2"
|
|
38
|
-
isodate = "
|
|
38
|
+
isodate = "^0.7.2"
|
|
39
39
|
pyhocon = "^0.3.61"
|
|
40
|
-
pyparsing = "^3.2.
|
|
40
|
+
pyparsing = "^3.2.3"
|
|
41
41
|
|
|
42
42
|
[tool.poetry.group.dev.dependencies]
|
|
43
|
-
pytest = ">=8.
|
|
43
|
+
pytest = ">=8.4.1"
|
|
44
44
|
pre-commit = ">=4.2.0"
|
|
45
|
-
commitizen = ">=4.
|
|
46
|
-
pytest-httpserver = "^1.1.
|
|
47
|
-
ruff = ">=0.
|
|
45
|
+
commitizen = ">=4.8.3"
|
|
46
|
+
pytest-httpserver = "^1.1.3"
|
|
47
|
+
ruff = ">=0.12.7"
|
|
48
48
|
|
|
49
49
|
[tool.commitizen]
|
|
50
50
|
name = "cz_conventional_commits"
|
|
51
|
-
version = "3.
|
|
51
|
+
version = "3.6.0"
|
|
52
52
|
tag_format = "v$major.$minor.$patch$prerelease"
|
|
53
53
|
version_files = [
|
|
54
54
|
"pyproject.toml:version",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|