strictcli 0.15.0__tar.gz → 0.15.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.15.1/.rlsbl/changes/.validated +1 -0
- strictcli-0.15.1/.rlsbl/changes/0.15.0.md +9 -0
- strictcli-0.15.1/.rlsbl/changes/0.15.1.jsonl +6 -0
- strictcli-0.15.1/.rlsbl/changes/0.15.1.md +9 -0
- strictcli-0.15.1/.rlsbl/config.json +12 -0
- strictcli-0.15.1/.rlsbl/releases/v0.15.1.toml +8 -0
- strictcli-0.15.1/.rlsbl/version +1 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/CHANGELOG.md +13 -3
- {strictcli-0.15.0 → strictcli-0.15.1}/PKG-INFO +1 -1
- {strictcli-0.15.0 → strictcli-0.15.1}/package-lock.json +2 -2
- {strictcli-0.15.0 → strictcli-0.15.1}/package.json +1 -1
- {strictcli-0.15.0 → strictcli-0.15.1}/pyproject.toml +1 -1
- {strictcli-0.15.0 → strictcli-0.15.1}/strictcli/__init__.py +65 -62
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_config.py +28 -1
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_config_set_bugs.py +24 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/uv.lock +1 -1
- strictcli-0.15.0/.rlsbl/changes/.validated +0 -1
- strictcli-0.15.0/.rlsbl/config.json +0 -8
- strictcli-0.15.0/.rlsbl/version +0 -1
- {strictcli-0.15.0 → strictcli-0.15.1}/.claude/settings.json +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.github/workflows/ci.yml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.github/workflows/publish.yml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.gitignore +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.npmignore +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.claude/settings.json +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.github/workflows/ci.yml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.github/workflows/publish.yml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.gitignore +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.npmignore +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.rlsbl/hooks/post-release.sh +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.rlsbl/hooks/pre-checks.sh +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.rlsbl/hooks/pre-release.sh +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.rlsbl/lint/go.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.rlsbl/lint/npm.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/.rlsbl/lint/python.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/CHANGELOG.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/CLAUDE.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/bases/LICENSE +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.10.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.10.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.11.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.11.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.12.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.12.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.13.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.13.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.14.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.14.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.15.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.4.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.4.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.4.1.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.4.1.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.5.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.5.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.6.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.6.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.6.1.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.6.1.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.7.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.7.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.7.1.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.7.1.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.1.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.1.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.2.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.2.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.3.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.3.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.4.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.4.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.5.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.5.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.6.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.6.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.7.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.8.7.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.9.0.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.9.0.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.9.1.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/0.9.1.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/changes/unreleased.jsonl +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/hashes.json +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/hooks/post-release.sh +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/hooks/pre-checks.sh +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/hooks/pre-release.sh +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/lint/go.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/lint/npm.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/lint/python.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/unreleased.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.10.0.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.11.0.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.12.0.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.13.0.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.15.0.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.8.5.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.8.6.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.8.7.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.9.0.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.rlsbl/releases/v0.9.1.toml +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/.strictcli/schema.json +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/CLAUDE.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/LICENSE +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/README.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/index.js +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/postinstall.js +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_arg_default.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_at_prefix.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_auto_version.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_check_command.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_check_discovery.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_check_runner.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_check_schema.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_check_types.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_choices.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_command_help_suggestion.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_config_file_path.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_deep_nesting.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_dependencies.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_deprecated.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_dump_schema.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_e2e.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_env.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_exit_codes.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_float_type.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_global_flags.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_help.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_int_type.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_mutex.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_nesting.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_parser.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_passthrough.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_registration.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_repeatable.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_tagdsl.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_tags.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_toml_loading.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_validate.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/tests/test_variadic.py +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/todo/.defer/deferred.md +0 -0
- {strictcli-0.15.0 → strictcli-0.15.1}/todo/.done/original-idea.md +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4df925097e74f9a99e5ece43f3df2cd90f7b3e17
|
|
@@ -0,0 +1,9 @@
|
|
|
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.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{"commits":["face7b1fcf296fcd3ba1d5d7b15194c71f79ac0c"],"user_facing":true,"description":"**Breaking.** Config set now accepts negative numeric values. Unknown single-dash tokens produce 'unexpected argument' instead of 'unknown flag'.","type":"breaking"}
|
|
2
|
+
{"commits":["d4bbb758e89f174279d3d1a281ded985c47c958a"],"user_facing":true,"description":"**Fix.** Config show now outputs lowercase true/false for bool values, matching Go implementation.","type":"fix"}
|
|
3
|
+
{"commits":["b2bdcfa7388ac5904c5da98a017c4248cc2737ea"],"user_facing":false}
|
|
4
|
+
{"commits":["053285501d0c135dffc782f038ac4f35881bcd89"],"user_facing":false}
|
|
5
|
+
{"commits":["48504dbc7255e89a73c663f4745eaf7a64ec0466"],"user_facing":false}
|
|
6
|
+
{"commits":["ca62cef3775568ecda2e7bdadf04ee813608d9d6"],"user_facing":false}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
## 0.15.1
|
|
2
|
+
|
|
3
|
+
### Breaking
|
|
4
|
+
|
|
5
|
+
- **Breaking.** Config set now accepts negative numeric values. Unknown single-dash tokens produce 'unexpected argument' instead of 'unknown flag'.
|
|
6
|
+
|
|
7
|
+
### Fixes
|
|
8
|
+
|
|
9
|
+
- **Fix.** Config show now outputs lowercase true/false for bool values, matching Go implementation.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Version bump type: patch, minor, or major
|
|
2
|
+
bump = "patch"
|
|
3
|
+
# Short description of this release (required)
|
|
4
|
+
description = "Config show bool parity and negative values in config set"
|
|
5
|
+
# Optional context explaining why these changes were made
|
|
6
|
+
context = "config show now outputs lowercase true/false matching Go. config set accepts negative numeric values."
|
|
7
|
+
include = ["pypi", "npm"]
|
|
8
|
+
exclude = []
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.63.0
|
|
@@ -2,19 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
-
## 0.15.
|
|
5
|
+
## 0.15.1
|
|
6
6
|
|
|
7
|
-
Config
|
|
7
|
+
Config show bool parity and negative values in config set
|
|
8
8
|
|
|
9
9
|
<details>
|
|
10
10
|
<summary>Context</summary>
|
|
11
11
|
|
|
12
|
-
config
|
|
12
|
+
config show now outputs lowercase true/false matching Go. config set accepts negative numeric values.
|
|
13
13
|
|
|
14
14
|
</details>
|
|
15
15
|
|
|
16
16
|
### Breaking
|
|
17
17
|
|
|
18
|
+
- **Breaking.** Config set now accepts negative numeric values. Unknown single-dash tokens produce 'unexpected argument' instead of 'unknown flag'.
|
|
19
|
+
|
|
20
|
+
### Fixes
|
|
21
|
+
|
|
22
|
+
- **Fix.** Config show now outputs lowercase true/false for bool values, matching Go implementation.
|
|
23
|
+
|
|
24
|
+
## 0.15.0
|
|
25
|
+
|
|
26
|
+
### Breaking
|
|
27
|
+
|
|
18
28
|
- **Breaking.** `config set` now validates keys against registered flags and rejects unknown keys.
|
|
19
29
|
|
|
20
30
|
### Features
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strictcli",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "strictcli",
|
|
9
|
-
"version": "0.15.
|
|
9
|
+
"version": "0.15.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.15.
|
|
5
|
+
__version__ = "0.15.1"
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"App", "Flag", "Arg", "Tag", "MutexGroup", "CoRequired", "Requires",
|
|
@@ -125,6 +125,15 @@ def _coerce_config_value(value: object, flag: "Flag") -> object:
|
|
|
125
125
|
raise ValueError(f"unsupported flag type {flag.type}")
|
|
126
126
|
|
|
127
127
|
|
|
128
|
+
def _format_config_value(value: object) -> str:
|
|
129
|
+
"""Format a config value for display, matching Go's formatConfigValue."""
|
|
130
|
+
if value is None:
|
|
131
|
+
return "<nil>"
|
|
132
|
+
if isinstance(value, bool):
|
|
133
|
+
return "true" if value else "false"
|
|
134
|
+
return str(value)
|
|
135
|
+
|
|
136
|
+
|
|
128
137
|
_CHECK_NAME_RE = re.compile(r"^[a-z][a-z0-9-]*$")
|
|
129
138
|
_CHECK_REQUIRED_FIELDS = {"tags", "severity", "fast", "pure", "needs_network", "depends_on"}
|
|
130
139
|
_CHECK_VALID_SEVERITIES = {"error", "warn"}
|
|
@@ -306,12 +315,18 @@ def _strict_int(s: str) -> int:
|
|
|
306
315
|
Python's int() silently strips whitespace; Go's strconv.Atoi does not.
|
|
307
316
|
This matches Go's stricter behavior. Additionally, the result is
|
|
308
317
|
range-checked to fit in a signed 64-bit integer, matching Go's int/int64.
|
|
318
|
+
|
|
319
|
+
All errors raise ValueError with the same message format as Go's
|
|
320
|
+
parseIntStrict: "expected integer, got '<value>'".
|
|
309
321
|
"""
|
|
310
322
|
if s != s.strip():
|
|
311
|
-
raise ValueError(f"
|
|
312
|
-
|
|
323
|
+
raise ValueError(f"expected integer, got '{s}'")
|
|
324
|
+
try:
|
|
325
|
+
n = int(s)
|
|
326
|
+
except ValueError:
|
|
327
|
+
raise ValueError(f"expected integer, got '{s}'") from None
|
|
313
328
|
if n < -(2**63) or n > 2**63 - 1:
|
|
314
|
-
raise ValueError("integer
|
|
329
|
+
raise ValueError(f"expected integer, got '{s}'")
|
|
315
330
|
return n
|
|
316
331
|
|
|
317
332
|
|
|
@@ -1020,7 +1035,7 @@ class App:
|
|
|
1020
1035
|
else:
|
|
1021
1036
|
value = None
|
|
1022
1037
|
source = "default"
|
|
1023
|
-
print(f"{param} = {value} (source: {source})")
|
|
1038
|
+
print(f"{param} = {_format_config_value(value)} (source: {source})")
|
|
1024
1039
|
|
|
1025
1040
|
config_grp.commands["show"] = Command(
|
|
1026
1041
|
name="show",
|
|
@@ -1060,10 +1075,7 @@ class App:
|
|
|
1060
1075
|
if matched_flag.type == bool:
|
|
1061
1076
|
typed_value = _strict_bool(value)
|
|
1062
1077
|
elif matched_flag.type == int:
|
|
1063
|
-
|
|
1064
|
-
typed_value = _strict_int(value)
|
|
1065
|
-
except ValueError:
|
|
1066
|
-
raise ValueError(f"expected integer, got '{value}'")
|
|
1078
|
+
typed_value = _strict_int(value)
|
|
1067
1079
|
elif matched_flag.type == float:
|
|
1068
1080
|
try:
|
|
1069
1081
|
typed_value = _strict_float(value)
|
|
@@ -1295,10 +1307,8 @@ class App:
|
|
|
1295
1307
|
if f.type is int:
|
|
1296
1308
|
try:
|
|
1297
1309
|
_store_value(f, _strict_int(value_part))
|
|
1298
|
-
except ValueError:
|
|
1299
|
-
raise _ParseError(
|
|
1300
|
-
f"--{f.name}: expected integer, got {value_part!r}"
|
|
1301
|
-
)
|
|
1310
|
+
except ValueError as e:
|
|
1311
|
+
raise _ParseError(f"--{f.name}: {e}")
|
|
1302
1312
|
elif f.type is float:
|
|
1303
1313
|
try:
|
|
1304
1314
|
_store_value(f, _strict_float(value_part))
|
|
@@ -1339,10 +1349,8 @@ class App:
|
|
|
1339
1349
|
if f.type is int:
|
|
1340
1350
|
try:
|
|
1341
1351
|
_store_value(f, _strict_int(raw))
|
|
1342
|
-
except ValueError:
|
|
1343
|
-
raise _ParseError(
|
|
1344
|
-
f"--{f.name}: expected integer, got {raw!r}"
|
|
1345
|
-
)
|
|
1352
|
+
except ValueError as e:
|
|
1353
|
+
raise _ParseError(f"--{f.name}: {e}")
|
|
1346
1354
|
elif f.type is float:
|
|
1347
1355
|
try:
|
|
1348
1356
|
_store_value(f, _strict_float(raw))
|
|
@@ -1370,10 +1378,8 @@ class App:
|
|
|
1370
1378
|
if f.type is int:
|
|
1371
1379
|
try:
|
|
1372
1380
|
_store_value(f, _strict_int(raw))
|
|
1373
|
-
except ValueError:
|
|
1374
|
-
raise _ParseError(
|
|
1375
|
-
f"--{f.name}: expected integer, got {raw!r}"
|
|
1376
|
-
)
|
|
1381
|
+
except ValueError as e:
|
|
1382
|
+
raise _ParseError(f"--{f.name}: {e}")
|
|
1377
1383
|
elif f.type is float:
|
|
1378
1384
|
try:
|
|
1379
1385
|
_store_value(f, _strict_float(raw))
|
|
@@ -1414,10 +1420,9 @@ class App:
|
|
|
1414
1420
|
elif f.type is int:
|
|
1415
1421
|
try:
|
|
1416
1422
|
coerced = _strict_int(env_val)
|
|
1417
|
-
except ValueError:
|
|
1423
|
+
except ValueError as e:
|
|
1418
1424
|
raise _ParseError(
|
|
1419
|
-
f"--{f.name}:
|
|
1420
|
-
f"(from env var '{f.env}')"
|
|
1425
|
+
f"--{f.name}: {e} (from env var '{f.env}')"
|
|
1421
1426
|
)
|
|
1422
1427
|
cli_set[f.name] = [coerced] if f.repeatable else coerced
|
|
1423
1428
|
elif f.type is float:
|
|
@@ -1694,8 +1699,8 @@ def _parse_command(
|
|
|
1694
1699
|
if f.type is int:
|
|
1695
1700
|
try:
|
|
1696
1701
|
_store_value(f, _strict_int(value_part))
|
|
1697
|
-
except ValueError:
|
|
1698
|
-
raise _ParseError(f"--{f.name}:
|
|
1702
|
+
except ValueError as e:
|
|
1703
|
+
raise _ParseError(f"--{f.name}: {e}")
|
|
1699
1704
|
elif f.type is float:
|
|
1700
1705
|
try:
|
|
1701
1706
|
_store_value(f, _strict_float(value_part))
|
|
@@ -1736,8 +1741,8 @@ def _parse_command(
|
|
|
1736
1741
|
if f.type is int:
|
|
1737
1742
|
try:
|
|
1738
1743
|
_store_value(f, _strict_int(raw))
|
|
1739
|
-
except ValueError:
|
|
1740
|
-
raise _ParseError(f"--{f.name}:
|
|
1744
|
+
except ValueError as e:
|
|
1745
|
+
raise _ParseError(f"--{f.name}: {e}")
|
|
1741
1746
|
elif f.type is float:
|
|
1742
1747
|
try:
|
|
1743
1748
|
_store_value(f, _strict_float(raw))
|
|
@@ -1756,40 +1761,39 @@ def _parse_command(
|
|
|
1756
1761
|
continue
|
|
1757
1762
|
|
|
1758
1763
|
# -x (short form)
|
|
1759
|
-
if tok.startswith("-") and len(tok) == 2:
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
i += 1
|
|
1765
|
-
else:
|
|
1766
|
-
# str/int/float flag: consume next token as value
|
|
1767
|
-
if i + 1 < len(tokens):
|
|
1768
|
-
raw = tokens[i + 1]
|
|
1769
|
-
if f.type is int:
|
|
1770
|
-
try:
|
|
1771
|
-
_store_value(f, _strict_int(raw))
|
|
1772
|
-
except ValueError:
|
|
1773
|
-
raise _ParseError(f"--{f.name}: expected integer, got {raw!r}")
|
|
1774
|
-
elif f.type is float:
|
|
1775
|
-
try:
|
|
1776
|
-
_store_value(f, _strict_float(raw))
|
|
1777
|
-
except ValueError as e:
|
|
1778
|
-
raise _float_parse_error(f.name, raw, e)
|
|
1779
|
-
else:
|
|
1780
|
-
resolved, stdin_consumed_by[0] = _resolve_at_prefix(
|
|
1781
|
-
f.name, raw, stdin_consumed_by[0],
|
|
1782
|
-
)
|
|
1783
|
-
_store_value(f, resolved)
|
|
1784
|
-
i += 2
|
|
1785
|
-
else:
|
|
1786
|
-
raise _ParseError(f"flag '{tok}' requires a value")
|
|
1764
|
+
if tok.startswith("-") and len(tok) == 2 and tok in short_lookup:
|
|
1765
|
+
f = short_lookup[tok]
|
|
1766
|
+
if f.type is bool:
|
|
1767
|
+
cli_set[f.name] = True
|
|
1768
|
+
i += 1
|
|
1787
1769
|
else:
|
|
1788
|
-
|
|
1770
|
+
# str/int/float flag: consume next token as value
|
|
1771
|
+
if i + 1 < len(tokens):
|
|
1772
|
+
raw = tokens[i + 1]
|
|
1773
|
+
if f.type is int:
|
|
1774
|
+
try:
|
|
1775
|
+
_store_value(f, _strict_int(raw))
|
|
1776
|
+
except ValueError as e:
|
|
1777
|
+
raise _ParseError(f"--{f.name}: {e}")
|
|
1778
|
+
elif f.type is float:
|
|
1779
|
+
try:
|
|
1780
|
+
_store_value(f, _strict_float(raw))
|
|
1781
|
+
except ValueError as e:
|
|
1782
|
+
raise _float_parse_error(f.name, raw, e)
|
|
1783
|
+
else:
|
|
1784
|
+
resolved, stdin_consumed_by[0] = _resolve_at_prefix(
|
|
1785
|
+
f.name, raw, stdin_consumed_by[0],
|
|
1786
|
+
)
|
|
1787
|
+
_store_value(f, resolved)
|
|
1788
|
+
i += 2
|
|
1789
|
+
else:
|
|
1790
|
+
raise _ParseError(f"flag '{tok}' requires a value")
|
|
1789
1791
|
continue
|
|
1790
1792
|
|
|
1791
|
-
#
|
|
1792
|
-
|
|
1793
|
+
# Token starts with "-" but doesn't match any known flag;
|
|
1794
|
+
# treat as a positional arg (e.g. negative numbers like -7, -3.14)
|
|
1795
|
+
positionals.append(tok)
|
|
1796
|
+
i += 1
|
|
1793
1797
|
|
|
1794
1798
|
# Step 4: resolve env vars for flags not set by CLI
|
|
1795
1799
|
for f in cmd.flags:
|
|
@@ -1809,10 +1813,9 @@ def _parse_command(
|
|
|
1809
1813
|
elif f.type is int:
|
|
1810
1814
|
try:
|
|
1811
1815
|
coerced = _strict_int(env_val)
|
|
1812
|
-
except ValueError:
|
|
1816
|
+
except ValueError as e:
|
|
1813
1817
|
raise _ParseError(
|
|
1814
|
-
f"--{f.name}:
|
|
1815
|
-
f"(from env var '{f.env}')"
|
|
1818
|
+
f"--{f.name}: {e} (from env var '{f.env}')"
|
|
1816
1819
|
)
|
|
1817
1820
|
cli_set[f.name] = [coerced] if f.repeatable else coerced
|
|
1818
1821
|
elif f.type is float:
|
|
@@ -183,7 +183,34 @@ def test_config_show(tmp_path, monkeypatch):
|
|
|
183
183
|
assert r.exit_code == 0
|
|
184
184
|
assert "target = from-config (source: config)" in r.stdout
|
|
185
185
|
assert "count = 1 (source: default)" in r.stdout
|
|
186
|
-
assert "verbose =
|
|
186
|
+
assert "verbose = false (source: default)" in r.stdout
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# --- Test 10b: config show formats bools as lowercase (Go parity) ---
|
|
190
|
+
|
|
191
|
+
def test_config_show_lowercase_bools(tmp_path, monkeypatch):
|
|
192
|
+
"""Config show formats bools as lowercase true/false, matching Go output."""
|
|
193
|
+
config_home = _write_config(tmp_path, "testapp", {"verbose": True})
|
|
194
|
+
monkeypatch.setenv("XDG_CONFIG_HOME", config_home)
|
|
195
|
+
app = _make_config_app(config=True)
|
|
196
|
+
r = app.test(["config", "show"])
|
|
197
|
+
assert r.exit_code == 0
|
|
198
|
+
# Bool from config must be lowercase
|
|
199
|
+
assert "verbose = true (source: config)" in r.stdout
|
|
200
|
+
# None (no default for bool) must show as <nil>
|
|
201
|
+
assert "verbose = True" not in r.stdout
|
|
202
|
+
assert "verbose = False" not in r.stdout
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_config_show_bool_false_default_lowercase(tmp_path, monkeypatch):
|
|
206
|
+
"""Config show formats default False bool as lowercase false."""
|
|
207
|
+
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path))
|
|
208
|
+
app = _make_config_app(config=True)
|
|
209
|
+
r = app.test(["config", "show"])
|
|
210
|
+
assert r.exit_code == 0
|
|
211
|
+
# verbose has no explicit default, defaults to False -- must be lowercase
|
|
212
|
+
assert "verbose = false (source: default)" in r.stdout
|
|
213
|
+
assert "verbose = False" not in r.stdout
|
|
187
214
|
|
|
188
215
|
|
|
189
216
|
# --- Test 11: config respects XDG_CONFIG_HOME ---
|
|
@@ -198,6 +198,30 @@ def test_config_set_unknown_key_error(tmp_path):
|
|
|
198
198
|
assert r.stderr.strip() == "config set: unknown key 'nonexistent_flag'"
|
|
199
199
|
|
|
200
200
|
|
|
201
|
+
def test_config_set_negative_int(tmp_path):
|
|
202
|
+
"""config set accepts negative integers like -7 as positional values."""
|
|
203
|
+
app, config_file = _make_app(tmp_path)
|
|
204
|
+
|
|
205
|
+
r = app.test(["config", "set", "count", "-7"])
|
|
206
|
+
assert r.exit_code == 0, f"exit_code={r.exit_code}, stderr={r.stderr!r}"
|
|
207
|
+
|
|
208
|
+
data = json.loads(config_file.read_text())
|
|
209
|
+
assert isinstance(data["count"], int), f"expected int, got {type(data['count']).__name__}"
|
|
210
|
+
assert data["count"] == -7
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def test_config_set_negative_float(tmp_path):
|
|
214
|
+
"""config set accepts negative floats like -3.14 as positional values."""
|
|
215
|
+
app, config_file = _make_app(tmp_path)
|
|
216
|
+
|
|
217
|
+
r = app.test(["config", "set", "rate", "-3.14"])
|
|
218
|
+
assert r.exit_code == 0, f"exit_code={r.exit_code}, stderr={r.stderr!r}"
|
|
219
|
+
|
|
220
|
+
data = json.loads(config_file.read_text())
|
|
221
|
+
assert isinstance(data["rate"], float), f"expected float, got {type(data['rate']).__name__}"
|
|
222
|
+
assert data["rate"] == -3.14
|
|
223
|
+
|
|
224
|
+
|
|
201
225
|
def test_config_set_round_trip_typed(tmp_path):
|
|
202
226
|
"""Values set via config set are read back with correct types by a new app."""
|
|
203
227
|
app, config_file = _make_app(tmp_path)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a6dff298b7a2dad7fa928b088eeb4f3f9c0b2cf5
|
strictcli-0.15.0/.rlsbl/version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.60.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
|