From 749da810a4463dd2f61d81db9ded0560416b2420 Mon Sep 17 00:00:00 2001 From: Espen Albert Date: Tue, 24 Mar 2026 10:15:07 +0000 Subject: [PATCH 1/2] fix(docs): Align Typer CLI option required/default with Typer rules --- pkg_ext/_internal/signature_parser.py | 22 ++++++++++++-------- pkg_ext/_internal/signature_parser_test.py | 24 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/pkg_ext/_internal/signature_parser.py b/pkg_ext/_internal/signature_parser.py index 27fc870..12ec5e5 100644 --- a/pkg_ext/_internal/signature_parser.py +++ b/pkg_ext/_internal/signature_parser.py @@ -388,11 +388,16 @@ def _extract_choices(annotation: Any) -> list[str] | None: return None -def _is_required(param_info: Any) -> bool: - """Check if parameter is required (no default value).""" - if param_info.default is None is param_info.default_factory: - return True - return param_info.default is ... +def is_cli_required_for_docs(info: ParameterInfo) -> bool: + return info.default is ... and info.default_factory is None + + +def _cli_default_object_for_docs(info: ParameterInfo) -> Any: + if info.default is ...: + if info.default_factory is None: + raise ValueError("required CLI parameter has no default to materialize") + return info.default_factory() + return info.default def _format_envvar(envvar: str | list[str] | None) -> str | None: @@ -419,9 +424,10 @@ def extract_cli_params(func: Callable) -> list[CLIParamInfo]: info = param.default annotation = hints.get(name) is_arg = isinstance(info, ArgumentInfo) + required = is_cli_required_for_docs(info) default_repr: str | None = None - if not _is_required(info): - default_repr = stable_repr(info.default) + if not required: + default_repr = stable_repr(_cli_default_object_for_docs(info)) params.append( CLIParamInfo( param_name=name, @@ -429,7 +435,7 @@ def extract_cli_params(func: Callable) -> list[CLIParamInfo]: flags=[] if is_arg else _resolve_cli_flags(name, info), help=info.help, default_repr=default_repr, - required=_is_required(info), + required=required, envvar=_format_envvar(info.envvar), is_argument=is_arg, hidden=info.hidden, diff --git a/pkg_ext/_internal/signature_parser_test.py b/pkg_ext/_internal/signature_parser_test.py index bb6e9bd..a238aa6 100644 --- a/pkg_ext/_internal/signature_parser_test.py +++ b/pkg_ext/_internal/signature_parser_test.py @@ -134,6 +134,30 @@ def test_extract_cli_params(): assert verbose_param.flags == ["--verbose"] +def test_extract_cli_params_default_factory_and_none_defaults(): + def cmd( + exclude: list[str] = typer.Option(..., "--exclude", default_factory=list), + tflint: bool | None = typer.Option(None, "--tflint/--no-tflint"), + include: list[str] = typer.Option([], "--include"), + nested: list[str] = typer.Option( + ..., + "--nested", + default_factory=lambda: list(("p1", "p2")), + ), + ) -> None: + pass + + ps = {p.param_name: p for p in extract_cli_params(cmd)} + assert not ps["exclude"].required + assert ps["exclude"].default_repr == "[]" + assert not ps["tflint"].required + assert ps["tflint"].default_repr == "None" + assert not ps["include"].required + assert ps["include"].default_repr == "[]" + assert not ps["nested"].required + assert ps["nested"].default_repr == "['p1', 'p2']" + + def test_stable_repr_normalizes_memory_addresses(): sentinel = object() result = stable_repr(sentinel) From 01a60e99bbbfec7df9664f424927f8e71d8eb20f Mon Sep 17 00:00:00 2001 From: Espen Albert Date: Tue, 24 Mar 2026 10:16:21 +0000 Subject: [PATCH 2/2] chore: regen --- .changelog/026.yaml | 12 ++++++++++++ .groups.yaml | 1 - docs/api_commands/index.md | 8 ++++---- docs/changelog/index.md | 10 +++++----- docs/example/index.md | 4 ++-- docs/generate/index.md | 6 +++--- docs/stability/index.md | 4 ++-- docs/workflows/index.md | 6 +++--- 8 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 .changelog/026.yaml diff --git a/.changelog/026.yaml b/.changelog/026.yaml new file mode 100644 index 0000000..c70cc4b --- /dev/null +++ b/.changelog/026.yaml @@ -0,0 +1,12 @@ +name: is_cli_required_for_docs +ts: 2026-03-24 10:15:38.763157+00:00 +type: keep_private +full_path: _internal.signature_parser.is_cli_required_for_docs +--- +name: api_dump +ts: 2026-03-24 10:15:51.990437+00:00 +type: fix +author: Espen Albert +changelog_message: 'fix(docs): Align Typer CLI option required/default with Typer rules' +message: 'fix(docs): Align Typer CLI option required/default with Typer rules' +short_sha: 749da8 diff --git a/.groups.yaml b/.groups.yaml index 4c3645d..86b55a7 100644 --- a/.groups.yaml +++ b/.groups.yaml @@ -47,7 +47,6 @@ groups: - _internal.cli.gen_cmds owned_refs: - _internal.cli.gen_cmds.gen_docs - - _internal.cli.gen_cmds.gen_tests - name: stability owned_modules: - _internal.cli.stability_cmds diff --git a/docs/api_commands/index.md b/docs/api_commands/index.md index d156a03..a8c364a 100644 --- a/docs/api_commands/index.md +++ b/docs/api_commands/index.md @@ -17,7 +17,7 @@ > **Since:** 0.1.0 ```python -def diff_api(*, baseline_ref: str | None = ...): +def diff_api(*, baseline_ref: str | None = None): ... ``` @@ -27,7 +27,7 @@ Show API changes between baseline and dev dump. | Flag | Type | Default | Description | |---|---|---|---| -| `--baseline` | `str | None` | *required* | Git tag/ref to compare against (default: {pkg}.api.yaml file) | +| `--baseline` | `str | None` | `None` | Git tag/ref to compare against (default: {pkg}.api.yaml file) | ### Changes @@ -43,7 +43,7 @@ Show API changes between baseline and dev dump. > **Since:** 0.1.0 ```python -def dump_api(*, output: Path | None = ..., dev: bool = False): +def dump_api(*, output: Path | None = None, dev: bool = False): ... ``` @@ -53,7 +53,7 @@ Dump public API to YAML for diffing and breaking change detection. | Flag | Type | Default | Description | |---|---|---|---| -| `-o`, `--output` | `Path | None` | *required* | Output file path | +| `-o`, `--output` | `Path | None` | `None` | Output file path | | `--dev` | `bool` | `False` | Write to -dev file (gitignored for local comparison) | ### Changes diff --git a/docs/changelog/index.md b/docs/changelog/index.md index a9d553d..2f513b8 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -69,7 +69,7 @@ Create a ChoreAction for internal changes that warrant a release. > **Since:** 0.1.0 ```python -def promote(*, name: str | None = ..., group: str | None = ..., module_filter: str | None = ..., pattern: str | None = ..., undecided: bool = False, pr_number: int = 0): +def promote(*, name: str | None = None, group: str | None = None, module_filter: str | None = None, pattern: str | None = None, undecided: bool = False, pr_number: int = 0): ... ``` @@ -79,10 +79,10 @@ Promote symbols to public API (private or undecided). | Flag | Type | Default | Description | |---|---|---|---| -| `--name`, `-n` | `str | None` | *required* | Symbol name to promote | -| `--group`, `-g` | `str | None` | *required* | Target group | -| `--module`, `-m` | `str | None` | *required* | Filter by module path prefix (inside the package, don't include the package name) | -| `--pattern`, `-p` | `str | None` | *required* | Filter by name pattern (e.g., 'dump_*') | +| `--name`, `-n` | `str | None` | `None` | Symbol name to promote | +| `--group`, `-g` | `str | None` | `None` | Target group | +| `--module`, `-m` | `str | None` | `None` | Filter by module path prefix (inside the package, don't include the package name) | +| `--pattern`, `-p` | `str | None` | `None` | Filter by name pattern (e.g., 'dump_*') | | `--undecided`, `-u` | `bool` | `False` | Include symbols without changelog entry (not yet decided) | | `--pr` | `int` | `0` | PR number (auto-detected if not provided) | diff --git a/docs/example/index.md b/docs/example/index.md index 3aa3e5c..45455b0 100644 --- a/docs/example/index.md +++ b/docs/example/index.md @@ -40,7 +40,7 @@ Verify all symbols in examples_include have corresponding .md files. > **Since:** 0.4.0 ```python -def gen_example_prompt(*, group: str | None = ...): +def gen_example_prompt(*, group: str | None = None): ... ``` @@ -50,7 +50,7 @@ Build an AI prompt for missing example docs and copy to clipboard. | Flag | Type | Default | Description | |---|---|---|---| -| `-g`, `--group` | `str | None` | *required* | Generate for specific group only | +| `-g`, `--group` | `str | None` | `None` | Generate for specific group only | ### Changes diff --git a/docs/generate/index.md b/docs/generate/index.md index 435630a..17f92be 100644 --- a/docs/generate/index.md +++ b/docs/generate/index.md @@ -16,7 +16,7 @@ > **Since:** 0.1.0 ```python -def gen_docs(*, output_dir: Path | None = ..., group: str | None = ...): +def gen_docs(*, output_dir: Path | None = None, group: str | None = None): ... ``` @@ -26,8 +26,8 @@ Generate documentation from public API. | Flag | Type | Default | Description | |---|---|---|---| -| `-o`, `--output-dir` | `Path | None` | *required* | Output directory (default: docs/) | -| `-g`, `--group` | `str | None` | *required* | Generate for specific group only | +| `-o`, `--output-dir` | `Path | None` | `None` | Output directory (default: docs/) | +| `-g`, `--group` | `str | None` | `None` | Generate for specific group only | ### Changes diff --git a/docs/stability/index.md b/docs/stability/index.md index 4fc0ba0..e88d4d1 100644 --- a/docs/stability/index.md +++ b/docs/stability/index.md @@ -18,7 +18,7 @@ > **Since:** 0.1.0 ```python -def dep(*, target: str = ..., replacement: str | None = ...): +def dep(*, target: str = ..., replacement: str | None = None): ... ``` @@ -29,7 +29,7 @@ Mark target as deprecated. | Flag | Type | Default | Description | |---|---|---|---| | `--target`, `-t` | `str` | *required* | Target: group \| group.symbol \| group.symbol.arg | -| `--replacement`, `-r` | `str | None` | *required* | Replacement suggestion | +| `--replacement`, `-r` | `str | None` | `None` | Replacement suggestion | ### Changes diff --git a/docs/workflows/index.md b/docs/workflows/index.md index 7a2547a..0bb3d6d 100644 --- a/docs/workflows/index.md +++ b/docs/workflows/index.md @@ -46,7 +46,7 @@ def post_merge(*, explicit_pr: int = 0, push: bool = False, skip_clean_old_entri > **Since:** 0.1.0 ```python -def pre_change(*, group: str | None = ..., git_changes_since: GitSince = , skip_fix_commits: bool = False, full: bool = False, skip_docs: bool = False, skip_open_in_editor: bool | None = ..., keep_private: bool = False): +def pre_change(*, group: str | None = None, git_changes_since: GitSince = , skip_fix_commits: bool = False, full: bool = False, skip_docs: bool = False, skip_open_in_editor: bool | None = None, keep_private: bool = False): ... ``` @@ -56,12 +56,12 @@ Handle new symbols, update changelog, optionally sync files and docs. | Flag | Type | Default | Description | |---|---|---|---| -| `-g`, `--group` | `str | None` | *required* | Generate for specific group only | +| `-g`, `--group` | `str | None` | `None` | Generate for specific group only | | `--git-since` | `GitSince` | `` | Will use git log to look for 'fix' commits to include in the changelog [no_git_changes, last_git_tag, pr_base_branch, default] | | `--skip-fix-commits` | `bool` | `False` | Skip prompts for fix commits in git history | | `--full` | `bool` | `False` | Run pre-commit workflow after pre-change (sync + docs + diff) | | `--skip-docs` | `bool` | `False` | Skip doc regeneration | -| `--skip-open` | `bool | None` | *required* | Skip opening files in editor | +| `--skip-open` | `bool | None` | `None` | Skip opening files in editor | | `--keep-private` | `bool` | `False` | Automatically keep all new symbols private without prompting | ### Changes