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.
@@ -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.
@@ -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.
@@ -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
+ ```