confargs 0.2.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.
- confargs-0.2.0/.gitattributes +29 -0
- confargs-0.2.0/.github/workflows/ci.yml +66 -0
- confargs-0.2.0/.github/workflows/pr-title.yml +35 -0
- confargs-0.2.0/.github/workflows/publish.yml +38 -0
- confargs-0.2.0/.github/workflows/release-please.yml +41 -0
- confargs-0.2.0/.gitignore +26 -0
- confargs-0.2.0/.pre-commit-config.yaml +28 -0
- confargs-0.2.0/.release-please-manifest.json +3 -0
- confargs-0.2.0/CHANGELOG.md +86 -0
- confargs-0.2.0/LICENSE +21 -0
- confargs-0.2.0/PKG-INFO +222 -0
- confargs-0.2.0/README.md +197 -0
- confargs-0.2.0/examples/README.md +22 -0
- confargs-0.2.0/examples/demo.py +77 -0
- confargs-0.2.0/examples/example.args +10 -0
- confargs-0.2.0/pyproject.toml +80 -0
- confargs-0.2.0/release-please-config.json +32 -0
- confargs-0.2.0/src/confargs/__init__.py +44 -0
- confargs-0.2.0/src/confargs/argfile.py +77 -0
- confargs-0.2.0/src/confargs/base.py +72 -0
- confargs-0.2.0/src/confargs/cli.py +167 -0
- confargs-0.2.0/src/confargs/coercion.py +129 -0
- confargs-0.2.0/src/confargs/demo.py +77 -0
- confargs-0.2.0/src/confargs/env_source.py +50 -0
- confargs-0.2.0/src/confargs/exceptions.py +62 -0
- confargs-0.2.0/src/confargs/help.py +79 -0
- confargs-0.2.0/src/confargs/namespace.py +69 -0
- confargs-0.2.0/src/confargs/options.py +261 -0
- confargs-0.2.0/src/confargs/processor.py +250 -0
- confargs-0.2.0/src/confargs/py.typed +0 -0
- confargs-0.2.0/src/confargs/toml_source.py +105 -0
- confargs-0.2.0/tests/__init__.py +0 -0
- confargs-0.2.0/tests/robot_cli.py +222 -0
- confargs-0.2.0/tests/test_cli.py +156 -0
- confargs-0.2.0/tests/test_coercion.py +151 -0
- confargs-0.2.0/tests/test_eager.py +144 -0
- confargs-0.2.0/tests/test_env_source.py +66 -0
- confargs-0.2.0/tests/test_example.py +80 -0
- confargs-0.2.0/tests/test_help.py +73 -0
- confargs-0.2.0/tests/test_options.py +169 -0
- confargs-0.2.0/tests/test_processor.py +258 -0
- confargs-0.2.0/tests/test_robot_cli.py +232 -0
- confargs-0.2.0/tests/test_toml_source.py +128 -0
- confargs-0.2.0/uv.lock +788 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Normalize all text files to LF on checkout and commit.
|
|
2
|
+
* text=auto eol=lf
|
|
3
|
+
|
|
4
|
+
# Explicit text file declarations.
|
|
5
|
+
*.py text eol=lf
|
|
6
|
+
*.pyi text eol=lf
|
|
7
|
+
*.toml text eol=lf
|
|
8
|
+
*.cfg text eol=lf
|
|
9
|
+
*.ini text eol=lf
|
|
10
|
+
*.yml text eol=lf
|
|
11
|
+
*.yaml text eol=lf
|
|
12
|
+
*.md text eol=lf
|
|
13
|
+
*.rst text eol=lf
|
|
14
|
+
*.txt text eol=lf
|
|
15
|
+
*.json text eol=lf
|
|
16
|
+
*.sh text eol=lf
|
|
17
|
+
.gitignore text eol=lf
|
|
18
|
+
.gitattributes text eol=lf
|
|
19
|
+
py.typed text eol=lf
|
|
20
|
+
|
|
21
|
+
# Binary files (never normalized).
|
|
22
|
+
*.png binary
|
|
23
|
+
*.jpg binary
|
|
24
|
+
*.jpeg binary
|
|
25
|
+
*.gif binary
|
|
26
|
+
*.ico binary
|
|
27
|
+
*.whl binary
|
|
28
|
+
*.gz binary
|
|
29
|
+
*.zip binary
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ci-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
env:
|
|
13
|
+
# Pin uv for reproducible CI; bump deliberately.
|
|
14
|
+
UV_VERSION: "0.12.7"
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
env:
|
|
20
|
+
UV_PYTHON: "3.12"
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v3
|
|
25
|
+
with:
|
|
26
|
+
version: ${{ env.UV_VERSION }}
|
|
27
|
+
enable-cache: true
|
|
28
|
+
- name: Sync (locked)
|
|
29
|
+
run: uv sync --locked
|
|
30
|
+
- name: Ruff (lint)
|
|
31
|
+
run: uv run ruff check .
|
|
32
|
+
- name: Ruff (format check)
|
|
33
|
+
run: uv run ruff format --check .
|
|
34
|
+
- name: Mypy
|
|
35
|
+
run: uv run mypy
|
|
36
|
+
|
|
37
|
+
test:
|
|
38
|
+
name: test (${{ matrix.os }}, py${{ matrix.python-version }})
|
|
39
|
+
runs-on: ${{ matrix.os }}
|
|
40
|
+
env:
|
|
41
|
+
# setup-uv@v3 has no `python-version` input, so uv picks the interpreter
|
|
42
|
+
# from UV_PYTHON (downloading it if needed) for each matrix entry.
|
|
43
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
44
|
+
strategy:
|
|
45
|
+
fail-fast: false
|
|
46
|
+
# Test every supported Python on Linux; on the costlier Windows runners
|
|
47
|
+
# cover only the oldest and newest supported versions.
|
|
48
|
+
matrix:
|
|
49
|
+
os: [ubuntu-latest]
|
|
50
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
51
|
+
include:
|
|
52
|
+
- os: windows-latest
|
|
53
|
+
python-version: "3.10"
|
|
54
|
+
- os: windows-latest
|
|
55
|
+
python-version: "3.13"
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
- name: Install uv
|
|
59
|
+
uses: astral-sh/setup-uv@v3
|
|
60
|
+
with:
|
|
61
|
+
version: ${{ env.UV_VERSION }}
|
|
62
|
+
enable-cache: true
|
|
63
|
+
- name: Sync (locked)
|
|
64
|
+
run: uv sync --locked
|
|
65
|
+
- name: Run tests
|
|
66
|
+
run: uv run pytest --cov=confargs --cov-report=xml
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: PR Title Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, edited, synchronize, reopened]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
pull-requests: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
lint-pr-title:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: amannn/action-semantic-pull-request@v5
|
|
15
|
+
env:
|
|
16
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
17
|
+
with:
|
|
18
|
+
types: |
|
|
19
|
+
feat
|
|
20
|
+
fix
|
|
21
|
+
docs
|
|
22
|
+
style
|
|
23
|
+
refactor
|
|
24
|
+
perf
|
|
25
|
+
test
|
|
26
|
+
build
|
|
27
|
+
ci
|
|
28
|
+
chore
|
|
29
|
+
revert
|
|
30
|
+
requireScope: false
|
|
31
|
+
subjectPattern: ^.+$
|
|
32
|
+
subjectPatternError: |
|
|
33
|
+
PR title must follow the Conventional Commits format:
|
|
34
|
+
type: description
|
|
35
|
+
Example: feat: add eager option support
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
# Automated releases are handled by release-please (.github/workflows/release-please.yml),
|
|
4
|
+
# which builds and publishes on release creation. This workflow is a manual
|
|
5
|
+
# fallback for publishing a release out-of-band.
|
|
6
|
+
on:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v3
|
|
16
|
+
- name: Build sdist and wheel
|
|
17
|
+
run: uv build
|
|
18
|
+
- name: Upload artifacts
|
|
19
|
+
uses: actions/upload-artifact@v4
|
|
20
|
+
with:
|
|
21
|
+
name: dist
|
|
22
|
+
path: dist/
|
|
23
|
+
|
|
24
|
+
publish:
|
|
25
|
+
needs: build
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
environment: pypi
|
|
28
|
+
permissions:
|
|
29
|
+
# Required for PyPI trusted publishing (OIDC); no API token needed.
|
|
30
|
+
id-token: write
|
|
31
|
+
steps:
|
|
32
|
+
- name: Download artifacts
|
|
33
|
+
uses: actions/download-artifact@v4
|
|
34
|
+
with:
|
|
35
|
+
name: dist
|
|
36
|
+
path: dist/
|
|
37
|
+
- name: Publish to PyPI
|
|
38
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Release Please
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
pull-requests: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release-please:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
outputs:
|
|
16
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
17
|
+
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
18
|
+
steps:
|
|
19
|
+
- uses: googleapis/release-please-action@v4
|
|
20
|
+
id: release
|
|
21
|
+
with:
|
|
22
|
+
config-file: release-please-config.json
|
|
23
|
+
manifest-file: .release-please-manifest.json
|
|
24
|
+
|
|
25
|
+
publish:
|
|
26
|
+
needs: release-please
|
|
27
|
+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
environment: pypi
|
|
30
|
+
permissions:
|
|
31
|
+
# Required for PyPI trusted publishing (OIDC); no API token needed.
|
|
32
|
+
id-token: write
|
|
33
|
+
contents: read
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- name: Install uv
|
|
37
|
+
uses: astral-sh/setup-uv@v3
|
|
38
|
+
- name: Build sdist and wheel
|
|
39
|
+
run: uv build
|
|
40
|
+
- name: Publish to PyPI
|
|
41
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
|
|
13
|
+
# uv: the lockfile is committed for reproducible installs and CI caching.
|
|
14
|
+
|
|
15
|
+
# Tooling caches
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
htmlcov/
|
|
21
|
+
coverage.xml
|
|
22
|
+
|
|
23
|
+
# Editors / OS
|
|
24
|
+
.idea/
|
|
25
|
+
.vscode/
|
|
26
|
+
.DS_Store
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
default_language_version:
|
|
2
|
+
python: python3
|
|
3
|
+
|
|
4
|
+
repos:
|
|
5
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
6
|
+
rev: v5.0.0
|
|
7
|
+
hooks:
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
- id: end-of-file-fixer
|
|
11
|
+
- id: trailing-whitespace
|
|
12
|
+
- id: mixed-line-ending
|
|
13
|
+
args: [--fix=lf]
|
|
14
|
+
|
|
15
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
16
|
+
rev: v0.16.5
|
|
17
|
+
hooks:
|
|
18
|
+
- id: ruff
|
|
19
|
+
args: [--fix]
|
|
20
|
+
- id: ruff-format
|
|
21
|
+
|
|
22
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
23
|
+
rev: v2.3.1
|
|
24
|
+
hooks:
|
|
25
|
+
- id: mypy
|
|
26
|
+
additional_dependencies: ["tomli>=2.0"]
|
|
27
|
+
pass_filenames: false
|
|
28
|
+
args: [--config-file=pyproject.toml]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to **confargs** are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
> **Pre-1.0 note:** while the version is `0.x.y`, the public API is still
|
|
9
|
+
> stabilising. Minor (`0.X.0`) releases may include breaking changes; patch
|
|
10
|
+
> (`0.x.Y`) releases are reserved for backwards-compatible fixes.
|
|
11
|
+
|
|
12
|
+
## [0.2.0](https://github.com/MarketSquare/confargs/compare/v0.1.0...v0.2.0) (2026-08-28)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* add argconfig-demo console entry-point ([d0249d8](https://github.com/MarketSquare/confargs/commit/d0249d83c0d37ab1e71d10ef41d4a9d79ad80b0a))
|
|
18
|
+
* add ConfigurationProcessor, Namespace and discovery controls ([8395145](https://github.com/MarketSquare/confargs/commit/83951450a7b9b81d6f47bb72cd2b0da64f61a373))
|
|
19
|
+
* add core option model and ArgConfig base ([a5e1c4d](https://github.com/MarketSquare/confargs/commit/a5e1c4d15c0bc6a4d1a4ce79a7cd69a12c6361a5))
|
|
20
|
+
* add eager options and argument-file expansion ([b6837bd](https://github.com/MarketSquare/confargs/commit/b6837bd4fda7c10efccb4bacd5fab420475ca7f9))
|
|
21
|
+
* add environment-variable source ([8b34439](https://github.com/MarketSquare/confargs/commit/8b34439eb153be00fd443eff0dcaba4ced96b4f2))
|
|
22
|
+
* add minimal command-line tokenizer ([e2c07bd](https://github.com/MarketSquare/confargs/commit/e2c07bd00c4ea8b3cb4f793b37b041dbce90ffd0))
|
|
23
|
+
* add TOML loading and config file discovery ([3e8171c](https://github.com/MarketSquare/confargs/commit/3e8171c18b3da3e49b658453fb7dc1a785ddfddf))
|
|
24
|
+
* add type resolution and value coercion ([3976797](https://github.com/MarketSquare/confargs/commit/397679794170b008feb256d320b72022069c0450))
|
|
25
|
+
* generate help text from docstrings ([13cd88c](https://github.com/MarketSquare/confargs/commit/13cd88c1738ebc480ef2575072140b431f70294a))
|
|
26
|
+
* support boolean flag negation with --no- prefix ([3d44e05](https://github.com/MarketSquare/confargs/commit/3d44e05746bfa050dbb436160b61c83564b21aab))
|
|
27
|
+
* validate config keys with strict_config mode ([f707858](https://github.com/MarketSquare/confargs/commit/f7078587200155f8122c725f9dc8b30fa4cec64e))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Documentation
|
|
31
|
+
|
|
32
|
+
* add publishing workflow, example and expanded README ([6f266d8](https://github.com/MarketSquare/confargs/commit/6f266d88cebdd66f55833eae7badecb0ab837cae))
|
|
33
|
+
* make examples/ a self-contained runnable example ([73a0948](https://github.com/MarketSquare/confargs/commit/73a094831a85cd0e86a00b570fd03e2f4cbc608d))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Build System
|
|
37
|
+
|
|
38
|
+
* single-source version and add CHANGELOG ([690efd9](https://github.com/MarketSquare/confargs/commit/690efd9b7017270a262ab13d75641891c73ca959))
|
|
39
|
+
|
|
40
|
+
## [Unreleased]
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Renamed the distribution and import package from `argconfig` to **`confargs`**
|
|
45
|
+
(the `argconfig` name was already taken on PyPI). The `ArgConfig` base class
|
|
46
|
+
keeps its name.
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- Eager options (`@option(is_eager=True)`): resolved before every other source,
|
|
51
|
+
directly against `argv`. An eager option's method returns tokens that replace
|
|
52
|
+
its own arguments, enabling argument-file expansion.
|
|
53
|
+
- `confargs.split_argument_file` / `confargs.read_argument_file` helpers that
|
|
54
|
+
parse Robot Framework-style argument files (comment lines, `name value` and
|
|
55
|
+
`name=value` forms) into argv tokens, including nested argument files.
|
|
56
|
+
|
|
57
|
+
## [0.1.0] - 2026-08-28
|
|
58
|
+
|
|
59
|
+
Initial development release.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- Declarative option model: options are methods on an `ArgConfig` subclass
|
|
64
|
+
decorated with `@option`, receiving the raw value and returning the final one.
|
|
65
|
+
- `ConfigurationProcessor` that merges sources with precedence
|
|
66
|
+
**CLI > environment variables > nearest TOML > user-directory TOML > default**
|
|
67
|
+
and returns an immutable `Namespace` (attribute and item access).
|
|
68
|
+
- Minimal command-line tokenizer: long/short options, `--opt=value`, attached
|
|
69
|
+
and combined short flags, repeatable list options, `--` terminator and
|
|
70
|
+
positional collection.
|
|
71
|
+
- Boolean flag negation via `--no-<flag>`.
|
|
72
|
+
- Type coercion from strings and native TOML values (`str`, `int`, `float`,
|
|
73
|
+
`bool`, `list[...]`, `Optional`).
|
|
74
|
+
- TOML discovery that walks up to the `.git` project root, with `--config`,
|
|
75
|
+
`--no-config` and `--ignore-git` controls and a per-user config fallback.
|
|
76
|
+
- `strict_config` mode (default on) that rejects unknown or cli-only keys in a
|
|
77
|
+
config section.
|
|
78
|
+
- Environment-variable source via explicit `envvar=` or `auto_env_vars`.
|
|
79
|
+
- Help generation from class and option docstrings; built-in `--help`.
|
|
80
|
+
- `confargs-demo` console entry-point and runnable example.
|
|
81
|
+
- Tooling: uv project, ruff, mypy (strict), pytest, pre-commit, CI matrix
|
|
82
|
+
(Python 3.10-3.13 on Linux and Windows) and a PyPI trusted-publishing
|
|
83
|
+
workflow.
|
|
84
|
+
|
|
85
|
+
[Unreleased]: https://github.com/MarketSquare/confargs/compare/v0.1.0...HEAD
|
|
86
|
+
[0.1.0]: https://github.com/MarketSquare/confargs/releases/tag/v0.1.0
|
confargs-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 confargs contributors
|
|
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.
|
confargs-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: confargs
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Declarative CLI argument parser that merges command line, TOML config files, and environment variables.
|
|
5
|
+
Project-URL: Homepage, https://github.com/MarketSquare/confargs
|
|
6
|
+
Project-URL: Repository, https://github.com/MarketSquare/confargs
|
|
7
|
+
Project-URL: Issues, https://github.com/MarketSquare/confargs/issues
|
|
8
|
+
Author: confargs contributors
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: argparse,arguments,cli,configuration,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.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# confargs
|
|
27
|
+
|
|
28
|
+
> ⚠️ Early development. APIs may change.
|
|
29
|
+
|
|
30
|
+
**confargs** is a small, declarative CLI argument parser for Python 3.10+ that
|
|
31
|
+
merges configuration from three sources into one result:
|
|
32
|
+
|
|
33
|
+
1. **Command line** arguments (`--log out.html`, `-l NONE`)
|
|
34
|
+
2. **Environment variables** (per-option or auto-generated)
|
|
35
|
+
3. **TOML config files** (discovered by walking up from the current directory,
|
|
36
|
+
`pyproject.toml`-style)
|
|
37
|
+
|
|
38
|
+
You describe options as **methods** on a class. Each method receives the raw
|
|
39
|
+
value from whichever source supplied it, performs any parsing/validation you
|
|
40
|
+
like, and returns the final value. confargs handles discovery, precedence and
|
|
41
|
+
basic type coercion; your code owns the domain logic.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import confargs
|
|
45
|
+
from confargs import ArgConfig
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MyArgs(ArgConfig):
|
|
49
|
+
"""My CLI tool.
|
|
50
|
+
|
|
51
|
+
Longer description shown in --help.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
name = "mytool"
|
|
55
|
+
|
|
56
|
+
@confargs.option
|
|
57
|
+
def log(self, value: str | None = "log.html") -> str | None:
|
|
58
|
+
"""HTML log file. Disable with the special value 'NONE'."""
|
|
59
|
+
if value == "NONE":
|
|
60
|
+
return None
|
|
61
|
+
return value
|
|
62
|
+
|
|
63
|
+
@confargs.option(names="--console/-c")
|
|
64
|
+
def console(self, value: str = "verbose") -> str:
|
|
65
|
+
choices = ["verbose", "dotted", "quiet", "none"]
|
|
66
|
+
if value not in choices:
|
|
67
|
+
raise confargs.OptionValueError(f"console must be one of {choices}")
|
|
68
|
+
return value
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
config = confargs.ConfigurationProcessor(MyArgs).process()
|
|
72
|
+
print(config.log, config.console)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Precedence
|
|
76
|
+
|
|
77
|
+
Highest wins: **CLI > environment variables > nearest TOML > user-directory TOML > option default**.
|
|
78
|
+
|
|
79
|
+
## Configuration sources
|
|
80
|
+
|
|
81
|
+
### TOML files
|
|
82
|
+
|
|
83
|
+
Config is read from a table named after your tool. By default that is
|
|
84
|
+
`[tool.<name>]` (e.g. `[tool.mytool]`); override it with
|
|
85
|
+
`default_config_section = "tool.custom"`. The file names searched are set with
|
|
86
|
+
`config_names` (default `["pyproject.toml"]`). Both `dashed-keys` and
|
|
87
|
+
`snake_case_keys` are accepted.
|
|
88
|
+
|
|
89
|
+
```toml
|
|
90
|
+
[tool.mytool]
|
|
91
|
+
log = "results.html"
|
|
92
|
+
console = "dotted"
|
|
93
|
+
tags = ["ci", "nightly"]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Discovery** walks up from the current directory looking for those files and
|
|
97
|
+
stops at the project root (a directory containing `.git`). If nothing is found,
|
|
98
|
+
a per-user config directory is consulted (`%APPDATA%\<name>` on Windows,
|
|
99
|
+
`$XDG_CONFIG_HOME/<name>` otherwise). Discovery is controlled by built-in,
|
|
100
|
+
CLI-only options:
|
|
101
|
+
|
|
102
|
+
- `--config PATH` — use only this file, skip discovery.
|
|
103
|
+
- `--no-config` — ignore config files entirely.
|
|
104
|
+
- `--ignore-git` — keep searching above the `.git` project root.
|
|
105
|
+
|
|
106
|
+
By default (`strict_config = True`) unknown keys — and any `cli_only` option —
|
|
107
|
+
found in the config section raise an error, which catches typos early. Set
|
|
108
|
+
`strict_config = False` on your class to silently ignore them instead.
|
|
109
|
+
|
|
110
|
+
### Environment variables
|
|
111
|
+
|
|
112
|
+
Set a name per option with `@option(envvar="MYTOOL_LOG")`, or enable
|
|
113
|
+
`auto_env_vars = True` on the class to expose every non-`cli_only` option as
|
|
114
|
+
`<NAME>_<OPTION>` (e.g. `MYTOOL_CONSOLE`).
|
|
115
|
+
|
|
116
|
+
### CLI-only options
|
|
117
|
+
|
|
118
|
+
Options marked `@option(cli_only=True)` are never read from TOML or the
|
|
119
|
+
environment — use this for switches that control the tool run itself (the
|
|
120
|
+
built-in discovery options above are defined this way).
|
|
121
|
+
|
|
122
|
+
## Options in depth
|
|
123
|
+
|
|
124
|
+
- Long names come from the method name (`dry_run` → `--dry-run`); a short name
|
|
125
|
+
is derived from the first letter when it is still free. Override with
|
|
126
|
+
`names="--console/-c"`.
|
|
127
|
+
- The value type is taken from the `value` parameter annotation. `bool` becomes
|
|
128
|
+
a flag; `list[...]` becomes a repeatable option; `int`/`float`/`str` are
|
|
129
|
+
coerced from strings. Your method receives the coerced value and returns the
|
|
130
|
+
final one — raise `confargs.OptionValueError` to reject it.
|
|
131
|
+
- Boolean options can be negated on the command line: `--verbose` sets it to
|
|
132
|
+
`True`, `--no-verbose` sets it to `False`.
|
|
133
|
+
|
|
134
|
+
### Eager options and argument files
|
|
135
|
+
|
|
136
|
+
Mark an option `is_eager=True` to resolve it *before* every other source,
|
|
137
|
+
directly against `argv`. The method's return value — an iterable of tokens or
|
|
138
|
+
`None` — replaces the option's own arguments, so it can inject more options.
|
|
139
|
+
This is how an `--argumentfile` option expands a file (Robot Framework style)
|
|
140
|
+
into extra arguments, including nested argument files:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from confargs import ArgConfig, option, read_argument_file
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class Args(ArgConfig):
|
|
147
|
+
@option(names="--argumentfile/-A", cli_only=True, is_eager=True)
|
|
148
|
+
def argumentfile(self, value: str | None = None) -> list[str] | None:
|
|
149
|
+
return read_argument_file(value) if value else None
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`ConfigurationProcessor(Args, argv=[...])` accepts an explicit argument list;
|
|
153
|
+
when omitted it falls back to `sys.argv[1:]`.
|
|
154
|
+
|
|
155
|
+
## Example
|
|
156
|
+
|
|
157
|
+
A complete, self-contained example lives in [`examples/demo.py`](examples/demo.py)
|
|
158
|
+
(with a sample [`examples/example.args`](examples/example.args) and
|
|
159
|
+
[`examples/README.md`](examples/README.md)). It's a single copy-pasteable file
|
|
160
|
+
showing value options, `--no-` flag negation, environment variables and an
|
|
161
|
+
eager `--argumentfile`. Run it from a checkout without installing anything:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
uv run python examples/demo.py --who Ada --repeat 3
|
|
165
|
+
uv run python examples/demo.py -A examples/example.args
|
|
166
|
+
uv run python examples/demo.py --help
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Separately, the packaged [`confargs.demo`](src/confargs/demo.py) module is
|
|
170
|
+
installed as the `confargs-demo` console script via `[project.scripts]`:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
uv run confargs-demo --console quiet --retries 5
|
|
174
|
+
uv run confargs-demo --help
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
To ship your own tool, point a console script at a `main()` that runs the
|
|
178
|
+
processor, for example in `pyproject.toml`:
|
|
179
|
+
|
|
180
|
+
```toml
|
|
181
|
+
[project.scripts]
|
|
182
|
+
mytool = "mytool.cli:main"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Development
|
|
186
|
+
|
|
187
|
+
This project uses [uv](https://docs.astral.sh/uv/).
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
uv sync # create the environment
|
|
191
|
+
uv run pytest # run the tests
|
|
192
|
+
uv run ruff check # lint
|
|
193
|
+
uv run ruff format # format
|
|
194
|
+
uv run mypy # type-check
|
|
195
|
+
pre-commit install # enable git hooks
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Publishing
|
|
199
|
+
|
|
200
|
+
Releases are published to PyPI by `.github/workflows/publish.yml` when a GitHub
|
|
201
|
+
Release is published. It uses [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/)
|
|
202
|
+
(OIDC), so no API token is stored in the repository — configure the project as a
|
|
203
|
+
trusted publisher on PyPI (workflow `publish.yml`, environment `pypi`) once.
|
|
204
|
+
|
|
205
|
+
## Versioning
|
|
206
|
+
|
|
207
|
+
confargs follows [Semantic Versioning](https://semver.org/). The version is
|
|
208
|
+
single-sourced from `__version__` in `src/confargs/__init__.py` (hatchling reads
|
|
209
|
+
it at build time). While the project is `0.x.y` the API is still stabilising, so
|
|
210
|
+
minor releases may include breaking changes. Notable changes are recorded in
|
|
211
|
+
[`CHANGELOG.md`](CHANGELOG.md).
|
|
212
|
+
|
|
213
|
+
Releases are automated with
|
|
214
|
+
[release-please](https://github.com/googleapis/release-please): merging
|
|
215
|
+
[Conventional Commits](https://www.conventionalcommits.org/) to `main` keeps an
|
|
216
|
+
open release PR that bumps `__version__`, updates the changelog and, once merged,
|
|
217
|
+
tags the release and publishes to PyPI. Pre-1.0, breaking changes bump the minor
|
|
218
|
+
version (`bump-minor-pre-major`).
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
MIT
|