Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changelog/026.yaml
Original file line number Diff line number Diff line change
@@ -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
1 change: 0 additions & 1 deletion .groups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions docs/api_commands/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
> **Since:** 0.1.0

```python
def diff_api(*, baseline_ref: str | None = ...):
def diff_api(*, baseline_ref: str | None = None):
...
```

Expand All @@ -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

Expand All @@ -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):
...
```

Expand All @@ -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
Expand Down
10 changes: 5 additions & 5 deletions docs/changelog/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
...
```

Expand All @@ -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) |

Expand Down
4 changes: 2 additions & 2 deletions docs/example/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
...
```

Expand All @@ -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

Expand Down
6 changes: 3 additions & 3 deletions docs/generate/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
...
```

Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions docs/stability/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
> **Since:** 0.1.0

```python
def dep(*, target: str = ..., replacement: str | None = ...):
def dep(*, target: str = ..., replacement: str | None = None):
...
```

Expand All @@ -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

Expand Down
6 changes: 3 additions & 3 deletions docs/workflows/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <GitSince.DEFAULT: 'default'>, 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 = <GitSince.DEFAULT: 'default'>, skip_fix_commits: bool = False, full: bool = False, skip_docs: bool = False, skip_open_in_editor: bool | None = None, keep_private: bool = False):
...
```

Expand All @@ -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` | `<GitSince.DEFAULT: 'default'>` | 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
Expand Down
22 changes: 14 additions & 8 deletions pkg_ext/_internal/signature_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -419,17 +424,18 @@ 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,
type_annotation=_annotation_str(annotation),
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,
Expand Down
24 changes: 24 additions & 0 deletions pkg_ext/_internal/signature_parser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading