Skip to content
Open
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
50 changes: 36 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ lifecycle:
```python
from __future__ import annotations

from typing import Any

import base_cli


Expand All @@ -54,7 +56,7 @@ app = base_cli.App(name="hello", version="0.1.0")

@app.command()
@base_cli.option("--name", default="world", show_default=True)
def hello(ctx: base_cli.Context, name: str) -> int:
def hello(ctx: base_cli.Context[Any, Any, Any], name: str) -> int:
ctx.log.info("greeting %s", name)
print(f"Hello, {name}!")
return base_cli.ExitCode.SUCCESS
Expand Down Expand Up @@ -179,8 +181,10 @@ from `base_cli`. `RuntimeBinding.layout` uses the public immutable
service payloads owned by a consumer:

```python
Config = dict[str, object]
context: base_cli.Context[Config, ApplicationState, Services]
from typing import Any

Config = dict[str, Any]
context: base_cli.Context[Config, ApplicationContext, Services]
```

`App.command()`, `App.subcommand()`, `@base_cli.command()`, `@base_cli.option()`,
Expand Down Expand Up @@ -266,6 +270,8 @@ guide](https://basefoundry.github.io/base-cli/adopter-readiness/) and run the th
```python
from __future__ import annotations

from typing import Any

import base_cli


Expand All @@ -278,7 +284,7 @@ app = base_cli.App(

@app.command()
@base_cli.option("--name", required=True)
def main(ctx: base_cli.Context, name: str) -> None:
def main(ctx: base_cli.Context[Any, Any, Any], name: str) -> None:
ctx.log.info("starting hello")
print(f"hello {name}")

Expand Down Expand Up @@ -321,8 +327,10 @@ name to `@app.command(...)`; change `App(name=...)` instead.
Register the command function explicitly:

```python
from typing import Any

@app.command()
def main(ctx: base_cli.Context) -> None:
def main(ctx: base_cli.Context[Any, Any, Any]) -> None:
...
```

Expand All @@ -333,8 +341,10 @@ removed from Click's keyword arguments.
For small scripts, the module-level decorators are available:

```python
from typing import Any

@base_cli.command()
def main(ctx: base_cli.Context) -> None:
def main(ctx: base_cli.Context[Any, Any, Any]) -> None:
...


Expand All @@ -354,6 +364,8 @@ Use `@app.subcommand()` when one CLI needs multiple verbs while keeping the
standard context, logging, redaction, and cleanup lifecycle for each invocation:

```python
from typing import Any

app = base_cli.App(
name="workspace-tools",
version="0.1.0",
Expand All @@ -363,13 +375,13 @@ app = base_cli.App(

@app.subcommand()
@base_cli.argument("project")
def status(ctx: base_cli.Context, project: str) -> None:
def status(ctx: base_cli.Context[Any, Any, Any], project: str) -> None:
ctx.log.info("checking %s", project)


@app.subcommand("sync")
@base_cli.option("--dry-run", is_flag=True)
def sync_project(ctx: base_cli.Context, dry_run: bool) -> None:
def sync_project(ctx: base_cli.Context[Any, Any, Any], dry_run: bool) -> None:
if ctx.dry_run:
ctx.log.info("previewing sync")
```
Expand Down Expand Up @@ -465,11 +477,13 @@ root parameters and before any existing group, command, or result callback
runs:

```python
def make_application_context(ctx: base_cli.Context) -> ApplicationContext:
def make_application_context(
ctx: base_cli.Context[Config, ApplicationContext, Services],
) -> ApplicationContext:
return ApplicationContext(environment=ctx.environment)


def make_services(ctx: base_cli.Context) -> Services:
def make_services(ctx: base_cli.Context[Config, ApplicationContext, Services]) -> Services:
services = Services(ctx.config)
ctx.on_cleanup(services.close)
return services
Expand Down Expand Up @@ -506,19 +520,23 @@ boundaries.
`base_cli.option` and `base_cli.argument` mirror Click's decorators:

```python
from typing import Any

@app.command()
@base_cli.argument("project")
@base_cli.option("--workspace", type=str)
def main(ctx: base_cli.Context, project: str, workspace: str | None) -> None:
def main(ctx: base_cli.Context[Any, Any, Any], project: str, workspace: str | None) -> None:
...
```

Use `sensitive=True` for options or arguments whose values must not reach
invocation logs or history writers:

```python
from typing import Any

@base_cli.option("--token", sensitive=True, required=True)
def main(ctx: base_cli.Context, token: str) -> None:
def main(ctx: base_cli.Context[Any, Any, Any], token: str) -> None:
...
```

Expand All @@ -528,9 +546,11 @@ values are redacted. Sensitive positional arguments are redacted according to
the Click command schema:

```python
from typing import Any

@app.command()
@base_cli.argument("credential", sensitive=True)
def login(ctx: base_cli.Context, credential: str) -> None:
def login(ctx: base_cli.Context[Any, Any, Any], credential: str) -> None:
...
```

Expand All @@ -544,8 +564,10 @@ For native `App` commands, use `dry_run=True` when a nonstandard option should
drive `ctx.dry_run` and the lifecycle's default durable-write suppression:

```python
from typing import Any

@base_cli.option("--preview", is_flag=True, dry_run=True)
def main(ctx: base_cli.Context, preview: bool) -> None:
def main(ctx: base_cli.Context[Any, Any, Any], preview: bool) -> None:
if ctx.dry_run:
ctx.log.info("previewing changes")
```
Expand Down
4 changes: 3 additions & 1 deletion docs/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ Pass `rich=True` when constructing an app and pass the active context's flag to
the shared record renderer:

```python
from typing import Any

import base_cli

app = base_cli.App(name="catalog", rich=True)

@app.command()
def list_items(ctx: base_cli.Context) -> None:
def list_items(ctx: base_cli.Context[Any, Any, Any]) -> None:
base_cli.render_records(
({"name": "base", "path": "/work/base"},),
requested_format="text",
Expand Down
Loading