confarg 0.0.1.dev4__tar.gz → 0.0.1.dev5__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.
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/PKG-INFO +47 -14
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/README.md +43 -11
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/pyproject.toml +4 -3
- confarg-0.0.1.dev5/src/confarg/__init__.py +33 -0
- confarg-0.0.1.dev4/src/confarg/__init__.py → confarg-0.0.1.dev5/src/confarg/_api.py +120 -135
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/_callable.py +43 -76
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/_defaults.py +11 -1
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/_files.py +24 -10
- confarg-0.0.1.dev5/src/confarg/_import.py +40 -0
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/_merge.py +30 -16
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/_parse_cli.py +34 -21
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/_parse_env.py +4 -4
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/_serialize.py +2 -3
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/_types.py +4 -37
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/__init__.py +1 -1
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/argparse/__init__.py +1 -1
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/argparse/_build.py +76 -65
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/argparse/_completion.py +129 -95
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/argparse/_namespace.py +42 -38
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/argparse/_register.py +4 -4
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/argparse/_spec.py +3 -5
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/click/__init__.py +7 -1
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/click/_completion.py +6 -3
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/click/_context.py +5 -16
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/cli/click/_register.py +20 -35
- confarg-0.0.1.dev5/src/confarg/cli/cyclopts/__init__.py +20 -0
- confarg-0.0.1.dev5/src/confarg/cli/cyclopts/_context.py +137 -0
- confarg-0.0.1.dev5/src/confarg/cli/cyclopts/_register.py +173 -0
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/dictexpr/__init__.py +3 -3
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/dictexpr/_expressions.py +16 -15
- confarg-0.0.1.dev4/src/confarg/_errors.py → confarg-0.0.1.dev5/src/confarg/exceptions.py +8 -2
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/typedload/__init__.py +2 -2
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/typedload/_coerce.py +32 -7
- {confarg-0.0.1.dev4 → confarg-0.0.1.dev5}/src/confarg/typedload/_construct.py +40 -50
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: confarg
|
|
3
|
-
Version: 0.0.1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.0.1.dev5
|
|
4
|
+
Summary: Load and resolve complex configurations from files, environment variables and command line arguments. Keep your favorite CLI library.
|
|
5
5
|
Author: confarg
|
|
6
6
|
Author-email: confarg <280620574+confarg@users.noreply.github.com>
|
|
7
|
+
License-Expression: MPL-2.0
|
|
7
8
|
Requires-Dist: argcomplete>=3.0 ; extra == 'completion'
|
|
8
9
|
Requires-Python: >=3.12
|
|
9
10
|
Project-URL: Documentation, https://confarg.github.io/confarg
|
|
@@ -13,30 +14,62 @@ Description-Content-Type: text/markdown
|
|
|
13
14
|
|
|
14
15
|
# A tool to manage complex configurations
|
|
15
16
|
|
|
16
|
-
> Load and resolve complex configurations from files, environment variables and command line arguments. Keep your favorite CLI library.
|
|
17
|
+
> Load and resolve complex configurations from files, environment variables and command line arguments. Keep your data structures and favorite CLI library.
|
|
17
18
|
|
|
19
|
+
`confarg` is a Python library that helps you load configurations in a modular fashion from multiple sources: files, environment variables, and command line arguments.
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
It can handle deeply nested configurations, type unions, derived classes, expressions and variable interpolation, configuration compositions and more, and can integrate with your favorite argument parser library such as `argparse`, `click` or `typer`.
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
`confarg` is not a framework. No decorator, base class or special annotation type required: none are provided. It is just a tool for the deserialization and serialization of complex configurations. Its footprint in your code is typically a few lines of code, making it easy to switch to it, or away from it.
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
## Install
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
```bash
|
|
28
|
+
pip install confarg
|
|
29
|
+
```
|
|
26
30
|
|
|
27
|
-
`confarg`
|
|
31
|
+
`confarg` comes with no dependency, but installing additional libraries such as `pyyaml` unlocks the support of extra configuration file formats.
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
## TL;DR
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
This library lets you read configurations stored in classes like this
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
```python
|
|
38
|
+
@dataclass
|
|
39
|
+
class Config:
|
|
40
|
+
value: float
|
|
41
|
+
flag: bool
|
|
42
|
+
subconfig: SubConfig1 | SubConfig2
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
with this kind of code
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
config = confarg.load(Config)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
which lets you build a configuration object from files,
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
value: 1.0
|
|
55
|
+
flag: false
|
|
56
|
+
subconfig:
|
|
57
|
+
foo: 42
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
but also and simultaneously from environment variables,
|
|
61
|
+
|
|
62
|
+
```properties
|
|
63
|
+
MYAPP_VALUE=0.0
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
and command line arguments,
|
|
34
67
|
|
|
35
68
|
```bash
|
|
36
|
-
|
|
69
|
+
myapp --subconfig.foo=33
|
|
37
70
|
```
|
|
38
71
|
|
|
39
|
-
|
|
72
|
+
Still here? Read along, we are just getting started.
|
|
40
73
|
|
|
41
74
|
## Getting started
|
|
42
75
|
|
|
@@ -1,29 +1,61 @@
|
|
|
1
1
|
# A tool to manage complex configurations
|
|
2
2
|
|
|
3
|
-
> Load and resolve complex configurations from files, environment variables and command line arguments. Keep your favorite CLI library.
|
|
3
|
+
> Load and resolve complex configurations from files, environment variables and command line arguments. Keep your data structures and favorite CLI library.
|
|
4
4
|
|
|
5
|
+
`confarg` is a Python library that helps you load configurations in a modular fashion from multiple sources: files, environment variables, and command line arguments.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
It can handle deeply nested configurations, type unions, derived classes, expressions and variable interpolation, configuration compositions and more, and can integrate with your favorite argument parser library such as `argparse`, `click` or `typer`.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
`confarg` is not a framework. No decorator, base class or special annotation type required: none are provided. It is just a tool for the deserialization and serialization of complex configurations. Its footprint in your code is typically a few lines of code, making it easy to switch to it, or away from it.
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
## Install
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
```bash
|
|
14
|
+
pip install confarg
|
|
15
|
+
```
|
|
13
16
|
|
|
14
|
-
`confarg`
|
|
17
|
+
`confarg` comes with no dependency, but installing additional libraries such as `pyyaml` unlocks the support of extra configuration file formats.
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
## TL;DR
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
This library lets you read configurations stored in classes like this
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
```python
|
|
24
|
+
@dataclass
|
|
25
|
+
class Config:
|
|
26
|
+
value: float
|
|
27
|
+
flag: bool
|
|
28
|
+
subconfig: SubConfig1 | SubConfig2
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
with this kind of code
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
config = confarg.load(Config)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
which lets you build a configuration object from files,
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
value: 1.0
|
|
41
|
+
flag: false
|
|
42
|
+
subconfig:
|
|
43
|
+
foo: 42
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
but also and simultaneously from environment variables,
|
|
47
|
+
|
|
48
|
+
```properties
|
|
49
|
+
MYAPP_VALUE=0.0
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
and command line arguments,
|
|
21
53
|
|
|
22
54
|
```bash
|
|
23
|
-
|
|
55
|
+
myapp --subconfig.foo=33
|
|
24
56
|
```
|
|
25
57
|
|
|
26
|
-
|
|
58
|
+
Still here? Read along, we are just getting started.
|
|
27
59
|
|
|
28
60
|
## Getting started
|
|
29
61
|
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
authors = [
|
|
3
3
|
{ name = "confarg", email = "280620574+confarg@users.noreply.github.com" },
|
|
4
4
|
]
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
description = "Load and resolve complex configurations from files, environment variables and command line arguments. Keep your favorite CLI library."
|
|
6
|
+
license = "MPL-2.0"
|
|
7
7
|
name = "confarg"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
|
-
version = "0.0.1.
|
|
10
|
+
version = "0.0.1.dev5"
|
|
11
11
|
|
|
12
12
|
[project.urls]
|
|
13
13
|
Documentation = "https://confarg.github.io/confarg"
|
|
@@ -31,6 +31,7 @@ dev = [
|
|
|
31
31
|
"pytest>=8.0",
|
|
32
32
|
"pyyaml>=6.0",
|
|
33
33
|
"tomli-w>=1.0",
|
|
34
|
+
"ty>=0.0.39",
|
|
34
35
|
"typer>=0.25.1",
|
|
35
36
|
"zensical>=0.0.41",
|
|
36
37
|
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
"""A tool to manage complex configurations.
|
|
6
|
+
|
|
7
|
+
> Load and resolve complex configurations from files, environment variables and command line arguments. Keep your data
|
|
8
|
+
> structures and favorite CLI library.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from confarg import exceptions
|
|
14
|
+
from confarg._api import build, dump, dump_file, from_dict, load, merge, resolve
|
|
15
|
+
from confarg._types import TagPolicy
|
|
16
|
+
|
|
17
|
+
__all__ = [ # noqa: RUF022
|
|
18
|
+
# Two-step API
|
|
19
|
+
"merge",
|
|
20
|
+
"build",
|
|
21
|
+
# Three-step API (dict-centric)
|
|
22
|
+
"resolve",
|
|
23
|
+
"from_dict",
|
|
24
|
+
# One-step convenience
|
|
25
|
+
"load",
|
|
26
|
+
# Dump
|
|
27
|
+
"dump",
|
|
28
|
+
"dump_file",
|
|
29
|
+
# Types
|
|
30
|
+
"TagPolicy",
|
|
31
|
+
# Exceptions / warnings
|
|
32
|
+
"exceptions",
|
|
33
|
+
]
|
|
@@ -2,40 +2,31 @@
|
|
|
2
2
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
|
|
5
|
-
"""
|
|
5
|
+
"""Public API implementation."""
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
10
|
import sys
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from typing import TYPE_CHECKING, Any
|
|
12
|
+
from typing import TYPE_CHECKING, Any, overload
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
15
|
from collections.abc import Mapping, Sequence
|
|
16
|
+
from types import UnionType
|
|
17
|
+
from typing import TypeAliasType
|
|
16
18
|
|
|
17
19
|
from confarg import _defaults
|
|
18
|
-
from confarg.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ConfargError,
|
|
22
|
-
ConfargWarning,
|
|
23
|
-
ExpressionEvalError,
|
|
24
|
-
InvalidConfigFileError,
|
|
25
|
-
MissingFieldError,
|
|
26
|
-
MissingReferenceError,
|
|
27
|
-
SymbolImportError,
|
|
28
|
-
TypeCoercionError,
|
|
29
|
-
UnknownArgumentError,
|
|
30
|
-
UnsafeExpressionError,
|
|
31
|
-
)
|
|
32
|
-
from confarg._files import INCLUDE_KEY, _dump_file, _load_file, _load_file_item
|
|
33
|
-
from confarg._merge import LIST_APPEND_KEY, _deep_merge
|
|
20
|
+
from confarg._files import _dump_file, _load_file, _load_file_item, _load_subpath_files
|
|
21
|
+
from confarg._merge import LIST_APPEND_KEY as _LIST_APPEND_KEY
|
|
22
|
+
from confarg._merge import _deep_merge
|
|
34
23
|
from confarg._parse_cli import _parse_cli
|
|
35
24
|
from confarg._parse_env import _parse_env
|
|
36
25
|
from confarg._serialize import _serialize
|
|
37
26
|
from confarg._types import _MISSING, TagPolicy, _is_dc, _is_struct, _is_struct_like, _resolve_type
|
|
27
|
+
from confarg._types import _StrToken as _ST
|
|
38
28
|
from confarg.dictexpr import resolve_expressions
|
|
29
|
+
from confarg.exceptions import ConfargError, MissingFieldError
|
|
39
30
|
from confarg.typedload import construct as _tc
|
|
40
31
|
|
|
41
32
|
|
|
@@ -54,7 +45,7 @@ def _load_cli_config(fpath: Path, subpath: str, config_flag: str) -> dict[str, A
|
|
|
54
45
|
append_items = fitem[last_key]
|
|
55
46
|
else:
|
|
56
47
|
append_items = [fitem]
|
|
57
|
-
fdata: dict[str, Any] = {
|
|
48
|
+
fdata: dict[str, Any] = {_LIST_APPEND_KEY: append_items}
|
|
58
49
|
for part in reversed(real_subpath.split(".")):
|
|
59
50
|
fdata = {part: fdata}
|
|
60
51
|
return fdata
|
|
@@ -67,7 +58,7 @@ def _load_cli_config(fpath: Path, subpath: str, config_flag: str) -> dict[str, A
|
|
|
67
58
|
|
|
68
59
|
|
|
69
60
|
def merge( # noqa: PLR0913
|
|
70
|
-
target: type,
|
|
61
|
+
target: type | TypeAliasType | UnionType,
|
|
71
62
|
*,
|
|
72
63
|
args: Sequence[str] | None = None,
|
|
73
64
|
env: Mapping[str, str] | None = None,
|
|
@@ -77,7 +68,7 @@ def merge( # noqa: PLR0913
|
|
|
77
68
|
config_flag: str = "config",
|
|
78
69
|
files: Sequence[str | Path] = (),
|
|
79
70
|
env_config: str | None = None,
|
|
80
|
-
union_tag: str =
|
|
71
|
+
union_tag: str = _defaults.UNION_TAG,
|
|
81
72
|
) -> dict[str, Any]:
|
|
82
73
|
"""Collect and merge configuration from all sources into a raw dict.
|
|
83
74
|
|
|
@@ -96,12 +87,18 @@ def merge( # noqa: PLR0913
|
|
|
96
87
|
to read all env vars without filtering, or to e.g. ``"MYAPP_"`` to
|
|
97
88
|
read only vars with that prefix.
|
|
98
89
|
env_separator: Separator used to split env var names into nested keys.
|
|
99
|
-
|
|
100
|
-
|
|
90
|
+
Defaults to ``"__"`` (double underscore).
|
|
91
|
+
cli_prefix: Required prefix for all CLI flags. Defaults to ``""``,
|
|
92
|
+
which means no prefix is required.
|
|
93
|
+
config_flag: Flag name used to specify config files on the CLI
|
|
94
|
+
(``--config path/to/file.yaml``). Set to ``""`` to disable.
|
|
95
|
+
Defaults to ``"config"``.
|
|
101
96
|
files: Paths to config files to load.
|
|
102
97
|
env_config: Name of an env var whose value is a config file path to load.
|
|
103
98
|
Loaded after ``files`` but before CLI ``--config`` files.
|
|
104
|
-
union_tag:
|
|
99
|
+
union_tag: Field name used as a discriminator tag in union types.
|
|
100
|
+
Defaults to ``"class"`` — a Python keyword that can never clash
|
|
101
|
+
with a dataclass field name.
|
|
105
102
|
|
|
106
103
|
Returns:
|
|
107
104
|
A plain dict of the merged configuration, with expression strings intact.
|
|
@@ -141,20 +138,11 @@ def merge( # noqa: PLR0913
|
|
|
141
138
|
env_data, env_configs = _parse_env(env_for_fields, env_prefix, env_separator, target, config_flag)
|
|
142
139
|
|
|
143
140
|
# 3. Load config files in priority order (all become config-level, below inline env/CLI)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if env_config is not None:
|
|
148
|
-
env_config_path = env.get(env_config)
|
|
149
|
-
if env_config_path:
|
|
150
|
-
config_data = _deep_merge(config_data, _load_file(Path(env_config_path)), union_tag=union_tag)
|
|
141
|
+
file_entries: list[tuple[str, Path]] = [("", Path(f)) for f in files]
|
|
142
|
+
if env_config and (env_config_path := env.get(env_config)):
|
|
143
|
+
file_entries.append(("", Path(env_config_path)))
|
|
151
144
|
env_configs.sort(key=lambda ec: ec[0])
|
|
152
|
-
|
|
153
|
-
fdata: dict[str, Any] = _load_file(fpath)
|
|
154
|
-
if subpath:
|
|
155
|
-
for part in reversed(subpath.split(".")):
|
|
156
|
-
fdata = {part: fdata}
|
|
157
|
-
config_data = _deep_merge(config_data, fdata, union_tag=union_tag)
|
|
145
|
+
config_data = _load_subpath_files(file_entries + env_configs, union_tag)
|
|
158
146
|
for subpath, fpath in cli_configs:
|
|
159
147
|
fdata = _load_cli_config(fpath, subpath, config_flag)
|
|
160
148
|
config_data = _deep_merge(config_data, fdata, union_tag=union_tag)
|
|
@@ -164,21 +152,24 @@ def merge( # noqa: PLR0913
|
|
|
164
152
|
return _deep_merge(merged, cli_data, union_tag=union_tag)
|
|
165
153
|
|
|
166
154
|
|
|
167
|
-
|
|
168
|
-
|
|
155
|
+
@overload
|
|
156
|
+
def build[T](target: type[T], data: dict[str, Any], *, union_tag: str = ...) -> T: ...
|
|
157
|
+
@overload
|
|
158
|
+
def build(target: object, data: dict[str, Any], *, union_tag: str = ...) -> Any: ...
|
|
159
|
+
def build[T](
|
|
160
|
+
target: type[T] | TypeAliasType | UnionType,
|
|
169
161
|
data: dict[str, Any],
|
|
170
162
|
*,
|
|
171
163
|
union_tag: str = "class",
|
|
172
164
|
) -> T:
|
|
173
|
-
"""
|
|
165
|
+
"""Resolve ``${...}`` expressions and construct the target type from a merged config dict.
|
|
174
166
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
have assembled yourself.
|
|
167
|
+
Use this as the second step after ``merge()``, or to load configuration
|
|
168
|
+
from a dict you have assembled yourself.
|
|
178
169
|
|
|
179
170
|
Args:
|
|
180
171
|
target: The dataclass type (or scalar type) to construct.
|
|
181
|
-
data: The raw config dict (e.g. the output of merge()).
|
|
172
|
+
data: The raw config dict (e.g. the output of ``merge()``).
|
|
182
173
|
union_tag: The field name used as a discriminator tag in unions.
|
|
183
174
|
|
|
184
175
|
Returns:
|
|
@@ -206,22 +197,26 @@ def from_dict[T](
|
|
|
206
197
|
" Provide a value via CLI flag (--<prefix> <value>), environment variable, or config file."
|
|
207
198
|
)
|
|
208
199
|
raise MissingFieldError(msg)
|
|
209
|
-
return _tc(target_r, raw, union_tag=union_tag)
|
|
200
|
+
return _tc(target_r, raw, union_tag=union_tag)
|
|
201
|
+
return _tc(target_r, resolved, union_tag=union_tag)
|
|
210
202
|
|
|
211
|
-
return _tc(target_r, resolved, union_tag=union_tag) # type: ignore[return-value]
|
|
212
203
|
|
|
204
|
+
def resolve(data: dict[str, Any]) -> dict[str, Any]:
|
|
205
|
+
"""Resolve ``${...}`` expressions in a merged config dict.
|
|
213
206
|
|
|
214
|
-
|
|
215
|
-
|
|
207
|
+
Use this between ``merge()`` and ``from_dict()`` when you need the
|
|
208
|
+
resolved dict itself (e.g. to inspect values or write it to a file):
|
|
216
209
|
|
|
217
|
-
|
|
218
|
-
|
|
210
|
+
raw = confarg.merge(MyConfig, ...)
|
|
211
|
+
resolved = confarg.resolve(raw)
|
|
212
|
+
confarg.dump_file(resolved, "out.yaml")
|
|
213
|
+
cfg = confarg.from_dict(MyConfig, resolved)
|
|
219
214
|
|
|
220
215
|
Args:
|
|
221
|
-
data: A plain config dict, e.g. the output of merge()
|
|
216
|
+
data: A plain config dict, e.g. the output of ``merge()``.
|
|
222
217
|
|
|
223
218
|
Returns:
|
|
224
|
-
A new dict with all
|
|
219
|
+
A new dict with all ``${...}`` expression strings replaced by their values.
|
|
225
220
|
|
|
226
221
|
Raises:
|
|
227
222
|
CircularReferenceError: If expression references form a cycle.
|
|
@@ -232,28 +227,24 @@ def interpolate(data: dict[str, Any]) -> dict[str, Any]:
|
|
|
232
227
|
return resolve_expressions(data)
|
|
233
228
|
|
|
234
229
|
|
|
235
|
-
|
|
236
|
-
|
|
230
|
+
@overload
|
|
231
|
+
def from_dict[T](target: type[T], data: dict[str, Any], *, union_tag: str = ...) -> T: ...
|
|
232
|
+
@overload
|
|
233
|
+
def from_dict(target: object, data: dict[str, Any], *, union_tag: str = ...) -> Any: ...
|
|
234
|
+
def from_dict[T](
|
|
235
|
+
target: type[T] | TypeAliasType | UnionType,
|
|
237
236
|
data: dict[str, Any],
|
|
238
237
|
*,
|
|
239
238
|
union_tag: str = "class",
|
|
240
239
|
) -> T:
|
|
241
|
-
"""Construct a typed object from an already-
|
|
240
|
+
"""Construct a typed object from an already-resolved config dict.
|
|
242
241
|
|
|
243
|
-
|
|
244
|
-
resolve
|
|
245
|
-
|
|
246
|
-
Use this together with interpolate() when you want to keep the interpolated
|
|
247
|
-
dict around (e.g. to dump it with dump_file()):
|
|
248
|
-
|
|
249
|
-
raw = confarg.merge(MyConfig, ...)
|
|
250
|
-
resolved = confarg.interpolate(raw)
|
|
251
|
-
confarg.dump_file(resolved, "out.yaml") # serialize the dict
|
|
252
|
-
cfg = confarg.construct(MyConfig, resolved) # build the typed object
|
|
242
|
+
Unlike ``build()``, this does NOT resolve ``${...}`` expressions — call
|
|
243
|
+
``resolve()`` first if needed.
|
|
253
244
|
|
|
254
245
|
Args:
|
|
255
246
|
target: The dataclass or plain-class type to construct.
|
|
256
|
-
data:
|
|
247
|
+
data: A resolved config dict (output of resolve() or merge()).
|
|
257
248
|
union_tag: The field name used as a discriminator tag in unions.
|
|
258
249
|
|
|
259
250
|
Returns:
|
|
@@ -265,12 +256,40 @@ def construct[T](
|
|
|
265
256
|
AmbiguousUnionError: If a Union cannot be disambiguated.
|
|
266
257
|
"""
|
|
267
258
|
target_r = _resolve_type(target)
|
|
268
|
-
return _tc(target_r, data, union_tag=union_tag)
|
|
259
|
+
return _tc(target_r, data, union_tag=union_tag)
|
|
269
260
|
|
|
270
261
|
|
|
271
|
-
|
|
262
|
+
@overload
|
|
263
|
+
def load[T](
|
|
272
264
|
target: type[T],
|
|
273
265
|
*,
|
|
266
|
+
args: Sequence[str] | None = ...,
|
|
267
|
+
env: Mapping[str, str] | None = ...,
|
|
268
|
+
env_prefix: str | None = ...,
|
|
269
|
+
env_separator: str = ...,
|
|
270
|
+
cli_prefix: str = ...,
|
|
271
|
+
config_flag: str = ...,
|
|
272
|
+
files: Sequence[str | Path] = ...,
|
|
273
|
+
env_config: str | None = ...,
|
|
274
|
+
union_tag: str = ...,
|
|
275
|
+
) -> T: ...
|
|
276
|
+
@overload
|
|
277
|
+
def load(
|
|
278
|
+
target: object,
|
|
279
|
+
*,
|
|
280
|
+
args: Sequence[str] | None = ...,
|
|
281
|
+
env: Mapping[str, str] | None = ...,
|
|
282
|
+
env_prefix: str | None = ...,
|
|
283
|
+
env_separator: str = ...,
|
|
284
|
+
cli_prefix: str = ...,
|
|
285
|
+
config_flag: str = ...,
|
|
286
|
+
files: Sequence[str | Path] = ...,
|
|
287
|
+
env_config: str | None = ...,
|
|
288
|
+
union_tag: str = ...,
|
|
289
|
+
) -> Any: ...
|
|
290
|
+
def load[T]( # noqa: PLR0913
|
|
291
|
+
target: type[T] | TypeAliasType | UnionType,
|
|
292
|
+
*,
|
|
274
293
|
args: Sequence[str] | None = None,
|
|
275
294
|
env: Mapping[str, str] | None = None,
|
|
276
295
|
env_prefix: str | None = _defaults.ENV_PREFIX,
|
|
@@ -279,16 +298,13 @@ def load[T]( # noqa: PLR0913
|
|
|
279
298
|
config_flag: str = "config",
|
|
280
299
|
files: Sequence[str | Path] = (),
|
|
281
300
|
env_config: str | None = None,
|
|
282
|
-
union_tag: str =
|
|
301
|
+
union_tag: str = _defaults.UNION_TAG,
|
|
283
302
|
) -> T:
|
|
284
|
-
"""
|
|
285
|
-
|
|
286
|
-
Sources are merged in priority order: config files (lowest), then
|
|
287
|
-
environment variables, then CLI arguments (highest).
|
|
303
|
+
"""Merge configuration from all sources and construct the target type.
|
|
288
304
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
305
|
+
Convenience wrapper for ``merge()`` + ``build()``. For more control —
|
|
306
|
+
e.g. to inspect or save the raw merged dict — call those directly.
|
|
307
|
+
See ``merge()`` for source priority and config file loading order.
|
|
292
308
|
|
|
293
309
|
Args:
|
|
294
310
|
target: The dataclass type (or scalar type) to load configuration into.
|
|
@@ -299,19 +315,22 @@ def load[T]( # noqa: PLR0913
|
|
|
299
315
|
to read all env vars without filtering, or to e.g. ``"MYAPP_"`` to
|
|
300
316
|
read only vars with that prefix.
|
|
301
317
|
env_separator: Separator used to split env var names into nested keys.
|
|
302
|
-
|
|
303
|
-
|
|
318
|
+
Defaults to ``"__"`` (double underscore).
|
|
319
|
+
cli_prefix: Required prefix for all CLI flags. Defaults to ``""``,
|
|
320
|
+
which means no prefix is required.
|
|
321
|
+
config_flag: Flag name used to specify config files on the CLI
|
|
322
|
+
(``--config path/to/file.yaml``). Set to ``""`` to disable.
|
|
323
|
+
Defaults to ``"config"``.
|
|
304
324
|
files: Paths to config files to load.
|
|
305
325
|
env_config: Name of an env var whose value is a config file path to load.
|
|
306
326
|
Loaded after ``files`` but before CLI ``--config`` files.
|
|
307
|
-
union_tag:
|
|
327
|
+
union_tag: Field name used as a discriminator tag in union types.
|
|
328
|
+
Defaults to ``"class"`` — a Python keyword that can never clash
|
|
329
|
+
with a dataclass field name.
|
|
308
330
|
|
|
309
331
|
Returns:
|
|
310
332
|
An instance of the target type populated with the merged configuration.
|
|
311
333
|
|
|
312
|
-
Config file loading order:
|
|
313
|
-
See ``merge()`` for the full description of config file loading order.
|
|
314
|
-
|
|
315
334
|
Raises:
|
|
316
335
|
MissingFieldError: If a required field is not provided by any source.
|
|
317
336
|
TypeCoercionError: If a value cannot be coerced to the target type.
|
|
@@ -335,13 +354,11 @@ def load[T]( # noqa: PLR0913
|
|
|
335
354
|
env_config=env_config,
|
|
336
355
|
union_tag=union_tag,
|
|
337
356
|
)
|
|
338
|
-
return
|
|
357
|
+
return build(target, data, union_tag=union_tag)
|
|
339
358
|
|
|
340
359
|
|
|
341
360
|
def _strip_str_tokens(value: Any) -> Any:
|
|
342
361
|
"""Recursively convert _StrToken instances to plain str for serialization."""
|
|
343
|
-
from confarg._types import _StrToken as _ST
|
|
344
|
-
|
|
345
362
|
if type(value) is _ST:
|
|
346
363
|
return str(value)
|
|
347
364
|
if isinstance(value, dict):
|
|
@@ -357,28 +374,25 @@ def dump(
|
|
|
357
374
|
union_tag: str = "class",
|
|
358
375
|
tag_policy: TagPolicy = "auto",
|
|
359
376
|
) -> dict[str, Any]:
|
|
360
|
-
"""Serialize to a plain dict.
|
|
361
|
-
|
|
362
|
-
Dispatches on the value type:
|
|
363
|
-
|
|
364
|
-
- **Dataclass instance**: serializes to a config-compatible dict.
|
|
365
|
-
``union_tag`` and ``tag_policy`` apply.
|
|
366
|
-
- **Raw dict** (e.g. from ``merge()``): normalizes internal tokens to plain
|
|
367
|
-
``str``. ``union_tag`` and ``tag_policy`` are ignored.
|
|
377
|
+
"""Serialize a dataclass instance to a config-compatible plain dict.
|
|
368
378
|
|
|
369
379
|
Args:
|
|
370
|
-
value: A dataclass instance
|
|
380
|
+
value: A dataclass instance.
|
|
371
381
|
union_tag: The field name used as a discriminator tag in unions.
|
|
372
|
-
tag_policy: "auto" (tag only when needed) or "always" (tag every union
|
|
382
|
+
tag_policy: ``"auto"`` (tag only when needed) or ``"always"`` (tag every union member).
|
|
373
383
|
|
|
374
384
|
Returns:
|
|
375
385
|
A plain dict representation.
|
|
376
386
|
|
|
377
387
|
Raises:
|
|
378
|
-
TypeError: If value is not a dataclass instance
|
|
388
|
+
TypeError: If value is not a dataclass instance.
|
|
379
389
|
"""
|
|
380
390
|
if isinstance(value, dict):
|
|
381
|
-
|
|
391
|
+
msg = (
|
|
392
|
+
"dump() takes a dataclass instance, not a dict."
|
|
393
|
+
" To write a raw config dict to a file, use dump_file() directly."
|
|
394
|
+
)
|
|
395
|
+
raise TypeError(msg)
|
|
382
396
|
if isinstance(value, type) or not _is_dc(type(value)):
|
|
383
397
|
tp_name = type(value).__name__
|
|
384
398
|
if _is_struct(type(value)):
|
|
@@ -389,7 +403,7 @@ def dump(
|
|
|
389
403
|
f" confarg.dump_file(raw, path)"
|
|
390
404
|
)
|
|
391
405
|
raise TypeError(msg)
|
|
392
|
-
msg = f"Expected a dataclass instance
|
|
406
|
+
msg = f"Expected a dataclass instance, got {tp_name}"
|
|
393
407
|
raise TypeError(msg)
|
|
394
408
|
tp = type(value)
|
|
395
409
|
return _serialize(tp, value, "", union_tag, tag_policy)
|
|
@@ -402,52 +416,23 @@ def dump_file(
|
|
|
402
416
|
union_tag: str = "class",
|
|
403
417
|
tag_policy: TagPolicy = "auto",
|
|
404
418
|
) -> None:
|
|
405
|
-
"""
|
|
419
|
+
"""Serialize and write to a config file.
|
|
406
420
|
|
|
407
|
-
Accepts dataclass
|
|
408
|
-
|
|
421
|
+
Accepts a dataclass instance or a raw config dict (e.g. from ``merge()``
|
|
422
|
+
or ``resolve()``). The output format is determined by the file extension
|
|
409
423
|
(.toml, .yaml, .yml, .json).
|
|
410
424
|
|
|
411
425
|
Args:
|
|
412
426
|
value: A dataclass instance or a raw config dict.
|
|
413
427
|
path: Path to the output file.
|
|
414
428
|
union_tag: The field name used as a discriminator tag in unions.
|
|
415
|
-
tag_policy: "auto" or "always"
|
|
429
|
+
tag_policy: ``"auto"`` or ``"always"``.
|
|
416
430
|
|
|
417
431
|
Raises:
|
|
418
432
|
TypeError: If value is not a dataclass instance or a dict.
|
|
419
433
|
InvalidConfigFileError: If the format is unsupported or the required library is not installed.
|
|
420
434
|
"""
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
# Two-step API
|
|
426
|
-
"merge",
|
|
427
|
-
"from_dict",
|
|
428
|
-
# Three-step API (dict-centric)
|
|
429
|
-
"interpolate",
|
|
430
|
-
"construct",
|
|
431
|
-
# One-step convenience
|
|
432
|
-
"load",
|
|
433
|
-
# Dump
|
|
434
|
-
"dump",
|
|
435
|
-
"dump_file",
|
|
436
|
-
# Types
|
|
437
|
-
"TagPolicy",
|
|
438
|
-
# Errors / warnings
|
|
439
|
-
"ConfargError",
|
|
440
|
-
"ConfargWarning",
|
|
441
|
-
"MissingFieldError",
|
|
442
|
-
"SymbolImportError",
|
|
443
|
-
"TypeCoercionError",
|
|
444
|
-
"InvalidConfigFileError",
|
|
445
|
-
"UnknownArgumentError",
|
|
446
|
-
"AmbiguousUnionError",
|
|
447
|
-
"CircularReferenceError",
|
|
448
|
-
"MissingReferenceError",
|
|
449
|
-
"UnsafeExpressionError",
|
|
450
|
-
"ExpressionEvalError",
|
|
451
|
-
# Constants
|
|
452
|
-
"INCLUDE_KEY",
|
|
453
|
-
]
|
|
435
|
+
if isinstance(value, dict):
|
|
436
|
+
_dump_file(_strip_str_tokens(value), Path(path))
|
|
437
|
+
else:
|
|
438
|
+
_dump_file(dump(value, union_tag=union_tag, tag_policy=tag_policy), Path(path))
|