strictcli 0.25.0__tar.gz → 0.26.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.25.0 → strictcli-0.26.0}/.rlsbl/config.json +2 -1
- {strictcli-0.25.0 → strictcli-0.26.0}/PKG-INFO +1 -1
- {strictcli-0.25.0 → strictcli-0.26.0}/package-lock.json +2 -2
- {strictcli-0.25.0 → strictcli-0.26.0}/package.json +1 -1
- {strictcli-0.25.0 → strictcli-0.26.0}/pyproject.toml +1 -1
- {strictcli-0.25.0 → strictcli-0.26.0}/strictcli/__init__.py +15 -12
- strictcli-0.26.0/strictcli/py.typed +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/uv.lock +1 -1
- {strictcli-0.25.0 → strictcli-0.26.0}/.claude/settings.json +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/.github/workflows/ci-pypi.yml +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/.github/workflows/ci.yml +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/.github/workflows/publish.yml +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/.gitignore +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/.npmignore +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/.rlsbl/hashes.json +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/.rlsbl/managed-files.json +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/.strictcli/schema.json +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/CLAUDE.md +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/LICENSE +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/README.md +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/index.js +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/postinstall.js +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_arg_default.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_arg_default_validation.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_at_prefix.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_auto_version.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_call.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_check_command.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_check_discovery.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_check_public_api.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_check_runner.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_check_schema.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_check_types.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_choices.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_choices_none.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_command_help_suggestion.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_command_tags.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_compound_types.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_config.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_config_fields.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_config_file_path.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_config_set_bugs.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_context.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_deep_nesting.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_dependencies.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_deprecated.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_dump_schema.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_e2e.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_env.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_exit_codes.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_flag_sets.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_float_type.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_global_flags.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_help.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_hermetic.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_int_type.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_invoke.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_keyword_flags.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_mcp.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_mutex.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_nesting.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_parser.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_passthrough.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_provenance.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_provenance_phase2.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_registration.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_repeatable.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_reserved_global_flags.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_tagdsl.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_toml_loading.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_tool_export.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_typed_args.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_unique.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_utilities.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_validate.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_variadic.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/tests/test_visibility.py +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/todo/.defer/deferred.md +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/todo/.done/keyword-collision-in-flag-param-name.md +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/todo/.done/original-idea.md +0 -0
- {strictcli-0.25.0 → strictcli-0.26.0}/todo/.done/public-check-runner-api.md +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strictcli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "strictcli",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.26.0",
|
|
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.
|
|
5
|
+
__version__ = "0.26.0"
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"App", "Flag", "Arg", "FlagSet", "MutexGroup", "CoRequired", "Requires",
|
|
@@ -30,7 +30,10 @@ import tomllib
|
|
|
30
30
|
from collections import deque
|
|
31
31
|
from dataclasses import dataclass, field
|
|
32
32
|
from pathlib import Path
|
|
33
|
-
from typing import Callable, Protocol, get_args, get_origin, runtime_checkable
|
|
33
|
+
from typing import Any, Callable, Protocol, TypeVar, get_args, get_origin, runtime_checkable
|
|
34
|
+
|
|
35
|
+
# TypeVar for decorator return types — preserves the decorated function's type
|
|
36
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
34
37
|
|
|
35
38
|
|
|
36
39
|
# Sentinel for distinguishing "not provided" from actual values
|
|
@@ -1904,10 +1907,10 @@ class Group:
|
|
|
1904
1907
|
hidden: bool = False,
|
|
1905
1908
|
interactive: bool = False,
|
|
1906
1909
|
config_fields: list[str] | None = None,
|
|
1907
|
-
) -> Callable:
|
|
1910
|
+
) -> Callable[[F], F]:
|
|
1908
1911
|
"""Decorator to register a command within this group."""
|
|
1909
1912
|
|
|
1910
|
-
def decorator(func:
|
|
1913
|
+
def decorator(func: F) -> F:
|
|
1911
1914
|
if name in self._groups:
|
|
1912
1915
|
raise ValueError(
|
|
1913
1916
|
f'command "{name}" collides with an existing group'
|
|
@@ -2202,9 +2205,9 @@ class App:
|
|
|
2202
2205
|
self._framework_fields[name] = cf
|
|
2203
2206
|
return cf
|
|
2204
2207
|
|
|
2205
|
-
def check(self, name: str):
|
|
2208
|
+
def check(self, name: str) -> Callable[[F], F]:
|
|
2206
2209
|
"""Decorator to register a check implementation."""
|
|
2207
|
-
def decorator(fn):
|
|
2210
|
+
def decorator(fn: F) -> F:
|
|
2208
2211
|
if not self._checks_enabled:
|
|
2209
2212
|
raise ValueError(
|
|
2210
2213
|
f'cannot register check "{name}": '
|
|
@@ -2508,10 +2511,10 @@ class App:
|
|
|
2508
2511
|
hidden: bool = False,
|
|
2509
2512
|
interactive: bool = False,
|
|
2510
2513
|
config_fields: list[str] | None = None,
|
|
2511
|
-
) -> Callable:
|
|
2514
|
+
) -> Callable[[F], F]:
|
|
2512
2515
|
"""Decorator to register a top-level command."""
|
|
2513
2516
|
|
|
2514
|
-
def decorator(func:
|
|
2517
|
+
def decorator(func: F) -> F:
|
|
2515
2518
|
cmd = _build_and_validate_command(
|
|
2516
2519
|
name,
|
|
2517
2520
|
help=help,
|
|
@@ -5114,10 +5117,10 @@ def flag(
|
|
|
5114
5117
|
validate: Callable | None = None,
|
|
5115
5118
|
repeatable: bool = False,
|
|
5116
5119
|
unique: object = _MISSING,
|
|
5117
|
-
) -> Callable:
|
|
5120
|
+
) -> Callable[[F], F]:
|
|
5118
5121
|
"""Module-level decorator to attach a Flag to a command handler."""
|
|
5119
5122
|
|
|
5120
|
-
def decorator(func:
|
|
5123
|
+
def decorator(func: F) -> F:
|
|
5121
5124
|
f = Flag(
|
|
5122
5125
|
name=name,
|
|
5123
5126
|
short=short,
|
|
@@ -5150,10 +5153,10 @@ def arg(
|
|
|
5150
5153
|
variadic: bool = False,
|
|
5151
5154
|
type: type = str,
|
|
5152
5155
|
choices: list | None = None,
|
|
5153
|
-
) -> Callable:
|
|
5156
|
+
) -> Callable[[F], F]:
|
|
5154
5157
|
"""Module-level decorator to attach an Arg to a command handler."""
|
|
5155
5158
|
|
|
5156
|
-
def decorator(func:
|
|
5159
|
+
def decorator(func: F) -> F:
|
|
5157
5160
|
a = Arg(
|
|
5158
5161
|
name=name, help=help, required=required, default=default,
|
|
5159
5162
|
variadic=variadic, type=type, choices=choices,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|