nested-config 2.1.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.
- nested_config-2.1.3/.gitignore +5 -0
- nested_config-2.1.3/.gitlab-ci.yml +77 -0
- nested_config-2.1.3/CHANGELOG.md +102 -0
- nested_config-2.1.3/LICENSE +21 -0
- nested_config-2.1.3/PKG-INFO +290 -0
- nested_config-2.1.3/README.md +258 -0
- nested_config-2.1.3/dev/.gitignore +2 -0
- nested_config-2.1.3/dev/build_and_publish.sh +33 -0
- nested_config-2.1.3/dev/mypy.sh +15 -0
- nested_config-2.1.3/dev/ruff.sh +8 -0
- nested_config-2.1.3/dev/run_tests.sh +43 -0
- nested_config-2.1.3/dev/test_setup.sh +36 -0
- nested_config-2.1.3/dev/test_vers.sh +3 -0
- nested_config-2.1.3/poetry.lock +662 -0
- nested_config-2.1.3/pyproject.toml +67 -0
- nested_config-2.1.3/src/nested_config/__init__.py +21 -0
- nested_config-2.1.3/src/nested_config/_pydantic.py +192 -0
- nested_config-2.1.3/src/nested_config/_types.py +19 -0
- nested_config-2.1.3/src/nested_config/expand.py +220 -0
- nested_config-2.1.3/src/nested_config/loaders.py +99 -0
- nested_config-2.1.3/src/nested_config/py.typed +0 -0
- nested_config-2.1.3/src/nested_config/version.py +10 -0
- nested_config-2.1.3/tests/test_future_annotations.py +64 -0
- nested_config-2.1.3/tests/test_nc_BaseModel.py +23 -0
- nested_config-2.1.3/tests/test_pydantic_purepath.py +68 -0
- nested_config-2.1.3/tests/test_simple_files.py +23 -0
- nested_config-2.1.3/tests/test_sub_model.py +132 -0
- nested_config-2.1.3/tests/toml_files/garage.toml +2 -0
- nested_config-2.1.3/tests/toml_files/house.toml +2 -0
- nested_config-2.1.3/tests/toml_files/house_bad_dimpath.toml +2 -0
- nested_config-2.1.3/tests/toml_files/house_dictdim.toml +4 -0
- nested_config-2.1.3/tests/toml_files/house_listdim.toml +2 -0
- nested_config-2.1.3/tests/toml_files/house_with_garage.toml +3 -0
- nested_config-2.1.3/tests/toml_files/neighborhood.toml +20 -0
- nested_config-2.1.3/tests/toml_files/simple_house.toml +3 -0
- nested_config-2.1.3/tests/toml_files/subdir/garage_dimensions.toml +3 -0
- nested_config-2.1.3/tests/toml_files/subdir/house_dimensions.toml +3 -0
- nested_config-2.1.3/tests/yaml_files/garage.yaml +2 -0
- nested_config-2.1.3/tests/yaml_files/house.yaml +2 -0
- nested_config-2.1.3/tests/yaml_files/house_bad_dimpath.yaml +2 -0
- nested_config-2.1.3/tests/yaml_files/house_dictdim.yaml +4 -0
- nested_config-2.1.3/tests/yaml_files/house_listdim.yaml +4 -0
- nested_config-2.1.3/tests/yaml_files/house_with_garage.yaml +3 -0
- nested_config-2.1.3/tests/yaml_files/simple_house.yaml +3 -0
- nested_config-2.1.3/tests/yaml_files/subdir/garage_dimensions.yaml +3 -0
- nested_config-2.1.3/tests/yaml_files/subdir/house_dimensions.yaml +3 -0
- nested_config-2.1.3/uv.lock +717 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# This file is a template, and might need editing before it works on your project.
|
|
2
|
+
# This is a sample GitLab CI/CD configuration file that should run without any modifications.
|
|
3
|
+
# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
|
|
4
|
+
# it uses echo commands to simulate the pipeline execution.
|
|
5
|
+
#
|
|
6
|
+
# A pipeline is composed of independent jobs that run scripts, grouped into stages.
|
|
7
|
+
# Stages run in sequential order, but jobs within stages run in parallel.
|
|
8
|
+
#
|
|
9
|
+
# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
|
|
10
|
+
#
|
|
11
|
+
# You can copy and paste this template into a new `.gitlab-ci.yml` file.
|
|
12
|
+
# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
|
|
13
|
+
#
|
|
14
|
+
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
|
15
|
+
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
|
16
|
+
# This specific template is located at:
|
|
17
|
+
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
|
|
18
|
+
|
|
19
|
+
variables:
|
|
20
|
+
UV_VERSION: 0.5.20
|
|
21
|
+
BASE_LAYER: bookworm-slim
|
|
22
|
+
UV_RUN_NS: uv run --no-sync
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
stages:
|
|
26
|
+
- test
|
|
27
|
+
|
|
28
|
+
default:
|
|
29
|
+
image: ghcr.io/astral-sh/uv:$UV_VERSION-python3.12-$BASE_LAYER
|
|
30
|
+
|
|
31
|
+
before_script:
|
|
32
|
+
- echo "UV_RUN_NS means '$UV_RUN_NS'"
|
|
33
|
+
- uv venv
|
|
34
|
+
- uv sync --all-extras
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
pytest_38_oldest_pyd18:
|
|
38
|
+
stage: test
|
|
39
|
+
image: ghcr.io/astral-sh/uv:$UV_VERSION-python3.8-$BASE_LAYER
|
|
40
|
+
script:
|
|
41
|
+
# backtrack to all older versions
|
|
42
|
+
- uv pip install typing_extensions==4.6 pydantic==1.8 tomli==2.0.0 pyyaml==5.1
|
|
43
|
+
- $UV_RUN_NS mypy --always-true PYDANTIC_1 ./
|
|
44
|
+
- $UV_RUN_NS pytest
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
pytest_38_all_latest:
|
|
48
|
+
stage: test
|
|
49
|
+
image: ghcr.io/astral-sh/uv:$UV_VERSION-python3.8-$BASE_LAYER
|
|
50
|
+
script:
|
|
51
|
+
- $UV_RUN_NS mypy --always-false PYDANTIC_1 ./
|
|
52
|
+
- $UV_RUN_NS pytest
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
pytest_312_pyd18:
|
|
56
|
+
stage: test
|
|
57
|
+
script:
|
|
58
|
+
# backtrack to older pydantic
|
|
59
|
+
- uv pip install pydantic==1.8
|
|
60
|
+
- $UV_RUN_NS mypy --always-true PYDANTIC_1 ./
|
|
61
|
+
- $UV_RUN_NS pytest
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
pytest_312_pyd110:
|
|
65
|
+
stage: test
|
|
66
|
+
script:
|
|
67
|
+
# backtrack to older pydantic
|
|
68
|
+
- uv pip install pydantic==1.10
|
|
69
|
+
- $UV_RUN_NS mypy --always-true PYDANTIC_1 ./
|
|
70
|
+
- $UV_RUN_NS pytest
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
pytest_312_all_latest:
|
|
74
|
+
stage: test
|
|
75
|
+
script:
|
|
76
|
+
- $UV_RUN_NS mypy --always-false PYDANTIC_1 ./
|
|
77
|
+
- $UV_RUN_NS pytest
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2.1.3] - 2025-01-16
|
|
11
|
+
|
|
12
|
+
- README updated.
|
|
13
|
+
- Switch from poetry/poetry-core to uv/hatchling, including in CI/CI.
|
|
14
|
+
- Replace `setuptools` dependency with `packaging`, since we're using it to get the
|
|
15
|
+
vendored `packaging` anyway.
|
|
16
|
+
|
|
17
|
+
## [2.1.2] - 2024-04-19
|
|
18
|
+
|
|
19
|
+
- Fixed problem where `expand_config` didn't work with PEP 563 stringized annotations. Now
|
|
20
|
+
using `typing.get_type_hints` rather than directly querying `__annotations__`.
|
|
21
|
+
- Added test with `from __future__ import annotations`
|
|
22
|
+
|
|
23
|
+
## [2.1.1] - 2024-04-19
|
|
24
|
+
|
|
25
|
+
- Export ConfigExpansionError in `__init__.py`
|
|
26
|
+
- Fix/update docstrings
|
|
27
|
+
|
|
28
|
+
## [2.1.0] - 2024-04-18
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- `nested_config.expand_config` - Recursively read in any config file(s) into a dict based
|
|
33
|
+
on model class(es). This is now the primary functionality, superseeding
|
|
34
|
+
`validate_config`, which will be deprecated.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Pydantic is now an extra (optional) dependency, only needed to use the below [deprecated
|
|
39
|
+
syntax](#2.1.0-deprecated). All Pydantic stuff has been merged into a single module and
|
|
40
|
+
emits deprecation warnings when used.
|
|
41
|
+
- Improved deb build and test scripts
|
|
42
|
+
|
|
43
|
+
### Deprecated <a name="2.1.0-deprecated"></a>
|
|
44
|
+
|
|
45
|
+
- Pydantic PurePath validator and JSON encoder -- Doesn't need to be part of this project
|
|
46
|
+
and not needed in Pydantic 2+
|
|
47
|
+
- Pydantic validation integration -- This project started out as being specifically for
|
|
48
|
+
working with Pydantic models and was tightly integrated with Pydantic, but that is no
|
|
49
|
+
longer necessary. Use with Pydantic or attrs or whatever is better left to the user.
|
|
50
|
+
|
|
51
|
+
## [2.0.3] - 2024-04-15
|
|
52
|
+
|
|
53
|
+
- Fix typing issue regression for Pydantic < 2.0 introduced in last release
|
|
54
|
+
- Move package to `src` directory
|
|
55
|
+
|
|
56
|
+
## [2.0.2] - 2024-04-12
|
|
57
|
+
|
|
58
|
+
- Generalize handling of lists and dicts such that if the source config value and the
|
|
59
|
+
model annotation are both lists, recursively evaluate each item. This addresses the
|
|
60
|
+
situation where there may be a dict in the source config that corresponds to a Pydantic
|
|
61
|
+
model and that dict contains paths to other configs.
|
|
62
|
+
|
|
63
|
+
## [2.0.1] - 2024-04-10
|
|
64
|
+
|
|
65
|
+
- Make dependency specifications more generous
|
|
66
|
+
- Use `yaml.safe_load`
|
|
67
|
+
- Test minimum dependency versions in CI
|
|
68
|
+
|
|
69
|
+
## [2.0.0] - 2024-04-09
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
|
|
73
|
+
- Project renamed from **pydantic-plus** to **nested-config**
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
77
|
+
- Can find paths to other config files and parse them using their respective Pydantic
|
|
78
|
+
models using `validate_config` or `BaseModel` (this is the main functionality now).
|
|
79
|
+
- Pydantic 2.0 compatibility.
|
|
80
|
+
- Can validate any config file. TOML and JSON built in, YAML optional, others can be
|
|
81
|
+
added.
|
|
82
|
+
- Validators for `PurePath` and `PureWindowsPath`
|
|
83
|
+
- Simplify JSON encoder specification to work for all `PurePaths`
|
|
84
|
+
- pytest and mypy checks, checked with GitLab CI/CD
|
|
85
|
+
|
|
86
|
+
## [1.1.3] - 2021-07-30
|
|
87
|
+
|
|
88
|
+
- Add README
|
|
89
|
+
- Simplify PurePosixPath validator
|
|
90
|
+
- Export `TomlParsingError` from rtoml for downstream exception handling (without needing to explicitly
|
|
91
|
+
import rtoml).
|
|
92
|
+
|
|
93
|
+
[Unreleased]: https://gitlab.com/osu-nrsg/nested-config/-/compare/v2.1.3...master
|
|
94
|
+
[2.1.3]: https://gitlab.com/osu-nrsg/nested-config/-/compare/v2.1.1...v2.1.3
|
|
95
|
+
[2.1.2]: https://gitlab.com/osu-nrsg/nested-config/-/compare/v2.1.1...v2.1.2
|
|
96
|
+
[2.1.1]: https://gitlab.com/osu-nrsg/nested-config/-/compare/v2.1.0...v2.1.1
|
|
97
|
+
[2.1.0]: https://gitlab.com/osu-nrsg/nested-config/-/compare/v2.0.3...v2.1.0
|
|
98
|
+
[2.0.3]: https://gitlab.com/osu-nrsg/nested-config/-/compare/v2.0.2...v2.0.3
|
|
99
|
+
[2.0.2]: https://gitlab.com/osu-nrsg/nested-config/-/compare/v2.0.1...v2.0.2
|
|
100
|
+
[2.0.1]: https://gitlab.com/osu-nrsg/nested-config/-/compare/v2.0.0...v2.0.1
|
|
101
|
+
[2.0.0]: https://gitlab.com/osu-nrsg/nested-config/-/compare/v1.1.3...v2.0.0
|
|
102
|
+
[1.1.3]: https://gitlab.com/osu-nrsg/nested-config/-/tags/v1.1.3
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Randall Pittman, Oregon State University
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nested-config
|
|
3
|
+
Version: 2.1.3
|
|
4
|
+
Summary: Parse configuration files that include paths to other config files into a single configuration object
|
|
5
|
+
Project-URL: Repository, https://gitlab.com/osu-nrsg/nested-config
|
|
6
|
+
Project-URL: Issues, https://gitlab.com/osu-nrsg/nested-config/-/issues
|
|
7
|
+
Project-URL: Changelog, https://gitlab.com/osu-nrsg/nested-config/-/blob/master/CHANGELOG.md
|
|
8
|
+
Project-URL: GitHub Mirror, https://github.com/RandallPittmanOrSt/nested-config
|
|
9
|
+
Author-email: Randall Pittman <pittmara@oregonstate.edu>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: config,configuration files
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Requires-Dist: packaging>=24.2
|
|
23
|
+
Requires-Dist: pydantic<3.0.0,>=1.8
|
|
24
|
+
Requires-Dist: single-version>=1.6.0
|
|
25
|
+
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
|
|
26
|
+
Requires-Dist: typing-extensions>=4.6.0
|
|
27
|
+
Provides-Extra: pydantic
|
|
28
|
+
Requires-Dist: pydantic>=1.8; extra == 'pydantic'
|
|
29
|
+
Provides-Extra: pyyaml
|
|
30
|
+
Requires-Dist: pyyaml>=5.1.0; extra == 'pyyaml'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# nested-config <!-- omit in toc -->
|
|
34
|
+
|
|
35
|
+
[](http://python.org/pypi/nested-config)
|
|
36
|
+
[GitLab repository](https://gitlab.com/osu-nrsg/nested-config)
|
|
37
|
+
[GitHub mirror](https://github.com/RandallPittmanOrSt/nested-config)
|
|
38
|
+
|
|
39
|
+
<span style="font-size: larger">If you've ever wanted to have the option of replacing part
|
|
40
|
+
of a configuration file with a path to another configuration file that contains those
|
|
41
|
+
sub-parameters, then _nested-config_ might be for you.</span>
|
|
42
|
+
|
|
43
|
+
_nested-config_ allows you to parse configuration files that contain references to other
|
|
44
|
+
configuration files using a series of [models](#model). If a model includes a [nested
|
|
45
|
+
model](#nested-model) as one of its attributes and _nested-config_ finds a string value
|
|
46
|
+
for that parameter in the configuration file instead of an associative
|
|
47
|
+
array[^assoc-array], then it assumes that this string is a path to another configuration
|
|
48
|
+
file that should be parsed and whose contents should replace the string in the main
|
|
49
|
+
configuration file. If the string appears to be a relative path, it is assumed to be
|
|
50
|
+
relative to the path of its parent configuration file.
|
|
51
|
+
|
|
52
|
+
## Contents
|
|
53
|
+
|
|
54
|
+
- [Contents](#contents)
|
|
55
|
+
- [Basic Usage](#basic-usage)
|
|
56
|
+
- [Nomenclature](#nomenclature)
|
|
57
|
+
- [loader](#loader)
|
|
58
|
+
- [model](#model)
|
|
59
|
+
- [nested model](#nested-model)
|
|
60
|
+
- [config dict](#config-dict)
|
|
61
|
+
- [API](#api)
|
|
62
|
+
- [`nested_config.expand_config(config_path, model, *, default_suffix = None)`](#nested_configexpand_configconfig_path-model--default_suffix--none)
|
|
63
|
+
- [`nested_config.config_dict_loaders`](#nested_configconfig_dict_loaders)
|
|
64
|
+
- [Included loaders](#included-loaders)
|
|
65
|
+
- [Adding loaders](#adding-loaders)
|
|
66
|
+
- [_Deprecated features in v2.1.0, to be removed in v3.0.0_](#deprecated-features-in-v210-to-be-removed-in-v300)
|
|
67
|
+
- [Pydantic 1.0/2.0 Compatibility](#pydantic-1020-compatibility)
|
|
68
|
+
- [Footnotes](#footnotes)
|
|
69
|
+
|
|
70
|
+
## Basic Usage
|
|
71
|
+
|
|
72
|
+
Given the following configuration files `/tmp/house.toml` and `/tmp/tmp2/dimensions.toml`:
|
|
73
|
+
|
|
74
|
+
<figure>
|
|
75
|
+
<figcaption>Figure 1: /tmp/house.toml</figcaption>
|
|
76
|
+
|
|
77
|
+
```toml
|
|
78
|
+
name = "my house"
|
|
79
|
+
dimensions = "tmp2/dimensions.toml"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
</figure>
|
|
83
|
+
|
|
84
|
+
<figure>
|
|
85
|
+
<figcaption>Figure 2: /tmp/tmp2/dimensions.toml</figcaption>
|
|
86
|
+
|
|
87
|
+
```toml
|
|
88
|
+
length = 10
|
|
89
|
+
width = 20
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
</figure>
|
|
93
|
+
|
|
94
|
+
You can expand these into a single dict with the following:
|
|
95
|
+
|
|
96
|
+
<figure>
|
|
97
|
+
<figcaption>Figure 3: Expand /tmp/house.toml</figcaption>
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import nested_config
|
|
101
|
+
|
|
102
|
+
class Dimensions:
|
|
103
|
+
length: int
|
|
104
|
+
width: int
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class House:
|
|
108
|
+
name: str
|
|
109
|
+
dimensions: Dimensions
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
house_dict = nested_config.expand_config("/tmp/house.toml", House)
|
|
113
|
+
print(house_dict)
|
|
114
|
+
# {'name': 'my house', 'dimensions': {'length': 10, 'width': 20}}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Note that in `/tmp/house.toml`, `dimensions` is not a mapping but is a path to another
|
|
118
|
+
toml file at a path relative to `house.toml`.
|
|
119
|
+
|
|
120
|
+
See [tests](https://gitlab.com/osu-nrsg/nested-config/-/tree/master/tests) for more
|
|
121
|
+
detailed use-cases, such as where the root model contains lists or dicts of other models
|
|
122
|
+
and when those may be included in the root config file or specified as paths to sub-config
|
|
123
|
+
files.
|
|
124
|
+
|
|
125
|
+
## Nomenclature
|
|
126
|
+
|
|
127
|
+
### loader
|
|
128
|
+
|
|
129
|
+
A _loader_ is a function that reads a config file and returns a `dict` containing the
|
|
130
|
+
key-value pairs from the file. _nested-config_ includes loaders for JSON, TOML, and (if
|
|
131
|
+
PyYAML is installed) YAML. For example, the JSON loader looks like this:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import json
|
|
135
|
+
|
|
136
|
+
def json_load(path):
|
|
137
|
+
with open(path, "rb") as fobj:
|
|
138
|
+
return json.load(fobj)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### model
|
|
142
|
+
|
|
143
|
+
_nested-config_ uses the term _model_ to refer to a class definition that includes
|
|
144
|
+
annotated attributes. For example, this model, `Dimensions`, includes three attributes,
|
|
145
|
+
each of float type, `x`, `y`, and `z`:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
class Dimensions:
|
|
149
|
+
x: float
|
|
150
|
+
y: float
|
|
151
|
+
z: float
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
A model can be decorated as a [dataclass][dataclasses] or using [`attrs.define`][attrs] or
|
|
155
|
+
can subclass [`pydantic.BaseModel`][pydantic] to provide some method for instantiating an
|
|
156
|
+
object instance of the model but they aren't necessary to use _nested-config_.
|
|
157
|
+
|
|
158
|
+
The only criterion for a type to be a model is that is has a `__dict__` attribute that
|
|
159
|
+
includes an `__annotations__` member. _Note: This does **not** mean that **instances** of
|
|
160
|
+
the model must have a `__dict__` attribute. For example, instances of classes with
|
|
161
|
+
`__slots__` and `NamedTuple` instances may not have a `__dict__` attribute._
|
|
162
|
+
|
|
163
|
+
### nested model
|
|
164
|
+
|
|
165
|
+
A _nested model_ is a model that is included within another model as one of its class
|
|
166
|
+
attributes. For example, the below model `House` includes an `name` of string type, and an
|
|
167
|
+
attribute `dimensions` of `Dimensions` type (defined above). Since `Dimensions` is a
|
|
168
|
+
_model_ type, this is an example of a _nested model_.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
class House:
|
|
172
|
+
name: str
|
|
173
|
+
dimensions: Dimensions
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### config dict
|
|
177
|
+
|
|
178
|
+
A _config dict_ is simply a `dict` with string keys such as may be obtained by reading in
|
|
179
|
+
configuration text. For example reading in a string of TOML text with `tomllib.loads`
|
|
180
|
+
returns a _config dict_.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
import tomllib
|
|
184
|
+
|
|
185
|
+
config = "x = 2\ny = 3"
|
|
186
|
+
print(tomllib.loads(config))
|
|
187
|
+
# {'x': 2, 'y': 3}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## API
|
|
191
|
+
|
|
192
|
+
### `nested_config.expand_config(config_path, model, *, default_suffix = None)`
|
|
193
|
+
|
|
194
|
+
This function first loads the config file at `config_path` into a [config
|
|
195
|
+
dict](#config-dict) using the appropriate [loader](#loader). It then uses the attribute
|
|
196
|
+
annotations of [`model`](#model) and/or any [nested models](#nested-model) within `model`
|
|
197
|
+
to see if any of the string values in the configuration file correspond to a nested model.
|
|
198
|
+
For each such case, the string is assumed to be a path and is loaded into another config
|
|
199
|
+
dict which replaces the string value in the parent config dict. This continues until all
|
|
200
|
+
paths are converted and then the fully-expanded config dict is returned.
|
|
201
|
+
|
|
202
|
+
Note that all non-absolute string paths are assumed to be relative to the path of their
|
|
203
|
+
parent config file.
|
|
204
|
+
|
|
205
|
+
The loader for a given config file is determined by file extension (AKA suffix). If
|
|
206
|
+
`default_suffix` is specified, any config file with an unknown suffix or no suffix will be
|
|
207
|
+
assumed to be of that type, e.g. `".toml"`. (Otherwise this is an error.) It is possible
|
|
208
|
+
for one config file to include a path to a config file of a different format, so long as
|
|
209
|
+
each file has the appropriate suffix and there is a loader for that suffix.
|
|
210
|
+
|
|
211
|
+
### `nested_config.config_dict_loaders`
|
|
212
|
+
|
|
213
|
+
`config_dict_loaders` is a `dict` that maps file suffixes to [loaders](#loader).
|
|
214
|
+
|
|
215
|
+
#### Included loaders
|
|
216
|
+
|
|
217
|
+
_nested-config_ automatically loads the following files based on extension:
|
|
218
|
+
|
|
219
|
+
| Format | Extensions(s) | Library |
|
|
220
|
+
| ------ | ------------- | ------------------------------------------ |
|
|
221
|
+
| JSON | .json | `json` (stdlib) |
|
|
222
|
+
| TOML | .toml | `tomllib` (Python 3.11+ stdlib) or `tomli` |
|
|
223
|
+
| YAML | .yaml, .yml | `pyyaml` (extra dependency[^yaml-extra]) |
|
|
224
|
+
|
|
225
|
+
#### Adding loaders
|
|
226
|
+
|
|
227
|
+
To add a loader for another file extension, simply update `config_dict_loaders`:
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
import nested_config
|
|
231
|
+
from nested_config import ConfigDict # alias for dict[str, Any]
|
|
232
|
+
|
|
233
|
+
def dummy_loader(config_path: Path | str) -> ConfigDict:
|
|
234
|
+
return {"a": 1, "b": 2}
|
|
235
|
+
|
|
236
|
+
nested_config.config_dict_loaders[".dmy"] = dummy_loader
|
|
237
|
+
|
|
238
|
+
# or add another extension for an existing loader
|
|
239
|
+
nested_config.config_dict_loaders[".jsn"] = nested_config.config_dict_loaders[".json"]
|
|
240
|
+
|
|
241
|
+
# or use a different library to replace an existing loader
|
|
242
|
+
import rtoml
|
|
243
|
+
|
|
244
|
+
def rtoml_load(path) -> ConfigDict:
|
|
245
|
+
with open(path, "rb") as fobj:
|
|
246
|
+
return rtoml.load(fobj)
|
|
247
|
+
|
|
248
|
+
nested_config.config_dict_loaders[".toml"] = rtoml_load
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### _Deprecated features in v2.1.0, to be removed in v3.0.0_
|
|
252
|
+
|
|
253
|
+
The following functionality is available only if Pydantic is installed:
|
|
254
|
+
|
|
255
|
+
- `nested_config.validate_config()` expands a configuration file according to a Pydantic
|
|
256
|
+
model and then validates the config dictionary into an instance of the Pydantic model.
|
|
257
|
+
- `nested_config.BaseModel` can be used as a replacement for `pydantic.BaseModel` to
|
|
258
|
+
include a `from_config()` classmethod on all models that uses
|
|
259
|
+
`nested_config.validate_config()` to create an instance of the model.
|
|
260
|
+
- By importing `nested_config`, `PurePath` validators and JSON encoders are added to
|
|
261
|
+
`pydantic` in Pydantic 1.8-1.10 (they are included in Pydantic 2.0+)
|
|
262
|
+
|
|
263
|
+
## Pydantic 1.0/2.0 Compatibility
|
|
264
|
+
|
|
265
|
+
The [pydantic functionality](#deprecated-features-in-v210-to-be-removed-in-v300) in
|
|
266
|
+
nested-config is runtime compatible with Pydantic 1.8+ and Pydantic 2.0.
|
|
267
|
+
|
|
268
|
+
The follow table gives info on how to configure the [mypy](https://www.mypy-lang.org/) and
|
|
269
|
+
[Pyright](https://microsoft.github.io/pyright) type checkers to properly work, depending
|
|
270
|
+
on the version of Pydantic you are using.
|
|
271
|
+
|
|
272
|
+
| Pydantic Version | [mypy config][1] | mypy cli | [Pyright config][2] |
|
|
273
|
+
|------------------|-----------------------------|-----------------------------|---------------------------------------------|
|
|
274
|
+
| 2.0+ | `always_false = PYDANTIC_1` | `--always-false PYDANTIC_1` | `defineConstant = { "PYDANTIC_1" = false }` |
|
|
275
|
+
| 1.8-1.10 | `always_true = PYDANTIC_1` | `--always-true PYDANTIC_1` | `defineConstant = { "PYDANTIC_1" = true }` |
|
|
276
|
+
|
|
277
|
+
## Footnotes
|
|
278
|
+
|
|
279
|
+
[^yaml-extra]: Install `pyyaml` separately with `pip` or install _nested-config_ with
|
|
280
|
+
`pip install nested-config[yaml]`.
|
|
281
|
+
|
|
282
|
+
[^assoc-array]: Each language uses one or more names for an associative arrays. JSON calls
|
|
283
|
+
it an _object_, YAML calls is a _mapping_, and TOML calls is a _table_.
|
|
284
|
+
Any of course in Python it's a _dictionary_, or `dict`.
|
|
285
|
+
|
|
286
|
+
[1]: https://mypy.readthedocs.io/en/latest/config_file.html
|
|
287
|
+
[2]: https://microsoft.github.io/pyright/#/configuration
|
|
288
|
+
[dataclasses]: https://docs.python.org/3/library/dataclasses.html
|
|
289
|
+
[attrs]: https://www.attrs.org
|
|
290
|
+
[pydantic]: https://pydantic.dev
|