diff --git a/README.md b/README.md index 6190f74..dbaff40 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ lifecycle: ```python from __future__ import annotations +from typing import Any + import base_cli @@ -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 @@ -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()`, @@ -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 @@ -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}") @@ -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: ... ``` @@ -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: ... @@ -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", @@ -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") ``` @@ -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 @@ -506,10 +520,12 @@ 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: ... ``` @@ -517,8 +533,10 @@ 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: ... ``` @@ -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: ... ``` @@ -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") ``` diff --git a/docs/integrations.md b/docs/integrations.md index ed43052..0d43124 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -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",