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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ and versions are tracked in the repo-root `VERSION` file.

### Fixed

- Make `App.name` authoritative for single-command identity, reject duplicate
or post-materialization registrations deterministically, stabilize inferred
names across Click releases, and make module-level `@command()` functions
retrievable and directly runnable through `run_app()`.
- Redact sensitive option values across every declared alias and Click value
form, redact sensitive positional arguments, and protect conventional secret
parameter names automatically before argv reaches logs or history writers.
Expand Down
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ Use `App` when you want a named command:
app = base_cli.App(name="workspace-tools", version="0.1.0")
```

`App.name` is the canonical Click command and program name. It controls usage,
help, version output, runtime identity, and the default invocation label. Do not
pass a conflicting name to `@app.command(...)`; change `App(name=...)` instead.

Register the command function explicitly:

```python
Expand All @@ -170,10 +174,19 @@ For small scripts, the module-level decorators are available:
@base_cli.command()
def main(ctx: base_cli.Context) -> None:
...


if __name__ == "__main__":
raise SystemExit(base_cli.run_app(main))
```

Prefer an explicit `App` when command names, versions, or consumer
policies should be visible at the top of the module.
The decorator returns the original function. `base_cli.get_command_app(main)`
retrieves its private owning `App` when an embedding layer needs the command
object. Every module-level registration gets an independent app; there is no
process-global command registry. Its default name is inferred from the function;
pass a public name such as `@base_cli.command("workspace-tools")` when needed.
Prefer an explicit `App` when versions or consumer policies should be visible at
the top of the module.

Use `@app.subcommand()` when one CLI needs multiple verbs while keeping the
standard context, logging, redaction, and cleanup lifecycle for each invocation:
Expand Down Expand Up @@ -208,6 +221,15 @@ compatibility. Use either `@app.command()` for a single-command CLI or
`@app.subcommand()` for a command group; do not mix the two registration styles
on one `App`.

Finish all command and subcommand registration before the first access to
`app.click_command`, direct app invocation, `run_app()`, or
`base_cli.testing.invoke()`. Successful materialization freezes registration;
late mutations and duplicate effective command names fail deterministically.
Inferred names are stable across supported Click releases: underscores become
hyphens and conventional `_command`, `_cmd`, `_group`, and `_grp` suffixes are
removed. Pass an explicit subcommand name when a different public spelling is
required.

## Options And Arguments

`base_cli.option` and `base_cli.argument` mirror Click's decorators:
Expand Down
11 changes: 10 additions & 1 deletion lib/python/base_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ def _resolve_version() -> str:
__version__ = _resolve_version()

from . import command_filters, command_protocol, history, testing
from .app import App, argument, command, delegated_display_command, option, run_app
from .app import (
App,
argument,
command,
delegated_display_command,
get_command_app,
option,
run_app,
)
from .command_filters import CommandFilterNormalizer, command_matches, normalize_command_filter, normalize_command_filters
from .command_protocol import (
BOOLEAN,
Expand Down Expand Up @@ -86,6 +94,7 @@ def _resolve_version() -> str:
"command",
"configure_logger",
"delegated_display_command",
"get_command_app",
"get_current_context",
"log_critical",
"log_debug",
Expand Down
Loading