conclude 1.0.0__tar.gz → 1.0.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.
Files changed (62) hide show
  1. {conclude-1.0.0 → conclude-1.0.2}/.gitignore +6 -0
  2. conclude-1.0.2/CHANGELOG.md +67 -0
  3. {conclude-1.0.0 → conclude-1.0.2}/PKG-INFO +18 -18
  4. {conclude-1.0.0 → conclude-1.0.2}/README.md +14 -14
  5. conclude-1.0.2/RELEASING.md +56 -0
  6. {conclude-1.0.0 → conclude-1.0.2}/docs/comparison.md +1 -1
  7. conclude-1.0.2/docs/concept.md +401 -0
  8. conclude-1.0.2/hatch_build.py +33 -0
  9. {conclude-1.0.0 → conclude-1.0.2}/pyproject.toml +24 -3
  10. conclude-1.0.2/spec/README.md +72 -0
  11. conclude-1.0.2/spec/casters.json +438 -0
  12. conclude-1.0.2/spec/config_layers.json +571 -0
  13. conclude-1.0.2/spec/config_tables.json +221 -0
  14. conclude-1.0.2/spec/dotenv.json +239 -0
  15. conclude-1.0.2/spec/gitignore.json +189 -0
  16. conclude-1.0.2/spec/guard.json +273 -0
  17. conclude-1.0.2/spec/invocation.json +1241 -0
  18. conclude-1.0.2/spec/merge.json +1076 -0
  19. conclude-1.0.2/spec/naming.json +94 -0
  20. conclude-1.0.2/spec/sources.json +315 -0
  21. conclude-1.0.2/spec/templates.json +603 -0
  22. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/__init__.py +1 -1
  23. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/env.py +5 -1
  24. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_comparison.py +15 -7
  25. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_docs.py +22 -7
  26. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_env.py +59 -0
  27. conclude-1.0.2/test/test_repo_hygiene.py +413 -0
  28. conclude-1.0.2/test/test_spec.py +473 -0
  29. conclude-1.0.0/.github/dependabot.yml +0 -10
  30. conclude-1.0.0/.github/workflows/ci.yml +0 -109
  31. conclude-1.0.0/.github/workflows/publish.yml +0 -125
  32. conclude-1.0.0/CHANGELOG.md +0 -7
  33. conclude-1.0.0/tests/test_repo_hygiene.py +0 -190
  34. conclude-1.0.0/uv.lock +0 -948
  35. {conclude-1.0.0 → conclude-1.0.2}/LICENSE +0 -0
  36. {conclude-1.0.0 → conclude-1.0.2}/docs/guide.md +0 -0
  37. {conclude-1.0.0 → conclude-1.0.2}/docs/reference.md +0 -0
  38. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/app.py +0 -0
  39. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/casters.py +0 -0
  40. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/developer.py +0 -0
  41. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/files.py +0 -0
  42. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/formatters.py +0 -0
  43. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/guard.py +0 -0
  44. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/infer.py +0 -0
  45. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/merge.py +0 -0
  46. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/naming.py +0 -0
  47. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/paths.py +0 -0
  48. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/py.typed +0 -0
  49. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/templates.py +0 -0
  50. {conclude-1.0.0 → conclude-1.0.2}/src/conclude/tomlwrite.py +0 -0
  51. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_app.py +0 -0
  52. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_casters.py +0 -0
  53. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_developer.py +0 -0
  54. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_dotenv_guard.py +0 -0
  55. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_files.py +0 -0
  56. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_formatters.py +0 -0
  57. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_guard.py +0 -0
  58. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_infer.py +0 -0
  59. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_merge.py +0 -0
  60. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_naming.py +0 -0
  61. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_templates.py +0 -0
  62. {conclude-1.0.0/tests → conclude-1.0.2/test}/test_tomlwrite_and_paths.py +0 -0
@@ -19,6 +19,12 @@ htmlcov/
19
19
  # Build artifacts
20
20
  build/
21
21
  dist/
22
+ *.tgz
23
+ *.tsbuildinfo
24
+
25
+ # Node
26
+ node_modules/
27
+ .verdaccio-storage/
22
28
 
23
29
  # Virtual environments
24
30
  .venv/
@@ -0,0 +1,67 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file. The
4
+ format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
+ and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.0.2] - 2026-09-26
8
+
9
+ ### Fixed
10
+
11
+ - Python tree is now under `python/` to match `node/` tree.
12
+
13
+ ## [1.0.1] - 2026-09-20
14
+
15
+ ### Fixed
16
+
17
+ - `.env` files, and dotenv-format developer files, are now read as UTF-8 on
18
+ every platform, and a leading byte-order mark is ignored. Previously the
19
+ platform's default text encoding was used, which garbled non-ASCII values
20
+ on Windows, and a byte-order mark (which some Windows editors add) made
21
+ the first variable silently disappear.
22
+
23
+ ### Changed
24
+
25
+ - The package's OS classifier is now `Operating System :: Unix` rather than
26
+ `OS Independent`: the tests run on Linux, and the paths conclude uses
27
+ (`~/.config`, `/etc`) are Unix conventions.
28
+ - The source distribution no longer includes `uv.lock` or `.github/`.
29
+
30
+ ## [1.0.0] - 2026-09-20
31
+
32
+ First public release.
33
+
34
+ ### Added
35
+
36
+ - **Inference from one dict.** `App(name, defaults)` infers each setting's
37
+ environment variable, config-file key, CLI flag, caster and formatter
38
+ from its name, type and default. `opt(type)` declares a typed setting
39
+ that starts out unset.
40
+ - **Layered resolution:** `defaults < system < user < project < env <
41
+ developer < CLI`, with per-key casting at every layer.
42
+ - **Config files:** user (`~/.config/<name>/config.toml`) and project
43
+ (`./.config.toml`, plus `.config.*.toml` siblings, configurable or
44
+ disabled with `config_cwd_aux_pattern`) files; an opt-in system-wide
45
+ `/etc/<name>/config.toml`; two-level `[PARENT]` / `[PARENT.CHILD]`
46
+ tables; a positional shorthand that picks a table.
47
+ - **`.env` fallback** beneath real environment variables (opt-in).
48
+ `dotenv_require_gitignored=True` holds it to the developer file's
49
+ gitignore guard, and `App.dotenv_status()` reports its state.
50
+ - **Developer config layer** (opt-in via `pyproject_path`): a private,
51
+ gitignored, project-local file named by `pyproject.toml`'s
52
+ `tool.conclude.developer.config` that beats ambient environment
53
+ variables and loses to the CLI. The file is TOML if its name ends in
54
+ `.toml` and dotenv otherwise (say `.env.local`, keyed by the app's
55
+ environment variable names, so it can be shared with docker compose or
56
+ direnv). It is applied only when the file exists, sits in a git working
57
+ tree, and is covered by its ignore rules, checked with the optional
58
+ `pathspec` extra: `pip install 'conclude[gitignore]'`.
59
+ `<NAME>_DEVELOPER_CONFIG=off` is a kill switch, and
60
+ `App.developer_status()` reports the layer's state and reason as data.
61
+ - **`describe_sources()`** for `--help`, listing every source and, for the
62
+ developer layer and a strict `.env`, whether it is active and why not.
63
+ - **Generated templates and docs:** `format_env()`, `format_toml()` and
64
+ `format_cli()` render every setting next to its default.
65
+ - **`--print-invocation`** and `format_invocation()`: reproduce a resolved
66
+ configuration as a command line.
67
+ - Standard library only; fully typed (`py.typed`); Python 3.11 and newer.
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: conclude
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Set up your defaults; conclude infers the env vars, config-file keys, and CLI flags, and resolves them all into one settings object.
5
5
  Project-URL: Homepage, https://github.com/tanakapayam/conclude
6
- Project-URL: Documentation, https://github.com/tanakapayam/conclude/blob/main/docs/guide.md
6
+ Project-URL: Documentation, https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md
7
7
  Project-URL: Repository, https://github.com/tanakapayam/conclude
8
8
  Project-URL: Issues, https://github.com/tanakapayam/conclude/issues
9
- Project-URL: Changelog, https://github.com/tanakapayam/conclude/blob/main/CHANGELOG.md
9
+ Project-URL: Changelog, https://github.com/tanakapayam/conclude/blob/main/python/CHANGELOG.md
10
10
  Author: Payam Tanaka
11
11
  License-Expression: MIT
12
12
  License-File: LICENSE
@@ -14,7 +14,7 @@ Keywords: cli,config,configuration,dotenv,environment-variables,settings,toml
14
14
  Classifier: Development Status :: 5 - Production/Stable
15
15
  Classifier: Environment :: Console
16
16
  Classifier: Intended Audience :: Developers
17
- Classifier: Operating System :: OS Independent
17
+ Classifier: Operating System :: Unix
18
18
  Classifier: Programming Language :: Python :: 3
19
19
  Classifier: Programming Language :: Python :: 3 :: Only
20
20
  Classifier: Programming Language :: Python :: 3.11
@@ -92,7 +92,7 @@ conclude deliberately does not do schema validation (values are cast,
92
92
  not validated), secret-manager backends, YAML or JSON files, or named
93
93
  environments; the table says where to look for those. For a dated,
94
94
  feature-by-feature comparison, see
95
- [How conclude compares](https://github.com/tanakapayam/conclude/blob/main/docs/comparison.md).
95
+ [How conclude compares](https://github.com/tanakapayam/conclude/blob/main/docs/python/comparison.md).
96
96
 
97
97
  ## Design principles
98
98
 
@@ -195,28 +195,28 @@ app = conclude.App(
195
195
  The developer file is always guarded, and `.env` is when you ask: the
196
196
  file is used only if it exists, sits in a git working tree, and is
197
197
  gitignored. Otherwise it is skipped quietly, and `describe_sources()`
198
- says why. Details: [the `.env` file](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#6-local-development-a-env-file), [the developer file](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#8-a-private-developer-config-file).
198
+ says why. Details: [the `.env` file](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#6-local-development-a-env-file), [the developer file](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#8-a-private-developer-config-file).
199
199
 
200
200
  ## What's in it
201
201
 
202
202
  - **Inference** of casters, formatters, CLI flags, env var names and
203
203
  config keys from the defaults alone; `opt(type)` for a setting that
204
- starts out unset ([guide, section 1](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#1-the-bare-minimum), [section 3](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#3-when-inference-isnt-quite-enough-casters-and-formatters)).
204
+ starts out unset ([guide, section 1](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#1-the-bare-minimum), [section 3](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#3-when-inference-isnt-quite-enough-casters-and-formatters)).
205
205
  - **Config-file tables**, including a positional shorthand that picks a
206
- table per recipient/deck/profile ([section 4](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#4-a-positional-shorthand--per-recipient-config-tables)).
206
+ table per recipient/deck/profile ([section 4](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#4-a-positional-shorthand--per-recipient-config-tables)).
207
207
  - **`--print-invocation`** and `App.format_invocation()`: print the
208
208
  command line that reproduces a resolved configuration
209
- ([section 5](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#5-debugging-and-documentation---print-invocation)).
209
+ ([section 5](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#5-debugging-and-documentation---print-invocation)).
210
210
  - **A `.env` fallback**, optionally required to be gitignored, sitting
211
- beneath real environment variables ([section 6](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#6-local-development-a-env-file)).
212
- - **A system-wide config file**, the lowest-priority file ([section 7](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#7-machine-wide-defaults-a-system-config-file)).
211
+ beneath real environment variables ([section 6](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#6-local-development-a-env-file)).
212
+ - **A system-wide config file**, the lowest-priority file ([section 7](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#7-machine-wide-defaults-a-system-config-file)).
213
213
  - **A private developer file**, TOML or dotenv, that beats ambient
214
- environment variables ([section 8](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#8-a-private-developer-config-file)).
214
+ environment variables ([section 8](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#8-a-private-developer-config-file)).
215
215
  - **`describe_sources()`** for `--help`: which sources are in play, and
216
- why a given one isn't ([section 9](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#9-turning-off-a-source-and-telling-the-user)).
216
+ why a given one isn't ([section 9](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#9-turning-off-a-source-and-telling-the-user)).
217
217
  - **Templates and docs generated from your defaults**:
218
218
  `format_env()`, `format_toml()`, `format_cli()`
219
- ([section 10](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#10-generating-docs-and-templates)).
219
+ ([section 10](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#10-generating-docs-and-templates)).
220
220
  - Standard library only, fully typed (`py.typed`), Python 3.11+. The
221
221
  gitignore check (developer file, strict `.env`) uses the optional
222
222
  `pathspec` package.
@@ -237,16 +237,16 @@ pip install 'conclude[gitignore]'
237
237
 
238
238
  ## Documentation
239
239
 
240
- - [Guide](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md) -- builds a small CLI, `remind`, one
240
+ - [Guide](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md) -- builds a small CLI, `remind`, one
241
241
  idea at a time.
242
- - [Reference](https://github.com/tanakapayam/conclude/blob/main/docs/reference.md) -- every class, method, and
242
+ - [Reference](https://github.com/tanakapayam/conclude/blob/main/docs/python/reference.md) -- every class, method, and
243
243
  module.
244
- - [Changelog](https://github.com/tanakapayam/conclude/blob/main/CHANGELOG.md).
244
+ - [Changelog](https://github.com/tanakapayam/conclude/blob/main/python/CHANGELOG.md).
245
245
 
246
246
  ## Development
247
247
 
248
248
  ```
249
- uv sync
249
+ uv sync --locked # fails if uv.lock is out of date; `uv lock` refreshes it
250
250
  uv run pytest
251
251
  uv run ruff check . && uv run ruff format --check .
252
252
  uv run mypy
@@ -61,7 +61,7 @@ conclude deliberately does not do schema validation (values are cast,
61
61
  not validated), secret-manager backends, YAML or JSON files, or named
62
62
  environments; the table says where to look for those. For a dated,
63
63
  feature-by-feature comparison, see
64
- [How conclude compares](https://github.com/tanakapayam/conclude/blob/main/docs/comparison.md).
64
+ [How conclude compares](https://github.com/tanakapayam/conclude/blob/main/docs/python/comparison.md).
65
65
 
66
66
  ## Design principles
67
67
 
@@ -164,28 +164,28 @@ app = conclude.App(
164
164
  The developer file is always guarded, and `.env` is when you ask: the
165
165
  file is used only if it exists, sits in a git working tree, and is
166
166
  gitignored. Otherwise it is skipped quietly, and `describe_sources()`
167
- says why. Details: [the `.env` file](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#6-local-development-a-env-file), [the developer file](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#8-a-private-developer-config-file).
167
+ says why. Details: [the `.env` file](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#6-local-development-a-env-file), [the developer file](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#8-a-private-developer-config-file).
168
168
 
169
169
  ## What's in it
170
170
 
171
171
  - **Inference** of casters, formatters, CLI flags, env var names and
172
172
  config keys from the defaults alone; `opt(type)` for a setting that
173
- starts out unset ([guide, section 1](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#1-the-bare-minimum), [section 3](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#3-when-inference-isnt-quite-enough-casters-and-formatters)).
173
+ starts out unset ([guide, section 1](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#1-the-bare-minimum), [section 3](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#3-when-inference-isnt-quite-enough-casters-and-formatters)).
174
174
  - **Config-file tables**, including a positional shorthand that picks a
175
- table per recipient/deck/profile ([section 4](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#4-a-positional-shorthand--per-recipient-config-tables)).
175
+ table per recipient/deck/profile ([section 4](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#4-a-positional-shorthand--per-recipient-config-tables)).
176
176
  - **`--print-invocation`** and `App.format_invocation()`: print the
177
177
  command line that reproduces a resolved configuration
178
- ([section 5](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#5-debugging-and-documentation---print-invocation)).
178
+ ([section 5](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#5-debugging-and-documentation---print-invocation)).
179
179
  - **A `.env` fallback**, optionally required to be gitignored, sitting
180
- beneath real environment variables ([section 6](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#6-local-development-a-env-file)).
181
- - **A system-wide config file**, the lowest-priority file ([section 7](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#7-machine-wide-defaults-a-system-config-file)).
180
+ beneath real environment variables ([section 6](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#6-local-development-a-env-file)).
181
+ - **A system-wide config file**, the lowest-priority file ([section 7](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#7-machine-wide-defaults-a-system-config-file)).
182
182
  - **A private developer file**, TOML or dotenv, that beats ambient
183
- environment variables ([section 8](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#8-a-private-developer-config-file)).
183
+ environment variables ([section 8](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#8-a-private-developer-config-file)).
184
184
  - **`describe_sources()`** for `--help`: which sources are in play, and
185
- why a given one isn't ([section 9](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#9-turning-off-a-source-and-telling-the-user)).
185
+ why a given one isn't ([section 9](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#9-turning-off-a-source-and-telling-the-user)).
186
186
  - **Templates and docs generated from your defaults**:
187
187
  `format_env()`, `format_toml()`, `format_cli()`
188
- ([section 10](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md#10-generating-docs-and-templates)).
188
+ ([section 10](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md#10-generating-docs-and-templates)).
189
189
  - Standard library only, fully typed (`py.typed`), Python 3.11+. The
190
190
  gitignore check (developer file, strict `.env`) uses the optional
191
191
  `pathspec` package.
@@ -206,16 +206,16 @@ pip install 'conclude[gitignore]'
206
206
 
207
207
  ## Documentation
208
208
 
209
- - [Guide](https://github.com/tanakapayam/conclude/blob/main/docs/guide.md) -- builds a small CLI, `remind`, one
209
+ - [Guide](https://github.com/tanakapayam/conclude/blob/main/docs/python/guide.md) -- builds a small CLI, `remind`, one
210
210
  idea at a time.
211
- - [Reference](https://github.com/tanakapayam/conclude/blob/main/docs/reference.md) -- every class, method, and
211
+ - [Reference](https://github.com/tanakapayam/conclude/blob/main/docs/python/reference.md) -- every class, method, and
212
212
  module.
213
- - [Changelog](https://github.com/tanakapayam/conclude/blob/main/CHANGELOG.md).
213
+ - [Changelog](https://github.com/tanakapayam/conclude/blob/main/python/CHANGELOG.md).
214
214
 
215
215
  ## Development
216
216
 
217
217
  ```
218
- uv sync
218
+ uv sync --locked # fails if uv.lock is out of date; `uv lock` refreshes it
219
219
  uv run pytest
220
220
  uv run ruff check . && uv run ruff format --check .
221
221
  uv run mypy
@@ -0,0 +1,56 @@
1
+ # Releasing conclude (Python)
2
+
3
+ ```
4
+ release published (tag python-v<version>)
5
+ |
6
+ v
7
+ ci ---> build ---> publish-testpypi (workflow_dispatch, target testpypi: a dry run)
8
+ | |
9
+ | +-> publish-pypi waits for a reviewer to approve the `pypi`
10
+ | environment, then publishes with trusted
11
+ | publishing (no stored token)
12
+ |
13
+ +-- one sdist + wheel, built once, uploaded as an artifact and promoted unchanged
14
+ ```
15
+
16
+ TestPyPI is the dry run here (there is no separate staging registry, unlike the Node
17
+ package's GitHub Packages stage): `workflow_dispatch` with `target: testpypi` builds,
18
+ checks, and publishes to TestPyPI, and installs nothing back automatically -- try it
19
+ yourself with `pip install -i https://test.pypi.org/simple/ conclude`. A real release
20
+ (`target: pypi`, or a published GitHub Release) is checked, gated behind a required
21
+ reviewer on the `pypi` environment, then published to PyPI.
22
+
23
+ ## One-time setup
24
+
25
+ 1. **Environments** (repository Settings, Environments):
26
+ - `testpypi`: no protection needed.
27
+ - `pypi`: add yourself (or a team) under *Required reviewers*; that is the approval
28
+ gate.
29
+ 2. **Trusted publisher**, on both <https://pypi.org> and <https://test.pypi.org>, for the
30
+ project `conclude`: this repository, workflow **`python-publish.yml`**, environment
31
+ `pypi` (`testpypi` on TestPyPI). For a project that does not exist yet, use
32
+ "Publishing" -> "Add a new pending publisher".
33
+ 3. Nothing to store: publishing uses OIDC. No PyPI token exists.
34
+
35
+ > **If this workflow was ever registered under a different filename** (for example the
36
+ > project's original `publish.yml`, before this repository moved every language into its
37
+ > own top-level directory): trusted publishing is matched by *workflow filename*, so the
38
+ > existing entries on PyPI and TestPyPI must be updated to `python-publish.yml` -- edit
39
+ > them under "Publishing" on each site -- or the next release fails with a permission
40
+ > error, not a helpful one.
41
+
42
+ ## Releasing
43
+
44
+ 1. Bump `__version__` in `src/conclude/__init__.py` (the single source of truth; see
45
+ `[tool.hatch.version]` in `pyproject.toml`), and the version in
46
+ `docs/python/comparison.md`'s "Verified" line if it's stale. Put a dated entry in
47
+ `CHANGELOG.md` (the release is refused while it says *Unreleased*).
48
+ 2. Merge to `main`.
49
+ 3. Create a GitHub Release from a new tag `python-v<version>` on `main`.
50
+ 4. `publish-pypi` waits for a reviewer to **approve the `pypi` deployment**; approve it
51
+ when you are happy.
52
+
53
+ ## Rehearsing
54
+
55
+ Actions, Publish, Run workflow, target `testpypi` -- builds, checks, and publishes to
56
+ TestPyPI without touching the `pypi` environment or a real version on PyPI.
@@ -3,7 +3,7 @@
3
3
  A feature-by-feature comparison with five libraries people often reach
4
4
  for instead. **Verified 2026-09-19** against each project's
5
5
  documentation and its PyPI metadata; the versions checked are
6
- conclude 1.0.0, ConfigArgParse 1.7.7, jsonargparse 4.52.0,
6
+ conclude 1.0.2, ConfigArgParse 1.7.7, jsonargparse 4.52.0,
7
7
  pydantic-settings 2.15.0, Dynaconf 3.3.5 and python-decouple 3.8.
8
8
 
9
9
  A dash (--) means no built-in support that the project's documentation