lib-layered-config 1.0.0__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of lib-layered-config might be problematic. Click here for more details.
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.github/workflows/ci.yml +7 -27
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.github/workflows/codeql.yml +3 -3
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/AGENTS.md +2 -2
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/CHANGELOG.md +16 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/PKG-INFO +23 -18
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/README.md +6 -1
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/docs/systemdesign/module_reference.md +29 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/pyproject.toml +17 -17
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/_utils.py +156 -15
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/build.py +3 -8
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/bump.py +4 -9
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/bump_major.py +2 -6
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/bump_minor.py +2 -6
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/bump_patch.py +2 -6
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/clean.py +2 -2
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/cli.py +81 -74
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/dev.py +2 -8
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/help.py +2 -1
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/install.py +2 -8
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/menu.py +16 -16
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/push.py +13 -12
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/release.py +19 -37
- lib_layered_config-1.1.0/scripts/run_cli.py +154 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/target_metadata.py +19 -7
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/test.py +112 -44
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/version_current.py +1 -6
- lib_layered_config-1.1.0/src/lib_layered_config/__init__conf__.py +67 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/adapters/file_loaders/structured.py +103 -16
- lib_layered_config-1.1.0/src/lib_layered_config/cli/__init__.py +144 -0
- lib_layered_config-1.1.0/src/lib_layered_config/cli/common.py +440 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/examples/generate.py +1 -1
- lib_layered_config-1.1.0/tests/adapters/test_file_loaders.py +107 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/e2e/test_cli.py +25 -3
- lib_layered_config-1.1.0/tests/unit/test_cli_helpers.py +253 -0
- lib_layered_config-1.0.0/scripts/run_cli.py +0 -137
- lib_layered_config-1.0.0/src/lib_layered_config/cli/__init__.py +0 -162
- lib_layered_config-1.0.0/src/lib_layered_config/cli/common.py +0 -232
- lib_layered_config-1.0.0/tests/adapters/test_file_loaders.py +0 -89
- lib_layered_config-1.0.0/tests/unit/test_cli_helpers.py +0 -305
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.devcontainer/devcontainer.json +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.devcontainer/settings.json +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.env.example +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.github/dependabot.yml +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.github/workflows/release.yml +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.gitignore +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.qlty/qlty.toml +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/.snyk +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/CONTRIBUTING.md +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/DEVELOPMENT.md +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/LICENSE +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/Makefile +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/codecov.yml +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/docs/systemdesign/concept.md +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/docs/systemdesign/test_matrix.md +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/notebooks/Quickstart.ipynb +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/__main__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/scripts/bump_version.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/__main__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/_layers.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/_platform.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/adapters/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/adapters/dotenv/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/adapters/dotenv/default.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/adapters/env/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/adapters/env/default.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/adapters/file_loaders/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/adapters/path_resolvers/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/adapters/path_resolvers/default.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/application/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/application/merge.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/application/ports.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/cli/constants.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/cli/deploy.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/cli/fail.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/cli/generate.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/cli/info.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/cli/read.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/core.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/domain/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/domain/config.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/domain/errors.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/examples/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/examples/deploy.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/observability.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/py.typed +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/src/lib_layered_config/testing.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/test-serialable.toml +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/test.toml +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/adapters/test_dotenv_loader.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/adapters/test_env_loader.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/adapters/test_path_resolver.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/adapters/test_port_contracts.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/application/test_merge.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/conftest.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/e2e/test_notebooks.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/e2e/test_read_config.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/examples/test_deploy.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/examples/test_generate.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/support/__init__.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/support/layered.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/support/os_markers.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/unit/test_config.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/unit/test_core.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/unit/test_errors.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/unit/test_examples.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/unit/test_layers_helpers.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/unit/test_observability.py +0 -0
- {lib_layered_config-1.0.0 → lib_layered_config-1.1.0}/tests/unit/test_testing.py +0 -0
|
@@ -118,7 +118,7 @@ jobs:
|
|
|
118
118
|
pipx install dist/*.whl
|
|
119
119
|
"$CLI_BIN" --version 2>/dev/null || python -m "$PACKAGE_MODULE" --version
|
|
120
120
|
- name: Install uv
|
|
121
|
-
uses: astral-sh/setup-uv@
|
|
121
|
+
uses: astral-sh/setup-uv@v7
|
|
122
122
|
with:
|
|
123
123
|
enable-cache: true
|
|
124
124
|
- name: uv tool install
|
|
@@ -145,42 +145,22 @@ jobs:
|
|
|
145
145
|
PIP_NO_INPUT: "1"
|
|
146
146
|
run: |
|
|
147
147
|
python - << 'PY'
|
|
148
|
-
import json, sys
|
|
149
148
|
from pathlib import Path
|
|
149
|
+
|
|
150
150
|
import nbformat
|
|
151
|
-
try:
|
|
152
|
-
from nbformat.validator import normalize # type: ignore
|
|
153
|
-
except Exception:
|
|
154
|
-
normalize = None # type: ignore
|
|
155
151
|
from nbclient import NotebookClient
|
|
156
152
|
|
|
157
153
|
nb_path = Path('notebooks/Quickstart.ipynb')
|
|
158
154
|
if not nb_path.exists():
|
|
159
155
|
raise SystemExit(f"Notebook not found: {nb_path}")
|
|
160
156
|
|
|
161
|
-
with nb_path.open('r', encoding='utf-8') as
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if normalize is not None:
|
|
165
|
-
nb_norm = normalize(nb) # may return notebook or a tuple
|
|
166
|
-
# pick the first element that looks like a notebook
|
|
167
|
-
cand = nb_norm
|
|
168
|
-
if isinstance(nb_norm, tuple):
|
|
169
|
-
for item in nb_norm:
|
|
170
|
-
if hasattr(item, 'cells') and hasattr(item, 'metadata'):
|
|
171
|
-
cand = item
|
|
172
|
-
break
|
|
173
|
-
nb = cand
|
|
174
|
-
# Final guard for robustness
|
|
175
|
-
if isinstance(nb, tuple) or not (hasattr(nb, 'cells') and hasattr(nb, 'metadata')):
|
|
176
|
-
raise SystemExit(f"Unexpected notebook object type after normalize: {type(nb)}")
|
|
177
|
-
|
|
178
|
-
client = NotebookClient(nb, timeout=900, kernel_name='python3', allow_errors=False)
|
|
157
|
+
with nb_path.open('r', encoding='utf-8') as handle:
|
|
158
|
+
notebook = nbformat.read(handle, as_version=4)
|
|
159
|
+
client = NotebookClient(notebook, timeout=900, kernel_name='python3', allow_errors=False)
|
|
179
160
|
client.execute()
|
|
180
161
|
|
|
181
|
-
# Optionally write executed notebook artifact (not committed)
|
|
182
162
|
out_path = Path('notebooks/Quickstart-executed.ipynb')
|
|
183
|
-
with out_path.open('w', encoding='utf-8') as
|
|
184
|
-
nbformat.write(
|
|
163
|
+
with out_path.open('w', encoding='utf-8') as handle:
|
|
164
|
+
nbformat.write(notebook, handle)
|
|
185
165
|
print(f"Executed notebook written to: {out_path}")
|
|
186
166
|
PY
|
|
@@ -26,15 +26,15 @@ jobs:
|
|
|
26
26
|
uses: actions/checkout@v4
|
|
27
27
|
|
|
28
28
|
- name: Initialize CodeQL
|
|
29
|
-
uses: github/codeql-action/init@
|
|
29
|
+
uses: github/codeql-action/init@v4
|
|
30
30
|
with:
|
|
31
31
|
languages: ${{ matrix.language }}
|
|
32
32
|
|
|
33
33
|
- name: Autobuild
|
|
34
|
-
uses: github/codeql-action/autobuild@
|
|
34
|
+
uses: github/codeql-action/autobuild@v4
|
|
35
35
|
|
|
36
36
|
- name: Perform CodeQL Analysis
|
|
37
|
-
uses: github/codeql-action/analyze@
|
|
37
|
+
uses: github/codeql-action/analyze@v4
|
|
38
38
|
with:
|
|
39
39
|
category: "/language:${{ matrix.language }}"
|
|
40
40
|
|
|
@@ -54,8 +54,8 @@ when writing or refracturing Python scripts, apply those Rules :
|
|
|
54
54
|
### Versioning & Releases
|
|
55
55
|
|
|
56
56
|
- Single source of truth for the package version is `pyproject.toml` (`[project].version`).
|
|
57
|
-
-
|
|
58
|
-
- On a version bump, update
|
|
57
|
+
- Release automation mirrors the metadata into `src/lib_layered_config/__init__conf__.py`; runtime code imports those constants instead of calling `importlib.metadata`.
|
|
58
|
+
- On a version bump, update `pyproject.toml`, run the metadata sync script (or `make bump`), and record the change in `CHANGELOG.md`.
|
|
59
59
|
- Tag releases `vX.Y.Z` and push tags; CI will build artifacts and publish when configured.
|
|
60
60
|
|
|
61
61
|
### Common Make Targets (Alphabetical)
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.0] - 2025-10-13
|
|
4
|
+
|
|
5
|
+
- Refactor CLI metadata commands (`info`, `--version`) to read from the
|
|
6
|
+
statically generated `__init__conf__` module, removing runtime
|
|
7
|
+
`importlib.metadata` lookups.
|
|
8
|
+
- Update CLI entrypoint to use `lib_cli_exit_tools.cli_session` for traceback
|
|
9
|
+
management, keeping the shared configuration in sync with the newer
|
|
10
|
+
`lib_cli_exit_tools` API without manual state restoration.
|
|
11
|
+
- Retire the `lib_layered_config.cli._default_env_prefix` compatibility export;
|
|
12
|
+
import `default_env_prefix` from `lib_layered_config.core` instead.
|
|
13
|
+
- Refresh dependency baselines to the latest stable releases (rich-click 1.9.3,
|
|
14
|
+
codecov-cli 11.2.3, PyYAML 6.0.3, ruff 0.14.0, etc.) and mark dataclasses with
|
|
15
|
+
`slots=True` where appropriate to embrace Python 3.13 idioms.
|
|
16
|
+
- Simplify the CI notebook smoke test to rely on upstream nbformat behaviour,
|
|
17
|
+
dropping compatibility shims for older notebook metadata schemas.
|
|
18
|
+
|
|
3
19
|
## [1.0.0] - 2025-10-09
|
|
4
20
|
|
|
5
21
|
- Add optional `default_file` support to the composition root and CLI so baseline configuration files load ahead of layered overrides.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lib_layered_config
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Cross-platform layered configuration loader for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/bitranox/lib_layered_config
|
|
6
6
|
Project-URL: Repository, https://github.com/bitranox/lib_layered_config.git
|
|
@@ -16,26 +16,26 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Classifier: Typing :: Typed
|
|
18
18
|
Requires-Python: >=3.13
|
|
19
|
-
Requires-Dist: lib-cli-exit-tools>=1.
|
|
20
|
-
Requires-Dist: rich-click>=1.9.
|
|
19
|
+
Requires-Dist: lib-cli-exit-tools>=2.1.0
|
|
20
|
+
Requires-Dist: rich-click>=1.9.3
|
|
21
21
|
Provides-Extra: dev
|
|
22
|
-
Requires-Dist: bandit>=1.
|
|
23
|
-
Requires-Dist: build>=1.3; extra == 'dev'
|
|
24
|
-
Requires-Dist: codecov-cli>=
|
|
22
|
+
Requires-Dist: bandit>=1.8.6; extra == 'dev'
|
|
23
|
+
Requires-Dist: build>=1.3.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: codecov-cli>=11.2.3; extra == 'dev'
|
|
25
25
|
Requires-Dist: coverage[toml]>=7.10.7; extra == 'dev'
|
|
26
26
|
Requires-Dist: hypothesis>=6.140.3; extra == 'dev'
|
|
27
|
-
Requires-Dist: import-linter>=2.
|
|
28
|
-
Requires-Dist: pip-audit>=2.
|
|
29
|
-
Requires-Dist: pyright>=1.1; extra == 'dev'
|
|
30
|
-
Requires-Dist: pytest-asyncio>=0
|
|
31
|
-
Requires-Dist: pytest-cov>=7.0; extra == 'dev'
|
|
32
|
-
Requires-Dist: pytest>=8.4; extra == 'dev'
|
|
33
|
-
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
34
|
-
Requires-Dist: ruff>=0.
|
|
35
|
-
Requires-Dist: textual>=6.
|
|
36
|
-
Requires-Dist: twine>=6.2; extra == 'dev'
|
|
27
|
+
Requires-Dist: import-linter>=2.5.2; extra == 'dev'
|
|
28
|
+
Requires-Dist: pip-audit>=2.9.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pyright>=1.1.406; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-asyncio>=1.2.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.4.2; extra == 'dev'
|
|
33
|
+
Requires-Dist: pyyaml>=6.0.3; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.14.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: textual>=6.3.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: twine>=6.2.0; extra == 'dev'
|
|
37
37
|
Provides-Extra: yaml
|
|
38
|
-
Requires-Dist: pyyaml>=6.0; extra == 'yaml'
|
|
38
|
+
Requires-Dist: pyyaml>=6.0.3; extra == 'yaml'
|
|
39
39
|
Description-Content-Type: text/markdown
|
|
40
40
|
|
|
41
41
|
# lib_layered_config
|
|
@@ -342,6 +342,10 @@ make build # build wheel / sdist artifacts
|
|
|
342
342
|
make run -- --help # run the CLI via the repo entrypoint
|
|
343
343
|
```
|
|
344
344
|
|
|
345
|
+
The development extra now targets the latest stable releases of the toolchain
|
|
346
|
+
(pytest 8.4.2, ruff 0.14.0, codecov-cli 11.2.3, etc.), so upgrading your local
|
|
347
|
+
environment before running `make` is recommended.
|
|
348
|
+
|
|
345
349
|
*Formatting gate:* Ruff formatting runs in check mode during `make test`. Run `ruff format .` (or `pre-commit run --all-files`) before pushing and consider `pre-commit install` to keep local edits aligned.
|
|
346
350
|
|
|
347
351
|
*Coverage gate:* the maintained test suite must stay ≥90% (see `pyproject.toml`). Add targeted unit tests if you extend functionality.
|
|
@@ -357,7 +361,8 @@ The GitHub Actions workflow executes three jobs:
|
|
|
357
361
|
|
|
358
362
|
- **Test matrix** (Linux/macOS/Windows, Python 3.13 + latest 3.x) running the same pipeline as `make test`.
|
|
359
363
|
- **pipx / uv verification** to prove the built wheel installs cleanly with the common Python app launchers.
|
|
360
|
-
- **Notebook smoke test** that executes `notebooks/Quickstart.ipynb` to keep the tutorial in sync.
|
|
364
|
+
- **Notebook smoke test** that executes `notebooks/Quickstart.ipynb` to keep the tutorial in sync using the native nbformat workflow (no compatibility shims required).
|
|
365
|
+
- CLI jobs run through `lib_cli_exit_tools.cli_session`, ensuring the `--traceback` flag behaves the same locally and in automation.
|
|
361
366
|
|
|
362
367
|
Packaging-specific jobs (conda, Nix, Homebrew sync) were retired; the Python packaging metadata in `pyproject.toml` remains the single source of truth.
|
|
363
368
|
|
|
@@ -302,6 +302,10 @@ make build # build wheel / sdist artifacts
|
|
|
302
302
|
make run -- --help # run the CLI via the repo entrypoint
|
|
303
303
|
```
|
|
304
304
|
|
|
305
|
+
The development extra now targets the latest stable releases of the toolchain
|
|
306
|
+
(pytest 8.4.2, ruff 0.14.0, codecov-cli 11.2.3, etc.), so upgrading your local
|
|
307
|
+
environment before running `make` is recommended.
|
|
308
|
+
|
|
305
309
|
*Formatting gate:* Ruff formatting runs in check mode during `make test`. Run `ruff format .` (or `pre-commit run --all-files`) before pushing and consider `pre-commit install` to keep local edits aligned.
|
|
306
310
|
|
|
307
311
|
*Coverage gate:* the maintained test suite must stay ≥90% (see `pyproject.toml`). Add targeted unit tests if you extend functionality.
|
|
@@ -317,7 +321,8 @@ The GitHub Actions workflow executes three jobs:
|
|
|
317
321
|
|
|
318
322
|
- **Test matrix** (Linux/macOS/Windows, Python 3.13 + latest 3.x) running the same pipeline as `make test`.
|
|
319
323
|
- **pipx / uv verification** to prove the built wheel installs cleanly with the common Python app launchers.
|
|
320
|
-
- **Notebook smoke test** that executes `notebooks/Quickstart.ipynb` to keep the tutorial in sync.
|
|
324
|
+
- **Notebook smoke test** that executes `notebooks/Quickstart.ipynb` to keep the tutorial in sync using the native nbformat workflow (no compatibility shims required).
|
|
325
|
+
- CLI jobs run through `lib_cli_exit_tools.cli_session`, ensuring the `--traceback` flag behaves the same locally and in automation.
|
|
321
326
|
|
|
322
327
|
Packaging-specific jobs (conda, Nix, Homebrew sync) were retired; the Python packaging metadata in `pyproject.toml` remains the single source of truth.
|
|
323
328
|
|
|
@@ -715,6 +715,9 @@ observability helpers.
|
|
|
715
715
|
### Format Loaders
|
|
716
716
|
- **Purpose:** Parse TOML/JSON/YAML into mappings.
|
|
717
717
|
- **Location:** `structured.py`
|
|
718
|
+
- **Supporting Helpers:** `_ensure_yaml_available`, `_require_yaml_module`,
|
|
719
|
+
`_parse_yaml_bytes` lazily import PyYAML, explain missing dependencies, and
|
|
720
|
+
wrap parser errors with domain-specific context.
|
|
718
721
|
|
|
719
722
|
---
|
|
720
723
|
|
|
@@ -724,6 +727,8 @@ observability helpers.
|
|
|
724
727
|
- Raises `NotFound` for missing files.
|
|
725
728
|
- Raises `InvalidFormat` for parse failures with format-specific context.
|
|
726
729
|
- Emits `config_file_read` / `config_file_loaded` events.
|
|
730
|
+
- YAML parsing uses `_parse_yaml_bytes` to normalise `None` payloads to empty
|
|
731
|
+
mappings and translate `YAMLError` into actionable `InvalidFormat`.
|
|
727
732
|
|
|
728
733
|
---
|
|
729
734
|
|
|
@@ -915,6 +920,12 @@ generating examples, and surfacing metadata without exposing internal APIs.
|
|
|
915
920
|
|
|
916
921
|
- Rich Click command group with subcommands `read`, `read-json`, `deploy`,
|
|
917
922
|
`generate-examples`, `env-prefix`, `info`, and `fail`.
|
|
923
|
+
- Metadata surfaces (`info`, `--version`) read directly from
|
|
924
|
+
`lib_layered_config.__init__conf__` so automation keeps CLI output in sync
|
|
925
|
+
with `pyproject.toml` without hitting `importlib.metadata` at runtime.
|
|
926
|
+
- Traceback handling delegates to `lib_cli_exit_tools.cli_session`, which
|
|
927
|
+
applies the `--traceback` preference via configuration overrides and restores
|
|
928
|
+
prior settings after each invocation.
|
|
918
929
|
- Helpers to normalise options, render human output, and manage traceback
|
|
919
930
|
preferences.
|
|
920
931
|
- Integrates with ``lib_cli_exit_tools`` for consistent error messaging.
|
|
@@ -945,6 +956,24 @@ helpers.
|
|
|
945
956
|
### Helper Functions (`_render_json`, `_render_human`, `_normalise_*`)
|
|
946
957
|
- **Purpose:** Keep command handlers declarative and reusable.
|
|
947
958
|
|
|
959
|
+
### Metadata Helpers (`version_string`, `describe_distribution`)
|
|
960
|
+
- **Purpose:** Produce CLI-friendly metadata strings sourced from
|
|
961
|
+
`lib_layered_config.__init__conf__`.
|
|
962
|
+
- **Input:** Constants exposed via `__init__conf__.info_lines()` and related
|
|
963
|
+
helpers maintained by release automation.
|
|
964
|
+
- **Output:** Click `--version` banner and `info` command lines.
|
|
965
|
+
- **Location:** `src/lib_layered_config/cli/common.py`
|
|
966
|
+
- **Supporting Docs:** The metadata module also exports
|
|
967
|
+
`metadata_fields()`/`info_lines()` for structured and human rendering.
|
|
968
|
+
|
|
969
|
+
### Traceback Support Helpers (`_session_overrides`)
|
|
970
|
+
- **Purpose:** Derive `lib_cli_exit_tools.cli_session` overrides from parsed CLI
|
|
971
|
+
arguments so the global `--traceback` flag works across entry points.
|
|
972
|
+
- **Input:** Raw argument sequences passed to the root CLI.
|
|
973
|
+
- **Output:** Mapping containing `{"traceback": True}` when verbose tracebacks
|
|
974
|
+
were requested; empty mapping otherwise.
|
|
975
|
+
- **Location:** `src/lib_layered_config/cli/__init__.py`
|
|
976
|
+
|
|
948
977
|
---
|
|
949
978
|
|
|
950
979
|
## Testing Approach
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lib_layered_config"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.1.0"
|
|
4
4
|
description = "Cross-platform layered configuration loader for Python"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"rich-click>=1.9.
|
|
9
|
-
"lib_cli_exit_tools>=1.
|
|
8
|
+
"rich-click>=1.9.3",
|
|
9
|
+
"lib_cli_exit_tools>=2.1.0",
|
|
10
10
|
]
|
|
11
11
|
license = { text = "MIT" }
|
|
12
12
|
authors = [{ name = "bitranox", email = "bitranox@gmail.com" }]
|
|
@@ -27,24 +27,24 @@ Issues = "https://github.com/bitranox/lib_layered_config/issues"
|
|
|
27
27
|
|
|
28
28
|
[project.optional-dependencies]
|
|
29
29
|
dev = [
|
|
30
|
-
"build>=1.3",
|
|
31
|
-
"codecov-cli>=
|
|
30
|
+
"build>=1.3.0",
|
|
31
|
+
"codecov-cli>=11.2.3",
|
|
32
32
|
"coverage[toml]>=7.10.7",
|
|
33
33
|
"hypothesis>=6.140.3",
|
|
34
|
-
"import-linter>=2.
|
|
35
|
-
"bandit>=1.
|
|
36
|
-
"pip-audit>=2.
|
|
37
|
-
"PyYAML>=6.0",
|
|
38
|
-
"pyright>=1.1",
|
|
39
|
-
"pytest>=8.4",
|
|
40
|
-
"pytest-asyncio>=0
|
|
41
|
-
"pytest-cov>=7.0",
|
|
42
|
-
"ruff>=0.
|
|
43
|
-
"textual>=6.
|
|
44
|
-
"twine>=6.2"
|
|
34
|
+
"import-linter>=2.5.2",
|
|
35
|
+
"bandit>=1.8.6",
|
|
36
|
+
"pip-audit>=2.9.0",
|
|
37
|
+
"PyYAML>=6.0.3",
|
|
38
|
+
"pyright>=1.1.406",
|
|
39
|
+
"pytest>=8.4.2",
|
|
40
|
+
"pytest-asyncio>=1.2.0",
|
|
41
|
+
"pytest-cov>=7.0.0",
|
|
42
|
+
"ruff>=0.14.0",
|
|
43
|
+
"textual>=6.3.0",
|
|
44
|
+
"twine>=6.2.0"
|
|
45
45
|
]
|
|
46
46
|
yaml = [
|
|
47
|
-
"PyYAML>=6.0"
|
|
47
|
+
"PyYAML>=6.0.3"
|
|
48
48
|
]
|
|
49
49
|
|
|
50
50
|
[project.scripts]
|
|
@@ -4,7 +4,7 @@ Purpose
|
|
|
4
4
|
-------
|
|
5
5
|
Collect helper functions used by the ``scripts/`` entry points (build, test,
|
|
6
6
|
release) so git helpers and subprocess wrappers live in one place. The behaviour mirrors the operational guidance described in
|
|
7
|
-
``docs/systemdesign/
|
|
7
|
+
``docs/systemdesign/module_reference.md`` and ``DEVELOPMENT.md``.
|
|
8
8
|
|
|
9
9
|
Contents
|
|
10
10
|
--------
|
|
@@ -21,16 +21,19 @@ avoid duplication and keep CI/CD behaviour consistent with documentation.
|
|
|
21
21
|
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
|
+
import json
|
|
24
25
|
import os
|
|
25
26
|
import re
|
|
26
27
|
import shlex
|
|
28
|
+
import shutil
|
|
27
29
|
import subprocess
|
|
28
30
|
import sys
|
|
29
31
|
import tomllib
|
|
32
|
+
import textwrap
|
|
30
33
|
from dataclasses import dataclass
|
|
31
34
|
from pathlib import Path
|
|
32
35
|
from subprocess import CompletedProcess
|
|
33
|
-
from typing import Any,
|
|
36
|
+
from typing import Any, Mapping, Sequence, cast
|
|
34
37
|
from urllib.parse import urlparse
|
|
35
38
|
|
|
36
39
|
|
|
@@ -54,6 +57,12 @@ class ProjectMetadata:
|
|
|
54
57
|
import_package: str
|
|
55
58
|
coverage_source: str
|
|
56
59
|
scripts: dict[str, str]
|
|
60
|
+
metadata_module: Path
|
|
61
|
+
version: str
|
|
62
|
+
summary: str
|
|
63
|
+
author_name: str
|
|
64
|
+
author_email: str
|
|
65
|
+
shell_command: str
|
|
57
66
|
|
|
58
67
|
def github_tarball_url(self, version: str) -> str:
|
|
59
68
|
if self.repo_host == "github.com" and self.repo_owner and self.repo_name:
|
|
@@ -90,6 +99,7 @@ class ProjectMetadata:
|
|
|
90
99
|
summary.append(f"repository={self.repo_url}")
|
|
91
100
|
if self.homepage:
|
|
92
101
|
summary.append(f"homepage={self.homepage}")
|
|
102
|
+
summary.append(f"version={self.version}")
|
|
93
103
|
return tuple(summary)
|
|
94
104
|
|
|
95
105
|
|
|
@@ -131,14 +141,7 @@ def run(
|
|
|
131
141
|
|
|
132
142
|
|
|
133
143
|
def cmd_exists(name: str) -> bool:
|
|
134
|
-
return (
|
|
135
|
-
subprocess.call(
|
|
136
|
-
["bash", "-lc", f"command -v {shlex.quote(name)} >/dev/null 2>&1"],
|
|
137
|
-
stdout=subprocess.DEVNULL,
|
|
138
|
-
stderr=subprocess.DEVNULL,
|
|
139
|
-
)
|
|
140
|
-
== 0
|
|
141
|
-
)
|
|
144
|
+
return shutil.which(name) is not None
|
|
142
145
|
|
|
143
146
|
|
|
144
147
|
def _normalize_slug(value: str) -> str:
|
|
@@ -185,12 +188,11 @@ def _load_pyproject(pyproject: Path) -> dict[str, object]:
|
|
|
185
188
|
if cached is not None:
|
|
186
189
|
return cached
|
|
187
190
|
raw_text = path.read_text(encoding="utf-8")
|
|
188
|
-
data: dict[str, object] = {}
|
|
189
191
|
try:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
parsed_obj = tomllib.loads(raw_text)
|
|
193
|
+
except tomllib.TOMLDecodeError as exc: # pragma: no cover - invalid pyproject fails fast
|
|
194
|
+
msg = f"Unable to parse {path}: {exc}"
|
|
195
|
+
raise ValueError(msg) from exc
|
|
194
196
|
data = {str(key): value for key, value in parsed_obj.items()}
|
|
195
197
|
_PYPROJECT_DATA_CACHE[path] = data
|
|
196
198
|
return data
|
|
@@ -314,6 +316,47 @@ def get_project_metadata(pyproject: Path = Path("pyproject.toml")) -> ProjectMet
|
|
|
314
316
|
coverage_source = _derive_coverage_source(data, import_package)
|
|
315
317
|
scripts = _derive_scripts(data)
|
|
316
318
|
|
|
319
|
+
version = read_version_from_pyproject(pyproject)
|
|
320
|
+
summary = description.strip() if description else ""
|
|
321
|
+
if not summary:
|
|
322
|
+
summary_candidate = project_table.get("summary")
|
|
323
|
+
summary = summary_candidate.strip() if isinstance(summary_candidate, str) else ""
|
|
324
|
+
if not summary:
|
|
325
|
+
summary = name
|
|
326
|
+
|
|
327
|
+
author_name = ""
|
|
328
|
+
author_email = ""
|
|
329
|
+
authors_value = project_table.get("authors")
|
|
330
|
+
if isinstance(authors_value, list):
|
|
331
|
+
authors_list = cast(list[object], authors_value)
|
|
332
|
+
for author_entry in authors_list:
|
|
333
|
+
if not isinstance(author_entry, dict):
|
|
334
|
+
continue
|
|
335
|
+
author_dict = cast(dict[str, object], author_entry)
|
|
336
|
+
name_field = author_dict.get("name")
|
|
337
|
+
email_field = author_dict.get("email")
|
|
338
|
+
if not author_name and isinstance(name_field, str) and name_field.strip():
|
|
339
|
+
author_name = name_field.strip()
|
|
340
|
+
if not author_email and isinstance(email_field, str) and email_field.strip():
|
|
341
|
+
author_email = email_field.strip()
|
|
342
|
+
if not author_name:
|
|
343
|
+
author_name = repo_owner or name
|
|
344
|
+
|
|
345
|
+
shell_command = slug.replace("_", "-")
|
|
346
|
+
preferred_entry = _select_cli_entry(
|
|
347
|
+
scripts,
|
|
348
|
+
(
|
|
349
|
+
slug,
|
|
350
|
+
name,
|
|
351
|
+
import_package,
|
|
352
|
+
import_package.replace("_", "-"),
|
|
353
|
+
),
|
|
354
|
+
)
|
|
355
|
+
if preferred_entry is not None:
|
|
356
|
+
shell_command = preferred_entry[0]
|
|
357
|
+
|
|
358
|
+
metadata_module = (Path("src") / import_package / "__init__conf__.py").resolve()
|
|
359
|
+
|
|
317
360
|
meta = ProjectMetadata(
|
|
318
361
|
name=name,
|
|
319
362
|
description=description,
|
|
@@ -326,11 +369,109 @@ def get_project_metadata(pyproject: Path = Path("pyproject.toml")) -> ProjectMet
|
|
|
326
369
|
import_package=import_package,
|
|
327
370
|
coverage_source=coverage_source,
|
|
328
371
|
scripts=scripts,
|
|
372
|
+
metadata_module=metadata_module,
|
|
373
|
+
version=version,
|
|
374
|
+
summary=summary,
|
|
375
|
+
author_name=author_name,
|
|
376
|
+
author_email=author_email,
|
|
377
|
+
shell_command=shell_command,
|
|
329
378
|
)
|
|
330
379
|
_METADATA_CACHE[path] = meta
|
|
331
380
|
return meta
|
|
332
381
|
|
|
333
382
|
|
|
383
|
+
def _quote(value: str) -> str:
|
|
384
|
+
return json.dumps(value, ensure_ascii=False)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _render_metadata_module(project: ProjectMetadata) -> str:
|
|
388
|
+
homepage = project.homepage or project.repo_url or ""
|
|
389
|
+
body = f'''"""Static package metadata surfaced to CLI commands and documentation.
|
|
390
|
+
|
|
391
|
+
Purpose
|
|
392
|
+
-------
|
|
393
|
+
Expose the current project metadata as simple constants. These values are kept
|
|
394
|
+
in sync with ``pyproject.toml`` by development automation (tests, push
|
|
395
|
+
pipelines), so runtime code does not query packaging metadata.
|
|
396
|
+
|
|
397
|
+
Contents
|
|
398
|
+
--------
|
|
399
|
+
* Module-level constants describing the published package.
|
|
400
|
+
* :func:`print_info` rendering the constants for the CLI ``info`` command.
|
|
401
|
+
|
|
402
|
+
System Role
|
|
403
|
+
-----------
|
|
404
|
+
Lives in the adapters/platform layer; CLI transports import these constants to
|
|
405
|
+
present authoritative project information without invoking packaging APIs.
|
|
406
|
+
"""
|
|
407
|
+
|
|
408
|
+
from __future__ import annotations
|
|
409
|
+
|
|
410
|
+
#: Distribution name declared in ``pyproject.toml``.
|
|
411
|
+
name = {_quote(project.name)}
|
|
412
|
+
#: Human-readable summary shown in CLI help output.
|
|
413
|
+
title = {_quote(project.summary)}
|
|
414
|
+
#: Current release version pulled from ``pyproject.toml`` by automation.
|
|
415
|
+
version = {_quote(project.version)}
|
|
416
|
+
#: Repository homepage presented to users.
|
|
417
|
+
homepage = {_quote(homepage)}
|
|
418
|
+
#: Author attribution surfaced in CLI output.
|
|
419
|
+
author = {_quote(project.author_name)}
|
|
420
|
+
#: Contact email surfaced in CLI output.
|
|
421
|
+
author_email = {_quote(project.author_email)}
|
|
422
|
+
#: Console-script name published by the package.
|
|
423
|
+
shell_command = {_quote(project.shell_command)}
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def print_info() -> None:
|
|
427
|
+
"""Print the summarised metadata block used by the CLI ``info`` command.
|
|
428
|
+
|
|
429
|
+
Why
|
|
430
|
+
Provides a single, auditable rendering function so documentation and
|
|
431
|
+
CLI output always match the system design reference.
|
|
432
|
+
|
|
433
|
+
Side Effects
|
|
434
|
+
Writes to ``stdout``.
|
|
435
|
+
|
|
436
|
+
Examples
|
|
437
|
+
--------
|
|
438
|
+
>>> print_info() # doctest: +ELLIPSIS
|
|
439
|
+
Info for {project.name}:
|
|
440
|
+
...
|
|
441
|
+
"""
|
|
442
|
+
|
|
443
|
+
fields = [
|
|
444
|
+
("name", name),
|
|
445
|
+
("title", title),
|
|
446
|
+
("version", version),
|
|
447
|
+
("homepage", homepage),
|
|
448
|
+
("author", author),
|
|
449
|
+
("author_email", author_email),
|
|
450
|
+
("shell_command", shell_command),
|
|
451
|
+
]
|
|
452
|
+
pad = max(len(label) for label, _ in fields)
|
|
453
|
+
lines = [f"Info for {{name}}:", ""]
|
|
454
|
+
lines.extend(f" {{label.ljust(pad)}} = {{value}}" for label, value in fields)
|
|
455
|
+
print("\\n".join(lines))
|
|
456
|
+
'''
|
|
457
|
+
return textwrap.dedent(body)
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def sync_metadata_module(project: ProjectMetadata) -> None:
|
|
461
|
+
"""Write ``__init__conf__.py`` so the constants mirror ``pyproject.toml``."""
|
|
462
|
+
|
|
463
|
+
content = _render_metadata_module(project)
|
|
464
|
+
module_path = project.metadata_module
|
|
465
|
+
module_path.parent.mkdir(parents=True, exist_ok=True)
|
|
466
|
+
try:
|
|
467
|
+
existing = module_path.read_text(encoding="utf-8")
|
|
468
|
+
except FileNotFoundError:
|
|
469
|
+
existing = ""
|
|
470
|
+
if existing == content:
|
|
471
|
+
return
|
|
472
|
+
module_path.write_text(content, encoding="utf-8")
|
|
473
|
+
|
|
474
|
+
|
|
334
475
|
def read_version_from_pyproject(pyproject: Path = Path("pyproject.toml")) -> str:
|
|
335
476
|
data = _load_pyproject(pyproject)
|
|
336
477
|
project_table = _as_str_mapping(data.get("project"))
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
-
from pathlib import Path
|
|
5
4
|
|
|
6
5
|
import rich_click as click
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
from ._utils import get_project_metadata, run
|
|
10
|
-
except ImportError:
|
|
11
|
-
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
12
|
-
from scripts._utils import get_project_metadata, run
|
|
7
|
+
from ._utils import get_project_metadata, run, sync_metadata_module
|
|
13
8
|
|
|
14
9
|
__all__ = ["build_artifacts"]
|
|
15
10
|
|
|
@@ -27,6 +22,7 @@ def _failure(label: str) -> str:
|
|
|
27
22
|
def build_artifacts() -> None:
|
|
28
23
|
"""Build Python wheel and sdist artifacts."""
|
|
29
24
|
|
|
25
|
+
sync_metadata_module(PROJECT)
|
|
30
26
|
click.echo("[build] Building wheel/sdist via python -m build")
|
|
31
27
|
build_result = run(["python", "-m", "build"], check=False, capture=False)
|
|
32
28
|
click.echo(f"[build] {_status('success') if build_result.code == 0 else _failure('failed')}")
|
|
@@ -39,7 +35,6 @@ def main() -> None: # pragma: no cover
|
|
|
39
35
|
|
|
40
36
|
|
|
41
37
|
if __name__ == "__main__": # pragma: no cover
|
|
42
|
-
|
|
43
|
-
from scripts.cli import main as cli_main
|
|
38
|
+
from .cli import main as cli_main
|
|
44
39
|
|
|
45
40
|
cli_main(["build", *sys.argv[1:]])
|
|
@@ -2,21 +2,16 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
from ._utils import run
|
|
9
|
-
except ImportError:
|
|
10
|
-
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
11
|
-
from scripts._utils import run
|
|
6
|
+
from ._utils import run
|
|
12
7
|
|
|
13
8
|
__all__ = ["bump"]
|
|
14
9
|
|
|
15
10
|
|
|
16
11
|
def bump(
|
|
17
12
|
*,
|
|
18
|
-
version:
|
|
19
|
-
part:
|
|
13
|
+
version: str | None = None,
|
|
14
|
+
part: str | None = None,
|
|
20
15
|
pyproject: Path = Path("pyproject.toml"),
|
|
21
16
|
changelog: Path = Path("CHANGELOG.md"),
|
|
22
17
|
) -> None:
|
|
@@ -32,6 +27,6 @@ def bump(
|
|
|
32
27
|
|
|
33
28
|
|
|
34
29
|
if __name__ == "__main__": # pragma: no cover
|
|
35
|
-
from
|
|
30
|
+
from .cli import main as cli_main
|
|
36
31
|
|
|
37
32
|
cli_main(["bump", *sys.argv[1:]])
|
|
@@ -3,11 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import sys
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
from .bump import bump
|
|
8
|
-
except ImportError:
|
|
9
|
-
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
10
|
-
from scripts.bump import bump
|
|
6
|
+
from .bump import bump
|
|
11
7
|
|
|
12
8
|
__all__ = ["bump_major"]
|
|
13
9
|
|
|
@@ -19,6 +15,6 @@ def bump_major(pyproject: Path = Path("pyproject.toml"), changelog: Path = Path(
|
|
|
19
15
|
|
|
20
16
|
|
|
21
17
|
if __name__ == "__main__": # pragma: no cover
|
|
22
|
-
from
|
|
18
|
+
from .cli import main as cli_main
|
|
23
19
|
|
|
24
20
|
cli_main(["bump", "--part", "major", *sys.argv[1:]])
|
|
@@ -3,11 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import sys
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
from .bump import bump
|
|
8
|
-
except ImportError:
|
|
9
|
-
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
10
|
-
from scripts.bump import bump
|
|
6
|
+
from .bump import bump
|
|
11
7
|
|
|
12
8
|
__all__ = ["bump_minor"]
|
|
13
9
|
|
|
@@ -19,6 +15,6 @@ def bump_minor(pyproject: Path = Path("pyproject.toml"), changelog: Path = Path(
|
|
|
19
15
|
|
|
20
16
|
|
|
21
17
|
if __name__ == "__main__": # pragma: no cover
|
|
22
|
-
from
|
|
18
|
+
from .cli import main as cli_main
|
|
23
19
|
|
|
24
20
|
cli_main(["bump", "--part", "minor", *sys.argv[1:]])
|