strictcli 0.20.0__tar.gz → 0.20.1__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.
- strictcli-0.20.1/.rlsbl/changes/.validated +1 -0
- strictcli-0.20.1/.rlsbl/changes/0.15.0.md +18 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.15.1.md +9 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.16.0.md +9 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.16.1.md +2 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.17.0.md +2 -0
- strictcli-0.20.1/.rlsbl/changes/0.18.0.md +14 -0
- strictcli-0.20.1/.rlsbl/changes/0.20.0.md +19 -0
- strictcli-0.20.1/.rlsbl/changes/0.20.1.jsonl +3 -0
- strictcli-0.20.1/.rlsbl/changes/0.20.1.md +14 -0
- strictcli-0.20.1/.rlsbl/releases/v0.20.1.toml +8 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/CHANGELOG.md +15 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/PKG-INFO +1 -1
- {strictcli-0.20.0 → strictcli-0.20.1}/package-lock.json +2 -2
- {strictcli-0.20.0 → strictcli-0.20.1}/package.json +1 -1
- {strictcli-0.20.0 → strictcli-0.20.1}/pyproject.toml +1 -1
- {strictcli-0.20.0 → strictcli-0.20.1}/strictcli/__init__.py +11 -3
- strictcli-0.20.1/tests/test_keyword_flags.py +117 -0
- strictcli-0.20.1/todo/keyword-collision-in-flag-param-name.md +18 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/uv.lock +1 -1
- strictcli-0.20.0/.rlsbl/changes/.validated +0 -1
- strictcli-0.20.0/.rlsbl/changes/0.15.0.md +0 -9
- strictcli-0.20.0/.rlsbl/changes/0.18.0.md +0 -5
- {strictcli-0.20.0 → strictcli-0.20.1}/.claude/settings.json +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.github/workflows/ci.yml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.github/workflows/publish.yml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.gitignore +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.npmignore +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.claude/settings.json +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.github/workflows/ci.yml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.github/workflows/publish.yml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.gitignore +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.npmignore +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.rlsbl/hooks/post-release.sh +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.rlsbl/hooks/pre-checks.sh +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.rlsbl/hooks/pre-release.sh +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.rlsbl/lint/npm.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/.rlsbl/lint/python.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/CHANGELOG.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/CLAUDE.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/bases/LICENSE +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.10.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.10.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.11.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.11.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.12.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.12.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.13.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.13.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.14.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.14.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.15.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.15.1.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.16.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.16.1.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.17.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.18.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.19.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.19.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.20.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.4.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.4.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.4.1.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.4.1.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.5.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.5.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.6.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.6.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.6.1.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.6.1.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.7.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.7.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.7.1.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.7.1.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.1.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.1.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.2.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.2.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.3.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.3.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.4.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.4.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.5.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.5.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.6.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.6.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.7.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.8.7.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.9.0.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.9.0.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.9.1.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/0.9.1.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/changes/unreleased.jsonl +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/config.json +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/hashes.json +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/hooks/post-release.sh +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/hooks/pre-checks.sh +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/hooks/pre-release.sh +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/lint/npm.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/lint/python.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/unreleased.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.10.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.11.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.12.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.13.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.15.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.15.1.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.16.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.16.1.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.17.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.18.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.19.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.20.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.8.5.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.8.6.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.8.7.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.9.0.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/releases/v0.9.1.toml +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.rlsbl/version +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/.strictcli/schema.json +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/CLAUDE.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/LICENSE +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/README.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/index.js +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/postinstall.js +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_arg_default.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_at_prefix.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_auto_version.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_check_command.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_check_discovery.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_check_public_api.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_check_runner.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_check_schema.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_check_types.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_choices.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_command_help_suggestion.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_command_tags.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_config.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_config_file_path.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_config_set_bugs.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_deep_nesting.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_dependencies.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_deprecated.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_dump_schema.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_e2e.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_env.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_exit_codes.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_flag_sets.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_float_type.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_global_flags.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_help.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_int_type.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_mutex.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_nesting.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_parser.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_passthrough.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_registration.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_repeatable.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_tagdsl.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_toml_loading.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_unique.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_utilities.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_validate.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/tests/test_variadic.py +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/todo/.defer/deferred.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/todo/.done/original-idea.md +0 -0
- {strictcli-0.20.0 → strictcli-0.20.1}/todo/.done/public-check-runner-api.md +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5b2b40dead26752424a198c76f3634593b1cdb9b
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## 0.15.0
|
|
2
|
+
|
|
3
|
+
Config set type coercion and key validation
|
|
4
|
+
|
|
5
|
+
<details>
|
|
6
|
+
<summary>Context</summary>
|
|
7
|
+
|
|
8
|
+
config set now validates keys against registered flags and coerces string values to the flag's declared type before writing. This is a breaking change: unknown keys are rejected.
|
|
9
|
+
|
|
10
|
+
</details>
|
|
11
|
+
|
|
12
|
+
### Breaking
|
|
13
|
+
|
|
14
|
+
- **Breaking.** `config set` now validates keys against registered flags and rejects unknown keys.
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
- **New feature.** `config set` coerces values to the flag's declared type (int, bool, float) before writing to config.
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
## 0.15.1
|
|
2
2
|
|
|
3
|
+
Config show bool parity and negative values in config set
|
|
4
|
+
|
|
5
|
+
<details>
|
|
6
|
+
<summary>Context</summary>
|
|
7
|
+
|
|
8
|
+
config show now outputs lowercase true/false matching Go. config set accepts negative numeric values.
|
|
9
|
+
|
|
10
|
+
</details>
|
|
11
|
+
|
|
3
12
|
### Breaking
|
|
4
13
|
|
|
5
14
|
- **Breaking.** Config set now accepts negative numeric values. Unknown single-dash tokens produce 'unexpected argument' instead of 'unknown flag'.
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
## 0.16.0
|
|
2
2
|
|
|
3
|
+
unique and env_separator flag fields, config array coercion, config set repeatable support
|
|
4
|
+
|
|
5
|
+
<details>
|
|
6
|
+
<summary>Context</summary>
|
|
7
|
+
|
|
8
|
+
Breaking: config show now requires --plain or --json. New features: unique field enforces no duplicate values on repeatable flags, env_separator controls how env vars are split into arrays, config arrays are coerced to declared types, config set supports repeatable flags with --clear and --default.
|
|
9
|
+
|
|
10
|
+
</details>
|
|
11
|
+
|
|
3
12
|
### Breaking
|
|
4
13
|
|
|
5
14
|
- **Config show.** `config show` now requires explicit `--plain` or `--json` flag instead of defaulting to plain text.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## 0.18.0
|
|
2
|
+
|
|
3
|
+
Public API for running checks programmatically
|
|
4
|
+
|
|
5
|
+
<details>
|
|
6
|
+
<summary>Context</summary>
|
|
7
|
+
|
|
8
|
+
Adds App.run_checks(), format_check_results(), format_check_results_json(), and CheckRunResult — a stable public API replacing the private _filter_checks/_resolve_check_order/_run_checks/_check_format_human functions that consumers like rlsbl were forced to use.
|
|
9
|
+
|
|
10
|
+
</details>
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- **New.** Public API for running checks programmatically: `App.run_checks()`, `format_check_results()`, `format_check_results_json()`, `CheckRunResult`.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## 0.20.0
|
|
2
|
+
|
|
3
|
+
Frozen Command, command tags, tag contracts
|
|
4
|
+
|
|
5
|
+
<details>
|
|
6
|
+
<summary>Context</summary>
|
|
7
|
+
|
|
8
|
+
Breaking: Command dataclass is now frozen=True with tuple fields (was mutable list). New: command tags (string labels with group inheritance) and tag contracts (registration-time validation that tagged commands have required flags).
|
|
9
|
+
|
|
10
|
+
</details>
|
|
11
|
+
|
|
12
|
+
### Breaking
|
|
13
|
+
|
|
14
|
+
- **Breaking.** Command dataclass is now `frozen=True` with `tuple` fields instead of `list`.
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
- **New.** Command tags: string labels on commands and groups with group-to-command inheritance.
|
|
19
|
+
- **New.** Tag contracts: registration-time validation that tagged commands have required flags.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"commits":["88874b77625f9f096a9e7c38212cb65ab7bd4188"],"user_facing":false}
|
|
2
|
+
{"commits":["0e5647f165e7b5ebb4bb135bd664f072306b282a"],"user_facing":true,"description":"**Fix.** Flags named after Python keywords (`--global`, `--class`, `--import`) now produce valid parameter names with a trailing underscore (e.g., `global_`).","type":"fix"}
|
|
3
|
+
{"commits":["8e769b6aa17c51d30edae5a074f4a6f31c624853"],"user_facing":false}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## 0.20.1
|
|
2
|
+
|
|
3
|
+
Fix keyword collision in flag parameter names
|
|
4
|
+
|
|
5
|
+
<details>
|
|
6
|
+
<summary>Context</summary>
|
|
7
|
+
|
|
8
|
+
Flags named after Python keywords (--global, --class, --import, etc.) now produce valid parameter names with a trailing underscore per PEP 8 convention.
|
|
9
|
+
|
|
10
|
+
</details>
|
|
11
|
+
|
|
12
|
+
### Fixes
|
|
13
|
+
|
|
14
|
+
- **Fix.** Flags named after Python keywords (`--global`, `--class`, `--import`) now produce valid parameter names with a trailing underscore (e.g., `global_`).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Version bump type: patch, minor, or major
|
|
2
|
+
bump = "patch"
|
|
3
|
+
# Short description of this release (required)
|
|
4
|
+
description = "Fix keyword collision in flag parameter names"
|
|
5
|
+
# Optional context explaining why these changes were made
|
|
6
|
+
context = "Flags named after Python keywords (--global, --class, --import, etc.) now produce valid parameter names with a trailing underscore per PEP 8 convention."
|
|
7
|
+
include = ["pypi", "npm"]
|
|
8
|
+
exclude = []
|
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## 0.20.1
|
|
6
|
+
|
|
7
|
+
Fix keyword collision in flag parameter names
|
|
8
|
+
|
|
9
|
+
<details>
|
|
10
|
+
<summary>Context</summary>
|
|
11
|
+
|
|
12
|
+
Flags named after Python keywords (--global, --class, --import, etc.) now produce valid parameter names with a trailing underscore per PEP 8 convention.
|
|
13
|
+
|
|
14
|
+
</details>
|
|
15
|
+
|
|
16
|
+
### Fixes
|
|
17
|
+
|
|
18
|
+
- **Fix.** Flags named after Python keywords (`--global`, `--class`, `--import`) now produce valid parameter names with a trailing underscore (e.g., `global_`).
|
|
19
|
+
|
|
5
20
|
## 0.20.0
|
|
6
21
|
|
|
7
22
|
Frozen Command, command tags, tag contracts
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strictcli",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "strictcli",
|
|
9
|
-
"version": "0.20.
|
|
9
|
+
"version": "0.20.1",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT"
|
|
12
12
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
__version__ = "0.20.
|
|
5
|
+
__version__ = "0.20.1"
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"App", "Flag", "Arg", "FlagSet", "MutexGroup", "CoRequired", "Requires",
|
|
@@ -12,6 +12,7 @@ __all__ = [
|
|
|
12
12
|
]
|
|
13
13
|
|
|
14
14
|
import contextlib
|
|
15
|
+
import keyword
|
|
15
16
|
import fnmatch
|
|
16
17
|
import importlib.metadata
|
|
17
18
|
import inspect
|
|
@@ -2553,8 +2554,15 @@ def _parse_command(
|
|
|
2553
2554
|
|
|
2554
2555
|
|
|
2555
2556
|
def _flag_param_name(flag_name: str) -> str:
|
|
2556
|
-
"""Convert a flag name like '--dry-run' to a Python parameter name 'dry_run'.
|
|
2557
|
-
|
|
2557
|
+
"""Convert a flag name like '--dry-run' to a Python parameter name 'dry_run'.
|
|
2558
|
+
|
|
2559
|
+
If the result is a Python keyword (e.g. 'global', 'class'), appends '_'
|
|
2560
|
+
per PEP 8 convention (e.g. 'global_', 'class_').
|
|
2561
|
+
"""
|
|
2562
|
+
name = flag_name.lstrip("-").replace("-", "_")
|
|
2563
|
+
if keyword.iskeyword(name):
|
|
2564
|
+
name += "_"
|
|
2565
|
+
return name
|
|
2558
2566
|
|
|
2559
2567
|
|
|
2560
2568
|
def _build_and_validate_command(
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Tests for Python keyword collision handling in flag parameter names."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
import strictcli
|
|
7
|
+
from strictcli import _flag_param_name
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# --- Unit tests for _flag_param_name ---
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_non_keyword_unchanged():
|
|
14
|
+
"""Non-keyword flag names convert normally (dashes to underscores)."""
|
|
15
|
+
assert _flag_param_name("--dry-run") == "dry_run"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_keyword_global():
|
|
19
|
+
"""'global' is a Python keyword, should get trailing underscore."""
|
|
20
|
+
assert _flag_param_name("--global") == "global_"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_keyword_class():
|
|
24
|
+
"""'class' is a Python keyword, should get trailing underscore."""
|
|
25
|
+
assert _flag_param_name("--class") == "class_"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_keyword_import():
|
|
29
|
+
"""'import' is a Python keyword, should get trailing underscore."""
|
|
30
|
+
assert _flag_param_name("--import") == "import_"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_keyword_for():
|
|
34
|
+
"""'for' is a Python keyword, should get trailing underscore."""
|
|
35
|
+
assert _flag_param_name("--for") == "for_"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_keyword_in():
|
|
39
|
+
"""'in' is a Python keyword, should get trailing underscore."""
|
|
40
|
+
assert _flag_param_name("--in") == "in_"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_non_keyword_globally():
|
|
44
|
+
"""'globally' is NOT a keyword, should NOT get trailing underscore."""
|
|
45
|
+
assert _flag_param_name("--globally") == "globally"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# --- Integration test: command with --global flag ---
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_command_with_global_flag():
|
|
52
|
+
"""A command with a --global bool flag works end-to-end."""
|
|
53
|
+
app = strictcli.App(name="testapp", version="1.0.0", help="test app")
|
|
54
|
+
received = {}
|
|
55
|
+
|
|
56
|
+
@app.command("cmd", help="a command")
|
|
57
|
+
@strictcli.flag("global", type=bool, help="apply globally")
|
|
58
|
+
def cmd(**kwargs):
|
|
59
|
+
received.update(kwargs)
|
|
60
|
+
|
|
61
|
+
r = app.test(["cmd", "--global"])
|
|
62
|
+
assert r.exit_code == 0
|
|
63
|
+
assert received["global_"] is True
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_command_with_global_flag_default():
|
|
67
|
+
"""When --global is not passed, its default (False) uses the global_ key."""
|
|
68
|
+
app = strictcli.App(name="testapp", version="1.0.0", help="test app")
|
|
69
|
+
received = {}
|
|
70
|
+
|
|
71
|
+
@app.command("cmd", help="a command")
|
|
72
|
+
@strictcli.flag("global", type=bool, help="apply globally")
|
|
73
|
+
def cmd(**kwargs):
|
|
74
|
+
received.update(kwargs)
|
|
75
|
+
|
|
76
|
+
r = app.test(["cmd"])
|
|
77
|
+
assert r.exit_code == 0
|
|
78
|
+
assert received["global_"] is False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# --- Integration test: config with keyword flag ---
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_config_key_uses_suffixed_name(tmp_path):
|
|
85
|
+
"""Config keys for keyword flags use the suffixed name (global_)."""
|
|
86
|
+
# Write a config file with the suffixed key
|
|
87
|
+
config_dir = tmp_path / "kwtest"
|
|
88
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
89
|
+
config_file = config_dir / "config.json"
|
|
90
|
+
config_file.write_text(json.dumps({"global_": True}) + "\n")
|
|
91
|
+
|
|
92
|
+
# Set XDG_CONFIG_HOME BEFORE creating the app, since config is loaded
|
|
93
|
+
# during App.__init__
|
|
94
|
+
old_xdg = os.environ.get("XDG_CONFIG_HOME")
|
|
95
|
+
os.environ["XDG_CONFIG_HOME"] = str(tmp_path)
|
|
96
|
+
try:
|
|
97
|
+
app = strictcli.App(
|
|
98
|
+
name="kwtest",
|
|
99
|
+
version="1.0.0",
|
|
100
|
+
help="test app",
|
|
101
|
+
config=True,
|
|
102
|
+
)
|
|
103
|
+
received = {}
|
|
104
|
+
|
|
105
|
+
@app.command("cmd", help="a command")
|
|
106
|
+
@strictcli.flag("global", type=bool, help="apply globally")
|
|
107
|
+
def cmd(**kwargs):
|
|
108
|
+
received.update(kwargs)
|
|
109
|
+
|
|
110
|
+
r = app.test(["cmd"])
|
|
111
|
+
assert r.exit_code == 0
|
|
112
|
+
assert received["global_"] is True
|
|
113
|
+
finally:
|
|
114
|
+
if old_xdg is None:
|
|
115
|
+
del os.environ["XDG_CONFIG_HOME"]
|
|
116
|
+
else:
|
|
117
|
+
os.environ["XDG_CONFIG_HOME"] = old_xdg
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Keyword collision in `_flag_param_name`
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
`_flag_param_name` does not handle Python keyword collisions. When a CLI flag name matches a Python keyword (e.g., `--global`), the generated parameter name is an invalid Python identifier.
|
|
6
|
+
|
|
7
|
+
rlsbl currently monkey-patches `_flag_param_name` to work around this by appending `_` for keywords like `global`.
|
|
8
|
+
|
|
9
|
+
## Fix
|
|
10
|
+
|
|
11
|
+
The fix is 2 lines in `_flag_param_name`:
|
|
12
|
+
|
|
13
|
+
1. Add `import keyword` at the top of the module
|
|
14
|
+
2. Add `if keyword.iskeyword(name): return name + "_"` to `_flag_param_name`
|
|
15
|
+
|
|
16
|
+
## Downstream impact
|
|
17
|
+
|
|
18
|
+
Once this ships in a strictcli release, the rlsbl monkey-patch can be removed.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
019cae063cca2c44324d0c76260c72e72334b7c8
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
## 0.15.0
|
|
2
|
-
|
|
3
|
-
### Breaking
|
|
4
|
-
|
|
5
|
-
- **Breaking.** `config set` now validates keys against registered flags and rejects unknown keys.
|
|
6
|
-
|
|
7
|
-
### Features
|
|
8
|
-
|
|
9
|
-
- **New feature.** `config set` coerces values to the flag's declared type (int, bool, float) before writing to config.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|