tomlstack 0.1.2__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.
- tomlstack-0.1.2/.github/workflows/ci.yml +60 -0
- tomlstack-0.1.2/.github/workflows/publish-pypi.yml +72 -0
- tomlstack-0.1.2/.github/workflows/publish-testpypi.yml +45 -0
- tomlstack-0.1.2/.gitignore +159 -0
- tomlstack-0.1.2/CHANGELOG.md +42 -0
- tomlstack-0.1.2/CONTRIBUTING.md +60 -0
- tomlstack-0.1.2/LICENSE +21 -0
- tomlstack-0.1.2/PKG-INFO +197 -0
- tomlstack-0.1.2/README.md +161 -0
- tomlstack-0.1.2/RELEASE.md +80 -0
- tomlstack-0.1.2/pyproject.toml +96 -0
- tomlstack-0.1.2/src/tomlstack/__init__.py +3 -0
- tomlstack-0.1.2/src/tomlstack/_version.py +34 -0
- tomlstack-0.1.2/src/tomlstack/api.py +81 -0
- tomlstack-0.1.2/src/tomlstack/base.py +29 -0
- tomlstack-0.1.2/src/tomlstack/errors.py +46 -0
- tomlstack-0.1.2/src/tomlstack/include.py +121 -0
- tomlstack-0.1.2/src/tomlstack/interpolate.py +124 -0
- tomlstack-0.1.2/src/tomlstack/loader.py +202 -0
- tomlstack-0.1.2/src/tomlstack/nodes.py +89 -0
- tomlstack-0.1.2/src/tomlstack/path_expr.py +78 -0
- tomlstack-0.1.2/tests/test_include_merge.py +161 -0
- tomlstack-0.1.2/tests/test_interpolation.py +94 -0
- tomlstack-0.1.2/tests/test_nodes.py +55 -0
- tomlstack-0.1.2/tests/test_smoke.py +24 -0
- tomlstack-0.1.2/uv.lock +951 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "main" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "main" ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test-and-build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: [ "3.11", "3.12", "3.13", "3.14" ]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- name: Set up Python
|
|
24
|
+
uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: ${{ matrix.python-version }}
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: |
|
|
30
|
+
python -m pip install --upgrade pip
|
|
31
|
+
python -m pip install -e ".[dev]"
|
|
32
|
+
|
|
33
|
+
- name: Lint
|
|
34
|
+
if: matrix.python-version == '3.11'
|
|
35
|
+
run: |
|
|
36
|
+
ruff check .
|
|
37
|
+
ruff format --check .
|
|
38
|
+
|
|
39
|
+
- name: Type check
|
|
40
|
+
if: matrix.python-version == '3.11'
|
|
41
|
+
run: mypy
|
|
42
|
+
|
|
43
|
+
- name: Run tests
|
|
44
|
+
if: matrix.python-version != '3.11'
|
|
45
|
+
run: python -m pytest -q
|
|
46
|
+
|
|
47
|
+
- name: Run tests with coverage
|
|
48
|
+
if: matrix.python-version == '3.11'
|
|
49
|
+
run: python -m pytest --cov=tomlstack --cov-report=term-missing --cov-report=xml
|
|
50
|
+
|
|
51
|
+
- name: Upload coverage artifact
|
|
52
|
+
if: matrix.python-version == '3.11'
|
|
53
|
+
uses: actions/upload-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: coverage-xml
|
|
56
|
+
path: coverage.xml
|
|
57
|
+
|
|
58
|
+
- name: Build package
|
|
59
|
+
if: matrix.python-version == '3.11'
|
|
60
|
+
run: python -m build
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: Publish PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
if: ${{ !contains(github.ref_name, 'rc') }}
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment: PyPI
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- name: Set up Python
|
|
24
|
+
uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.11"
|
|
27
|
+
|
|
28
|
+
- name: Install build and test tools
|
|
29
|
+
run: |
|
|
30
|
+
python -m pip install --upgrade pip
|
|
31
|
+
python -m pip install -e ".[test]"
|
|
32
|
+
python -m pip install build twine
|
|
33
|
+
|
|
34
|
+
- name: Run tests
|
|
35
|
+
run: python -m pytest -q
|
|
36
|
+
|
|
37
|
+
- name: Build package
|
|
38
|
+
run: python -m build
|
|
39
|
+
|
|
40
|
+
- name: Check distribution metadata
|
|
41
|
+
run: python -m twine check dist/*
|
|
42
|
+
|
|
43
|
+
- name: Publish to PyPI
|
|
44
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
45
|
+
|
|
46
|
+
- name: Generate changelog
|
|
47
|
+
id: changelog
|
|
48
|
+
run: |
|
|
49
|
+
if git describe --tags --abbrev=0 "${GITHUB_REF_NAME}^" >/dev/null 2>&1; then
|
|
50
|
+
PREV_TAG="$(git describe --tags --abbrev=0 "${GITHUB_REF_NAME}^")"
|
|
51
|
+
{
|
|
52
|
+
echo "CHANGELOG<<EOF"
|
|
53
|
+
git log --pretty=format:'- %s (%h)' "${PREV_TAG}..${GITHUB_REF_NAME}"
|
|
54
|
+
echo
|
|
55
|
+
echo "EOF"
|
|
56
|
+
} >> "$GITHUB_OUTPUT"
|
|
57
|
+
else
|
|
58
|
+
{
|
|
59
|
+
echo "CHANGELOG<<EOF"
|
|
60
|
+
echo "Initial release ${GITHUB_REF_NAME}."
|
|
61
|
+
echo "EOF"
|
|
62
|
+
} >> "$GITHUB_OUTPUT"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
- name: Create GitHub Release
|
|
66
|
+
uses: softprops/action-gh-release@v2
|
|
67
|
+
with:
|
|
68
|
+
tag_name: ${{ github.ref_name }}
|
|
69
|
+
name: ${{ github.ref_name }}
|
|
70
|
+
body: ${{ steps.changelog.outputs.CHANGELOG }}
|
|
71
|
+
files: |
|
|
72
|
+
dist/*
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Publish TestPyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*rc*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: TestPyPI
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
id-token: write
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 0
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.11"
|
|
26
|
+
|
|
27
|
+
- name: Install build and test tools
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade pip
|
|
30
|
+
python -m pip install -e ".[test]"
|
|
31
|
+
python -m pip install build twine
|
|
32
|
+
|
|
33
|
+
- name: Run tests
|
|
34
|
+
run: python -m pytest -q
|
|
35
|
+
|
|
36
|
+
- name: Build package
|
|
37
|
+
run: python -m build
|
|
38
|
+
|
|
39
|
+
- name: Check distribution metadata
|
|
40
|
+
run: python -m twine check dist/*
|
|
41
|
+
|
|
42
|
+
- name: Publish to TestPyPI
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
44
|
+
with:
|
|
45
|
+
repository-url: https://test.pypi.org/legacy/
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
105
|
+
__pypackages__/
|
|
106
|
+
|
|
107
|
+
# Celery stuff
|
|
108
|
+
celerybeat-schedule
|
|
109
|
+
celerybeat.pid
|
|
110
|
+
|
|
111
|
+
# SageMath parsed files
|
|
112
|
+
*.sage.py
|
|
113
|
+
|
|
114
|
+
# Environments
|
|
115
|
+
.env
|
|
116
|
+
.venv
|
|
117
|
+
env/
|
|
118
|
+
venv/
|
|
119
|
+
ENV/
|
|
120
|
+
env.bak/
|
|
121
|
+
venv.bak/
|
|
122
|
+
|
|
123
|
+
# Spyder project settings
|
|
124
|
+
.spyderproject
|
|
125
|
+
.spyproject
|
|
126
|
+
|
|
127
|
+
# Rope project settings
|
|
128
|
+
.ropeproject
|
|
129
|
+
|
|
130
|
+
# mkdocs documentation
|
|
131
|
+
/site
|
|
132
|
+
|
|
133
|
+
# mypy
|
|
134
|
+
.mypy_cache/
|
|
135
|
+
.dmypy.json
|
|
136
|
+
dmypy.json
|
|
137
|
+
|
|
138
|
+
# Pyre type checker
|
|
139
|
+
.pyre/
|
|
140
|
+
|
|
141
|
+
# pytype static type analyzer
|
|
142
|
+
.pytype/
|
|
143
|
+
|
|
144
|
+
# Cython debug symbols
|
|
145
|
+
cython_debug/
|
|
146
|
+
|
|
147
|
+
# PyCharm
|
|
148
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
149
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
150
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
151
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
152
|
+
#.idea/
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# custom
|
|
156
|
+
|
|
157
|
+
.vscode/*
|
|
158
|
+
# hatch-vcs generated version file
|
|
159
|
+
src/tomlstack/_version.py
|
|
@@ -0,0 +1,42 @@
|
|
|
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,
|
|
6
|
+
and this project follows Semantic Versioning.
|
|
7
|
+
|
|
8
|
+
## [0.1.2] - 2026-03-01
|
|
9
|
+
|
|
10
|
+
- first publish version
|
|
11
|
+
|
|
12
|
+
## [0.1.1rc3] - 2026-03-01
|
|
13
|
+
|
|
14
|
+
- try to fix OIDC
|
|
15
|
+
|
|
16
|
+
## [0.1.1rc2] - 2026-03-01
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- CI Python version matrix (`3.11`, `3.12`, `3.13`, `3.x`).
|
|
21
|
+
- Development tool configuration for `ruff`, `mypy`, and `pytest`.
|
|
22
|
+
- Publishing workflows for TestPyPI and PyPI with GitHub Release automation.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Packaging version now comes from git tags via `hatch-vcs`.
|
|
27
|
+
- CI now produces coverage reports and enforces a coverage threshold.
|
|
28
|
+
|
|
29
|
+
## [0.1.1rc1] - 2026-02-28
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- First release candidate published to TestPyPI.
|
|
34
|
+
|
|
35
|
+
## [0.1.0] - 2026-02-28
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- Initial public release of `tomlstack`.
|
|
40
|
+
- Top-level `include` support with deterministic merge behavior.
|
|
41
|
+
- `${path}` interpolation with cycle and undefined reference checks.
|
|
42
|
+
- Node provenance (`origin`, `history`, `explain`) APIs.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Development setup
|
|
4
|
+
|
|
5
|
+
1. Use Python 3.11+.
|
|
6
|
+
2. Install dev dependencies:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
python -m pip install -e ".[dev]"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Local quality checks
|
|
13
|
+
|
|
14
|
+
Run tests:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv run python -m pytest -q
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Run lint:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv run ruff check .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Run formatting check (optional):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
uv run ruff format --check .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Run type check:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv run mypy
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Project layout
|
|
39
|
+
|
|
40
|
+
- Source code: `src/tomlstack/`
|
|
41
|
+
- Tests: `tests/`
|
|
42
|
+
- CI workflows: `.github/workflows/`
|
|
43
|
+
|
|
44
|
+
## Commit and PR guidelines
|
|
45
|
+
|
|
46
|
+
- Keep commits focused and minimal.
|
|
47
|
+
- Add or update tests for behavior changes.
|
|
48
|
+
- Update `CHANGELOG.md` for user-visible changes.
|
|
49
|
+
- Ensure CI is green before merge.
|
|
50
|
+
|
|
51
|
+
## Release flow
|
|
52
|
+
|
|
53
|
+
1. Ensure `CHANGELOG.md` is up to date.
|
|
54
|
+
2. For release candidate, create tag like `v0.1.1rc1` and push.
|
|
55
|
+
3. For final release, create tag like `v0.1.1` and push.
|
|
56
|
+
4. CI workflows will run tests/build and publish automatically.
|
|
57
|
+
|
|
58
|
+
Version is derived from git tags via `hatch-vcs` (do not edit version manually in `pyproject.toml`).
|
|
59
|
+
|
|
60
|
+
See `RELEASE.md` for full publishing details.
|
tomlstack-0.1.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wxzhao7
|
|
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.
|
tomlstack-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tomlstack
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: TOML configuration loader with include/merge/interpolation
|
|
5
|
+
Project-URL: Homepage, https://github.com/wxzhao7/tomlstack
|
|
6
|
+
Project-URL: Repository, https://github.com/wxzhao7/tomlstack
|
|
7
|
+
Project-URL: Issues, https://github.com/wxzhao7/tomlstack/issues
|
|
8
|
+
Author: wxzhao7
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: config,configuration,include,interpolation,toml
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
23
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
27
|
+
Requires-Dist: twine>=5.1; extra == 'dev'
|
|
28
|
+
Provides-Extra: lint
|
|
29
|
+
Requires-Dist: ruff>=0.8; extra == 'lint'
|
|
30
|
+
Provides-Extra: test
|
|
31
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'test'
|
|
32
|
+
Requires-Dist: pytest>=8.3; extra == 'test'
|
|
33
|
+
Provides-Extra: typing
|
|
34
|
+
Requires-Dist: mypy>=1.11; extra == 'typing'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# tomlstack
|
|
38
|
+
|
|
39
|
+
`tomlstack` is a lightweight TOML config loader for Python 3.11+ with:
|
|
40
|
+
|
|
41
|
+
- top-level `include` loading
|
|
42
|
+
- deterministic merge by include order
|
|
43
|
+
- `${path}` interpolation with cycle/undefined checks
|
|
44
|
+
- node-level provenance (`origin`, `explain`, `history`)
|
|
45
|
+
|
|
46
|
+
tomlstack does not try to be a configuration framework.
|
|
47
|
+
It address two missing pieces to TOML: file composition and safe interpolation — while keeping files self-contained and explainable.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install tomlstack
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
`main.toml`:
|
|
58
|
+
|
|
59
|
+
```toml
|
|
60
|
+
include = [
|
|
61
|
+
"./base.toml",
|
|
62
|
+
"./prod.toml",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[db]
|
|
66
|
+
url = "postgres://${db.user}:${db.pass}@${db.host}:${db.port}"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`base.toml`:
|
|
70
|
+
|
|
71
|
+
```toml
|
|
72
|
+
[db]
|
|
73
|
+
user = "alice"
|
|
74
|
+
pass = "secret"
|
|
75
|
+
host = "localhost"
|
|
76
|
+
port = 5432
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Python:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from tomlstack import load
|
|
83
|
+
|
|
84
|
+
cfg = load("main.toml")
|
|
85
|
+
print(cfg["db"]["url"].raw) # raw interpolation string
|
|
86
|
+
cfg.resolve()
|
|
87
|
+
print(cfg["db"]["url"].value) # resolved value
|
|
88
|
+
print(cfg["db"]["url"].origin)
|
|
89
|
+
print(cfg["db"]["url"].history)
|
|
90
|
+
print(cfg.to_dict())
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Include Semantics
|
|
94
|
+
|
|
95
|
+
- top-level `include` only; nested `include` is treated as normal data
|
|
96
|
+
- syntax: string or list of strings
|
|
97
|
+
- valid include path forms:
|
|
98
|
+
- `./...` or `../...`
|
|
99
|
+
- `@label/...` (label from `__meta__.include.anchors`)
|
|
100
|
+
- absolute path
|
|
101
|
+
- any other form raises error with hint: `Use ./ or ../ or @label/`
|
|
102
|
+
|
|
103
|
+
### Meta Include Directives
|
|
104
|
+
|
|
105
|
+
```toml
|
|
106
|
+
[__meta__]
|
|
107
|
+
version = 1
|
|
108
|
+
|
|
109
|
+
[__meta__.include]
|
|
110
|
+
root = "../.."
|
|
111
|
+
|
|
112
|
+
[__meta__.include.anchors]
|
|
113
|
+
proj = "./shared"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- `__meta__.include.root` is sugar for `anchors.root`
|
|
117
|
+
- if both `root` and `anchors.root` exist and resolve differently, error
|
|
118
|
+
- anchor/root path values must be absolute or start with `./` or `../`
|
|
119
|
+
- if any file explicitly sets `__meta__.version`, all files in include chain must share one supported version (`1`)
|
|
120
|
+
|
|
121
|
+
## Merge Rules
|
|
122
|
+
|
|
123
|
+
Load order for current file:
|
|
124
|
+
|
|
125
|
+
1. merge first include
|
|
126
|
+
2. merge second include
|
|
127
|
+
3. ...
|
|
128
|
+
4. merge current file (highest priority)
|
|
129
|
+
|
|
130
|
+
Conflict behavior:
|
|
131
|
+
|
|
132
|
+
- dict: recursive merge, later wins on key conflict
|
|
133
|
+
- list: later value replaces whole list
|
|
134
|
+
- scalar: later value replaces earlier
|
|
135
|
+
|
|
136
|
+
## Interpolation Semantics
|
|
137
|
+
|
|
138
|
+
- interpolation happens on `cfg.resolve()`
|
|
139
|
+
- path syntax supports dot and list index: `${db.apps[0]}`
|
|
140
|
+
- full-string interpolation (`"${db.port}"`) keeps source type
|
|
141
|
+
- embedded interpolation (`"postgres://${db.host}:${db.port}"`) allows only:
|
|
142
|
+
- `str`, `int`, `float`, `date`, `time`, `datetime`
|
|
143
|
+
- formatting syntax: `${path:spec}`
|
|
144
|
+
- for `date/time/datetime`, formatting uses `strftime`
|
|
145
|
+
- otherwise uses Python `format(value, spec)`
|
|
146
|
+
- undefined reference raises `InterpolationUndefinedError`
|
|
147
|
+
- interpolation cycle raises `InterpolationCycleError`
|
|
148
|
+
|
|
149
|
+
## Public API
|
|
150
|
+
|
|
151
|
+
- `cfg = load("f.toml")`
|
|
152
|
+
- `cfg.resolve()`
|
|
153
|
+
- `cfg.to_dict()`
|
|
154
|
+
- `node = cfg["proj"][0]["path"]["foo"]`
|
|
155
|
+
- `node.raw`
|
|
156
|
+
- `node.value`
|
|
157
|
+
- `node.origin`
|
|
158
|
+
- `node.history`
|
|
159
|
+
- `node.preview()`
|
|
160
|
+
- `cfg.to_toml()` -> `NotImplementedError`
|
|
161
|
+
|
|
162
|
+
## Current Limitations
|
|
163
|
+
|
|
164
|
+
- interpolation path parser supports unquoted dot keys and numeric list indices
|
|
165
|
+
- no nested interpolation expressions
|
|
166
|
+
- `to_toml()` is not implemented yet
|
|
167
|
+
|
|
168
|
+
## TODO
|
|
169
|
+
|
|
170
|
+
- [ ] review the details of interpolation
|
|
171
|
+
- [ ] explain history with interpolation
|
|
172
|
+
|
|
173
|
+
## Release To PyPI
|
|
174
|
+
|
|
175
|
+
Build package:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
uv run --with build python -m build
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Upload to TestPyPI first:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
uv run --with twine python -m twine upload --repository testpypi dist/*
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Verify install from TestPyPI:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
python -m pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple tomlstack
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Upload to PyPI:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
uv run --with twine python -m twine upload dist/*
|
|
197
|
+
```
|