strictcli 0.18.0__tar.gz → 0.19.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.
- strictcli-0.19.0/.rlsbl/changes/.validated +1 -0
- strictcli-0.19.0/.rlsbl/changes/0.19.0.jsonl +2 -0
- strictcli-0.19.0/.rlsbl/changes/0.19.0.md +5 -0
- strictcli-0.19.0/.rlsbl/releases/v0.19.0.toml +0 -0
- strictcli-0.19.0/.rlsbl/version +1 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/CHANGELOG.md +9 -3
- {strictcli-0.18.0 → strictcli-0.19.0}/PKG-INFO +7 -7
- {strictcli-0.18.0 → strictcli-0.19.0}/README.md +6 -6
- {strictcli-0.18.0 → strictcli-0.19.0}/package-lock.json +2 -2
- {strictcli-0.18.0 → strictcli-0.19.0}/package.json +1 -1
- {strictcli-0.18.0 → strictcli-0.19.0}/pyproject.toml +1 -1
- {strictcli-0.18.0 → strictcli-0.19.0}/strictcli/__init__.py +24 -24
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_e2e.py +8 -8
- strictcli-0.18.0/tests/test_tags.py → strictcli-0.19.0/tests/test_flag_sets.py +22 -22
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_passthrough.py +5 -5
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_registration.py +4 -4
- {strictcli-0.18.0 → strictcli-0.19.0}/uv.lock +1 -1
- strictcli-0.18.0/.rlsbl/bases/.rlsbl/lint/go.toml +0 -17
- strictcli-0.18.0/.rlsbl/changes/.validated +0 -1
- strictcli-0.18.0/.rlsbl/lint/go.toml +0 -17
- strictcli-0.18.0/.rlsbl/version +0 -1
- {strictcli-0.18.0 → strictcli-0.19.0}/.claude/settings.json +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.github/workflows/ci.yml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.github/workflows/publish.yml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.gitignore +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.npmignore +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.claude/settings.json +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.github/workflows/ci.yml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.github/workflows/publish.yml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.gitignore +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.npmignore +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.rlsbl/hooks/post-release.sh +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.rlsbl/hooks/pre-checks.sh +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.rlsbl/hooks/pre-release.sh +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.rlsbl/lint/npm.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/.rlsbl/lint/python.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/CHANGELOG.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/CLAUDE.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/bases/LICENSE +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.10.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.10.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.11.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.11.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.12.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.12.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.13.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.13.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.14.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.14.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.15.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.15.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.15.1.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.15.1.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.16.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.16.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.16.1.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.16.1.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.17.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.17.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.18.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.18.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.4.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.4.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.4.1.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.4.1.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.5.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.5.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.6.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.6.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.6.1.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.6.1.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.7.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.7.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.7.1.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.7.1.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.1.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.1.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.2.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.2.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.3.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.3.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.4.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.4.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.5.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.5.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.6.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.6.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.7.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.8.7.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.9.0.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.9.0.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.9.1.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/0.9.1.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/changes/unreleased.jsonl +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/config.json +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/hashes.json +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/hooks/post-release.sh +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/hooks/pre-checks.sh +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/hooks/pre-release.sh +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/lint/npm.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/lint/python.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/unreleased.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.10.0.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.11.0.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.12.0.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.13.0.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.15.0.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.15.1.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.16.0.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.16.1.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.17.0.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.18.0.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.8.5.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.8.6.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.8.7.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.9.0.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.rlsbl/releases/v0.9.1.toml +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/.strictcli/schema.json +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/CLAUDE.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/LICENSE +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/index.js +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/postinstall.js +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_arg_default.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_at_prefix.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_auto_version.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_check_command.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_check_discovery.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_check_public_api.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_check_runner.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_check_schema.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_check_types.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_choices.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_command_help_suggestion.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_config.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_config_file_path.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_config_set_bugs.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_deep_nesting.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_dependencies.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_deprecated.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_dump_schema.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_env.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_exit_codes.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_float_type.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_global_flags.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_help.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_int_type.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_mutex.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_nesting.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_parser.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_repeatable.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_tagdsl.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_toml_loading.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_unique.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_utilities.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_validate.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/tests/test_variadic.py +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/todo/.defer/deferred.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/todo/.done/original-idea.md +0 -0
- {strictcli-0.18.0 → strictcli-0.19.0}/todo/.done/public-check-runner-api.md +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
436f8a9ce1b048614cb66e929969dfbcb0791689
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
{"commits":["03d0cd60c4d207b568a19bf6916f58aacdac1450","5a1368de51cbe0abe465f21f3a38f78ba1746fef"],"user_facing":true,"description":"**Breaking.** Renamed `Tag` to `FlagSet` and `tags=` parameter to `flag_sets=` across all API surfaces.","type":"breaking"}
|
|
2
|
+
{"commits":["03782821de6b48073259aa3edb66b141ab70eb3b","232a6e47db207159abfdb81a5e1b93c1cfce82ea","fa29d925446a00a2bddec93cb949f91f13682704"],"user_facing":false}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.69.2
|
|
@@ -2,17 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
-
## 0.
|
|
5
|
+
## 0.19.0
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Renamed Tag to FlagSet
|
|
8
8
|
|
|
9
9
|
<details>
|
|
10
10
|
<summary>Context</summary>
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Breaking: Tag class renamed to FlagSet, tags= parameter renamed to flag_sets= across all API surfaces. The Tag name was misleading — it implied labeling/annotation, but the type is actually a reusable bundle of flags.
|
|
13
13
|
|
|
14
14
|
</details>
|
|
15
15
|
|
|
16
|
+
### Breaking
|
|
17
|
+
|
|
18
|
+
- **Breaking.** Renamed `Tag` to `FlagSet` and `tags=` parameter to `flag_sets=` across all API surfaces.
|
|
19
|
+
|
|
20
|
+
## 0.18.0
|
|
21
|
+
|
|
16
22
|
### Features
|
|
17
23
|
|
|
18
24
|
- **New.** Public API for running checks programmatically: `App.run_checks()`, `format_check_results()`, `format_check_results_json()`, `CheckRunResult`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: strictcli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.0
|
|
4
4
|
Summary: A strict, zero-dependency CLI framework for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/smm-h/strictcli
|
|
6
6
|
Project-URL: Repository, https://github.com/smm-h/strictcli
|
|
@@ -222,12 +222,12 @@ Values resolve in this order: CLI argument > environment variable > default. If
|
|
|
222
222
|
|
|
223
223
|
Bool flags from env vars accept `1`, `true`, `yes` (case-insensitive) for `True` and `0`, `false`, `no` for `False`. Any other value is an error.
|
|
224
224
|
|
|
225
|
-
##
|
|
225
|
+
## FlagSets
|
|
226
226
|
|
|
227
|
-
|
|
227
|
+
FlagSets are reusable bundles of flags that can be applied to multiple commands:
|
|
228
228
|
|
|
229
229
|
```python
|
|
230
|
-
|
|
230
|
+
auth_flags = strictcli.FlagSet(
|
|
231
231
|
name="auth",
|
|
232
232
|
flags=[
|
|
233
233
|
strictcli.Flag(name="token", type=str, help="auth token", env="MYAPP_TOKEN", default=""),
|
|
@@ -235,16 +235,16 @@ auth_tag = strictcli.Tag(
|
|
|
235
235
|
],
|
|
236
236
|
)
|
|
237
237
|
|
|
238
|
-
@app.command("deploy", help="deploy the app",
|
|
238
|
+
@app.command("deploy", help="deploy the app", flag_sets=[auth_flags])
|
|
239
239
|
def deploy(token, insecure):
|
|
240
240
|
print(f"token={'set' if token else 'unset'}, insecure={insecure}")
|
|
241
241
|
|
|
242
|
-
@app.command("status", help="check status",
|
|
242
|
+
@app.command("status", help="check status", flag_sets=[auth_flags])
|
|
243
243
|
def status(token, insecure):
|
|
244
244
|
print(f"checking status")
|
|
245
245
|
```
|
|
246
246
|
|
|
247
|
-
Both commands now have `--token` and `--insecure` flags.
|
|
247
|
+
Both commands now have `--token` and `--insecure` flags. FlagSet flags appear in help output and are parsed like any other flag.
|
|
248
248
|
|
|
249
249
|
## Mutex Groups
|
|
250
250
|
|
|
@@ -204,12 +204,12 @@ Values resolve in this order: CLI argument > environment variable > default. If
|
|
|
204
204
|
|
|
205
205
|
Bool flags from env vars accept `1`, `true`, `yes` (case-insensitive) for `True` and `0`, `false`, `no` for `False`. Any other value is an error.
|
|
206
206
|
|
|
207
|
-
##
|
|
207
|
+
## FlagSets
|
|
208
208
|
|
|
209
|
-
|
|
209
|
+
FlagSets are reusable bundles of flags that can be applied to multiple commands:
|
|
210
210
|
|
|
211
211
|
```python
|
|
212
|
-
|
|
212
|
+
auth_flags = strictcli.FlagSet(
|
|
213
213
|
name="auth",
|
|
214
214
|
flags=[
|
|
215
215
|
strictcli.Flag(name="token", type=str, help="auth token", env="MYAPP_TOKEN", default=""),
|
|
@@ -217,16 +217,16 @@ auth_tag = strictcli.Tag(
|
|
|
217
217
|
],
|
|
218
218
|
)
|
|
219
219
|
|
|
220
|
-
@app.command("deploy", help="deploy the app",
|
|
220
|
+
@app.command("deploy", help="deploy the app", flag_sets=[auth_flags])
|
|
221
221
|
def deploy(token, insecure):
|
|
222
222
|
print(f"token={'set' if token else 'unset'}, insecure={insecure}")
|
|
223
223
|
|
|
224
|
-
@app.command("status", help="check status",
|
|
224
|
+
@app.command("status", help="check status", flag_sets=[auth_flags])
|
|
225
225
|
def status(token, insecure):
|
|
226
226
|
print(f"checking status")
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
-
Both commands now have `--token` and `--insecure` flags.
|
|
229
|
+
Both commands now have `--token` and `--insecure` flags. FlagSet flags appear in help output and are parsed like any other flag.
|
|
230
230
|
|
|
231
231
|
## Mutex Groups
|
|
232
232
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strictcli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "strictcli",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.19.0",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT"
|
|
12
12
|
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
__version__ = "0.
|
|
5
|
+
__version__ = "0.19.0"
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
|
-
"App", "Flag", "Arg", "
|
|
8
|
+
"App", "Flag", "Arg", "FlagSet", "MutexGroup", "CoRequired", "Requires",
|
|
9
9
|
"Implies", "Passthrough", "DeprecatedCommand", "Result", "flag", "arg",
|
|
10
10
|
"CheckResult", "CheckContext", "CheckRunResult",
|
|
11
11
|
"format_check_results", "format_check_results_json",
|
|
@@ -677,7 +677,7 @@ class Arg:
|
|
|
677
677
|
|
|
678
678
|
|
|
679
679
|
@dataclass
|
|
680
|
-
class
|
|
680
|
+
class FlagSet:
|
|
681
681
|
"""A reusable bundle of flags."""
|
|
682
682
|
|
|
683
683
|
name: str
|
|
@@ -740,7 +740,7 @@ class Command:
|
|
|
740
740
|
handler: Callable | None
|
|
741
741
|
flags: list[Flag] = field(default_factory=list)
|
|
742
742
|
args: list[Arg] = field(default_factory=list)
|
|
743
|
-
|
|
743
|
+
flag_sets: list[FlagSet] = field(default_factory=list)
|
|
744
744
|
mutex: list[MutexGroup] = field(default_factory=list)
|
|
745
745
|
dependencies: list[CoRequired | Requires | Implies] = field(default_factory=list)
|
|
746
746
|
passthrough: Passthrough | None = None
|
|
@@ -805,7 +805,7 @@ class Group:
|
|
|
805
805
|
*,
|
|
806
806
|
help: str,
|
|
807
807
|
args: list[Arg] | None = None,
|
|
808
|
-
|
|
808
|
+
flag_sets: list[FlagSet] | None = None,
|
|
809
809
|
mutex: list[MutexGroup] | None = None,
|
|
810
810
|
dependencies: list[CoRequired | Requires | Implies] | None = None,
|
|
811
811
|
passthrough: Passthrough | None = None,
|
|
@@ -818,7 +818,7 @@ class Group:
|
|
|
818
818
|
f'command "{name}" collides with an existing group'
|
|
819
819
|
)
|
|
820
820
|
cmd = _build_and_validate_command(
|
|
821
|
-
name, help=help, handler=func, args=args,
|
|
821
|
+
name, help=help, handler=func, args=args, flag_sets=flag_sets, mutex=mutex,
|
|
822
822
|
dependencies=dependencies,
|
|
823
823
|
env_prefix=self.env_prefix,
|
|
824
824
|
global_flags=self._global_flags,
|
|
@@ -1124,7 +1124,7 @@ class App:
|
|
|
1124
1124
|
help="Run project checks",
|
|
1125
1125
|
handler=_check_handler,
|
|
1126
1126
|
args=None,
|
|
1127
|
-
|
|
1127
|
+
flag_sets=None,
|
|
1128
1128
|
mutex=None,
|
|
1129
1129
|
dependencies=None,
|
|
1130
1130
|
env_prefix=self.env_prefix,
|
|
@@ -1140,7 +1140,7 @@ class App:
|
|
|
1140
1140
|
*,
|
|
1141
1141
|
help: str,
|
|
1142
1142
|
args: list[Arg] | None = None,
|
|
1143
|
-
|
|
1143
|
+
flag_sets: list[FlagSet] | None = None,
|
|
1144
1144
|
mutex: list[MutexGroup] | None = None,
|
|
1145
1145
|
dependencies: list[CoRequired | Requires | Implies] | None = None,
|
|
1146
1146
|
passthrough: Passthrough | None = None,
|
|
@@ -1153,7 +1153,7 @@ class App:
|
|
|
1153
1153
|
help=help,
|
|
1154
1154
|
handler=func,
|
|
1155
1155
|
args=args,
|
|
1156
|
-
|
|
1156
|
+
flag_sets=flag_sets,
|
|
1157
1157
|
mutex=mutex,
|
|
1158
1158
|
dependencies=dependencies,
|
|
1159
1159
|
env_prefix=self.env_prefix,
|
|
@@ -2483,7 +2483,7 @@ def _build_and_validate_command(
|
|
|
2483
2483
|
help: str,
|
|
2484
2484
|
handler: Callable,
|
|
2485
2485
|
args: list[Arg] | None,
|
|
2486
|
-
|
|
2486
|
+
flag_sets: list[FlagSet] | None,
|
|
2487
2487
|
mutex: list[MutexGroup] | None,
|
|
2488
2488
|
dependencies: list[CoRequired | Requires | Implies] | None = None,
|
|
2489
2489
|
env_prefix: str | None,
|
|
@@ -2495,22 +2495,22 @@ def _build_and_validate_command(
|
|
|
2495
2495
|
if not help or not help.strip():
|
|
2496
2496
|
raise ValueError(f'command "{name}": missing help text')
|
|
2497
2497
|
|
|
2498
|
-
# Passthrough commands must not have flags, args,
|
|
2498
|
+
# Passthrough commands must not have flags, args, flag sets, or mutex groups
|
|
2499
2499
|
if passthrough is not None:
|
|
2500
2500
|
decorator_flags = list(getattr(handler, "_strictcli_flags", []))
|
|
2501
2501
|
decorator_args = list(getattr(handler, "_strictcli_args", []))
|
|
2502
2502
|
has_flags = bool(decorator_flags)
|
|
2503
2503
|
has_args = bool(args) or bool(decorator_args)
|
|
2504
|
-
|
|
2504
|
+
has_flag_sets = bool(flag_sets)
|
|
2505
2505
|
has_mutex = bool(mutex)
|
|
2506
|
-
if has_flags or has_args or
|
|
2506
|
+
if has_flags or has_args or has_flag_sets or has_mutex:
|
|
2507
2507
|
parts = []
|
|
2508
2508
|
if has_flags:
|
|
2509
2509
|
parts.append("flags")
|
|
2510
2510
|
if has_args:
|
|
2511
2511
|
parts.append("args")
|
|
2512
|
-
if
|
|
2513
|
-
parts.append("
|
|
2512
|
+
if has_flag_sets:
|
|
2513
|
+
parts.append("flag sets")
|
|
2514
2514
|
if has_mutex:
|
|
2515
2515
|
parts.append("mutex groups")
|
|
2516
2516
|
raise ValueError(
|
|
@@ -2535,11 +2535,11 @@ def _build_and_validate_command(
|
|
|
2535
2535
|
all_args = list(args) if args else []
|
|
2536
2536
|
all_args.extend(decorator_args)
|
|
2537
2537
|
|
|
2538
|
-
# Merge
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
for
|
|
2542
|
-
|
|
2538
|
+
# Merge flag sets into flags
|
|
2539
|
+
resolved_flag_sets = list(flag_sets) if flag_sets else []
|
|
2540
|
+
flag_set_flags: list[Flag] = []
|
|
2541
|
+
for flag_set in resolved_flag_sets:
|
|
2542
|
+
flag_set_flags.extend(flag_set.flags)
|
|
2543
2543
|
|
|
2544
2544
|
# Resolve mutex groups and merge their flags
|
|
2545
2545
|
resolved_mutex = list(mutex) if mutex else []
|
|
@@ -2563,13 +2563,13 @@ def _build_and_validate_command(
|
|
|
2563
2563
|
)
|
|
2564
2564
|
mutex_flag_names.add(f.name)
|
|
2565
2565
|
|
|
2566
|
-
# All flags: decorator flags +
|
|
2567
|
-
all_flags = decorator_flags +
|
|
2566
|
+
# All flags: decorator flags + flag set flags + mutex flags + extra flags
|
|
2567
|
+
all_flags = decorator_flags + flag_set_flags + mutex_flags
|
|
2568
2568
|
if extra_flags:
|
|
2569
2569
|
all_flags.extend(extra_flags)
|
|
2570
2570
|
|
|
2571
2571
|
# Validate: no duplicate flag names (catches mutex flags overlapping with
|
|
2572
|
-
# regular flags or
|
|
2572
|
+
# regular flags or flag set flags)
|
|
2573
2573
|
seen_flag_names: set[str] = set()
|
|
2574
2574
|
for f in all_flags:
|
|
2575
2575
|
if f.name in seen_flag_names:
|
|
@@ -2745,7 +2745,7 @@ def _build_and_validate_command(
|
|
|
2745
2745
|
handler=handler,
|
|
2746
2746
|
flags=all_flags,
|
|
2747
2747
|
args=all_args,
|
|
2748
|
-
|
|
2748
|
+
flag_sets=resolved_flag_sets,
|
|
2749
2749
|
mutex=resolved_mutex,
|
|
2750
2750
|
dependencies=resolved_dependencies,
|
|
2751
2751
|
)
|
|
@@ -4,8 +4,8 @@ import strictcli
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def _build_rlsbl_app():
|
|
7
|
-
"""Build a mini rlsbl-like CLI app with commands, groups,
|
|
8
|
-
|
|
7
|
+
"""Build a mini rlsbl-like CLI app with commands, groups, flag sets, and env vars."""
|
|
8
|
+
auth_flag_set = strictcli.FlagSet(
|
|
9
9
|
name="auth",
|
|
10
10
|
flags=[
|
|
11
11
|
strictcli.Flag(
|
|
@@ -26,7 +26,7 @@ def _build_rlsbl_app():
|
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
# Top-level commands
|
|
29
|
-
@app.command("status", help="show release status",
|
|
29
|
+
@app.command("status", help="show release status", flag_sets=[auth_flag_set])
|
|
30
30
|
def status(token):
|
|
31
31
|
print(f"status: token={'set' if token else 'unset'}")
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ def _build_rlsbl_app():
|
|
|
34
34
|
"release",
|
|
35
35
|
help="create a new release",
|
|
36
36
|
args=[strictcli.Arg(name="bump", help="version bump type")],
|
|
37
|
-
|
|
37
|
+
flag_sets=[auth_flag_set],
|
|
38
38
|
)
|
|
39
39
|
@strictcli.flag("dry-run", type=bool, help="preview without making changes")
|
|
40
40
|
@strictcli.flag("yes", type=bool, help="non-interactive mode")
|
|
@@ -264,9 +264,9 @@ def test_e2e_kwargs_handler_defaults():
|
|
|
264
264
|
assert "replicas=1" in r.stdout
|
|
265
265
|
|
|
266
266
|
|
|
267
|
-
def
|
|
268
|
-
"""Command with **kwargs handler and
|
|
269
|
-
|
|
267
|
+
def test_e2e_kwargs_handler_with_flag_sets():
|
|
268
|
+
"""Command with **kwargs handler and flag sets receives flag set flag values."""
|
|
269
|
+
auth_flag_set = strictcli.FlagSet(
|
|
270
270
|
name="auth",
|
|
271
271
|
flags=[
|
|
272
272
|
strictcli.Flag(name="token", type=str, help="auth token", default=""),
|
|
@@ -275,7 +275,7 @@ def test_e2e_kwargs_handler_with_tags():
|
|
|
275
275
|
|
|
276
276
|
app = strictcli.App(name="kw", version="1.0.0", help="kwargs test app")
|
|
277
277
|
|
|
278
|
-
@app.command("push", help="push changes",
|
|
278
|
+
@app.command("push", help="push changes", flag_sets=[auth_flag_set])
|
|
279
279
|
def push_handler(**kwargs):
|
|
280
280
|
print(f"token={kwargs['token']}")
|
|
281
281
|
return 0
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
"""Tests for the
|
|
1
|
+
"""Tests for the flag set system."""
|
|
2
2
|
|
|
3
3
|
import strictcli
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def
|
|
7
|
-
"""
|
|
8
|
-
|
|
6
|
+
def test_flag_set_with_single_flag():
|
|
7
|
+
"""FlagSet with single flag applied to command."""
|
|
8
|
+
verbose_flag_set = strictcli.FlagSet(
|
|
9
9
|
name="verbose",
|
|
10
10
|
flags=[strictcli.Flag(name="verbose", type=bool, help="verbose output")],
|
|
11
11
|
)
|
|
12
12
|
app = strictcli.App(name="test", version="1.0.0", help="test app")
|
|
13
13
|
|
|
14
|
-
@app.command("cmd", help="a command",
|
|
14
|
+
@app.command("cmd", help="a command", flag_sets=[verbose_flag_set])
|
|
15
15
|
def cmd(verbose):
|
|
16
16
|
print(f"verbose={verbose}")
|
|
17
17
|
|
|
@@ -20,9 +20,9 @@ def test_tag_with_single_flag():
|
|
|
20
20
|
assert "verbose=True" in r.stdout
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def
|
|
24
|
-
"""
|
|
25
|
-
|
|
23
|
+
def test_flag_set_with_multiple_flags():
|
|
24
|
+
"""FlagSet with multiple flags applied to command."""
|
|
25
|
+
output_flag_set = strictcli.FlagSet(
|
|
26
26
|
name="output",
|
|
27
27
|
flags=[
|
|
28
28
|
strictcli.Flag(name="format", type=str, help="output format", default="text"),
|
|
@@ -31,7 +31,7 @@ def test_tag_with_multiple_flags():
|
|
|
31
31
|
)
|
|
32
32
|
app = strictcli.App(name="test", version="1.0.0", help="test app")
|
|
33
33
|
|
|
34
|
-
@app.command("cmd", help="a command",
|
|
34
|
+
@app.command("cmd", help="a command", flag_sets=[output_flag_set])
|
|
35
35
|
def cmd(format, color):
|
|
36
36
|
print(f"format={format} color={color}")
|
|
37
37
|
|
|
@@ -41,15 +41,15 @@ def test_tag_with_multiple_flags():
|
|
|
41
41
|
assert "color=True" in r.stdout
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
def
|
|
45
|
-
"""
|
|
46
|
-
|
|
44
|
+
def test_flag_set_flags_in_command_flags():
|
|
45
|
+
"""FlagSet flags appear in command's flags list."""
|
|
46
|
+
flag_set = strictcli.FlagSet(
|
|
47
47
|
name="debug",
|
|
48
48
|
flags=[strictcli.Flag(name="debug", type=bool, help="enable debug mode")],
|
|
49
49
|
)
|
|
50
50
|
app = strictcli.App(name="test", version="1.0.0", help="test app")
|
|
51
51
|
|
|
52
|
-
@app.command("cmd", help="a command",
|
|
52
|
+
@app.command("cmd", help="a command", flag_sets=[flag_set])
|
|
53
53
|
def cmd(debug):
|
|
54
54
|
pass
|
|
55
55
|
|
|
@@ -57,15 +57,15 @@ def test_tag_flags_in_command_flags():
|
|
|
57
57
|
assert app._commands["cmd"].flags[0].name == "debug"
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def
|
|
61
|
-
"""
|
|
62
|
-
|
|
60
|
+
def test_flag_set_flags_in_help():
|
|
61
|
+
"""FlagSet flags shown in help output."""
|
|
62
|
+
flag_set = strictcli.FlagSet(
|
|
63
63
|
name="debug",
|
|
64
64
|
flags=[strictcli.Flag(name="debug", type=bool, help="enable debug mode")],
|
|
65
65
|
)
|
|
66
66
|
app = strictcli.App(name="test", version="1.0.0", help="test app")
|
|
67
67
|
|
|
68
|
-
@app.command("cmd", help="a command",
|
|
68
|
+
@app.command("cmd", help="a command", flag_sets=[flag_set])
|
|
69
69
|
def cmd(debug):
|
|
70
70
|
pass
|
|
71
71
|
|
|
@@ -75,9 +75,9 @@ def test_tag_flags_in_help():
|
|
|
75
75
|
assert "enable debug mode" in r.stdout
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def
|
|
79
|
-
"""
|
|
80
|
-
|
|
78
|
+
def test_flag_set_flag_values_parsed():
|
|
79
|
+
"""FlagSet flag values parsed correctly through the full pipeline."""
|
|
80
|
+
auth_flag_set = strictcli.FlagSet(
|
|
81
81
|
name="auth",
|
|
82
82
|
flags=[
|
|
83
83
|
strictcli.Flag(name="token", type=str, help="auth token", default="none"),
|
|
@@ -86,11 +86,11 @@ def test_tag_flag_values_parsed():
|
|
|
86
86
|
)
|
|
87
87
|
app = strictcli.App(name="test", version="1.0.0", help="test app")
|
|
88
88
|
|
|
89
|
-
@app.command("deploy", help="deploy the app",
|
|
89
|
+
@app.command("deploy", help="deploy the app", flag_sets=[auth_flag_set])
|
|
90
90
|
def deploy(token, insecure):
|
|
91
91
|
print(f"token={token} insecure={insecure}")
|
|
92
92
|
|
|
93
|
-
# Test with all
|
|
93
|
+
# Test with all flag set flags provided
|
|
94
94
|
r = app.test(["deploy", "--token", "abc123", "--insecure"])
|
|
95
95
|
assert r.exit_code == 0
|
|
96
96
|
assert "token=abc123" in r.stdout
|
|
@@ -190,22 +190,22 @@ class TestPassthroughWithArgsRaisesValueError:
|
|
|
190
190
|
assert "args" in str(e)
|
|
191
191
|
|
|
192
192
|
|
|
193
|
-
class
|
|
194
|
-
def
|
|
193
|
+
class TestPassthroughWithFlagSetsRaisesValueError:
|
|
194
|
+
def test_flag_sets(self):
|
|
195
195
|
pt = strictcli.Passthrough(handler=lambda n, a, g: 0)
|
|
196
|
-
|
|
196
|
+
flag_set = strictcli.FlagSet(
|
|
197
197
|
name="auth",
|
|
198
198
|
flags=[strictcli.Flag(name="token", type=str, help="token", default="")],
|
|
199
199
|
)
|
|
200
200
|
app = _build_app()
|
|
201
201
|
try:
|
|
202
|
-
@app.command("exec", help="run", passthrough=pt,
|
|
202
|
+
@app.command("exec", help="run", passthrough=pt, flag_sets=[flag_set])
|
|
203
203
|
def exec_cmd(token):
|
|
204
204
|
pass
|
|
205
205
|
assert False, "should have raised ValueError"
|
|
206
206
|
except ValueError as e:
|
|
207
207
|
assert "passthrough" in str(e)
|
|
208
|
-
assert "
|
|
208
|
+
assert "flag sets" in str(e)
|
|
209
209
|
|
|
210
210
|
|
|
211
211
|
class TestPassthroughWithMutexRaisesValueError:
|
|
@@ -115,14 +115,14 @@ def test_env_prefix_correct():
|
|
|
115
115
|
assert app._commands["cmd"].flags[0].env == "MYAPP_TARGET"
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
def
|
|
119
|
-
"""Command inherits
|
|
120
|
-
|
|
118
|
+
def test_flag_set_merging():
|
|
119
|
+
"""Command inherits flag set flags."""
|
|
120
|
+
verbose_flag_set = strictcli.FlagSet(
|
|
121
121
|
name="verbose", flags=[strictcli.Flag(name="verbose", type=bool, help="verbose output")]
|
|
122
122
|
)
|
|
123
123
|
app = strictcli.App(name="test", version="1.0.0", help="test app")
|
|
124
124
|
|
|
125
|
-
@app.command("cmd", help="a command",
|
|
125
|
+
@app.command("cmd", help="a command", flag_sets=[verbose_flag_set])
|
|
126
126
|
def cmd(verbose):
|
|
127
127
|
pass
|
|
128
128
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
9fe0fe3a00effc0c9e39704a0bd0cc865542da83
|
strictcli-0.18.0/.rlsbl/version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.67.1
|
|
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
|