Typer with first-class async support: unified sync/async commands, callbacks, and lifecycle event handlers.
Project description
async-typer
Typer with first-class async support:
async def commands and callbacks work alongside regular sync ones via the
same @app.command() decorator — no second API to remember — plus lifecycle
event handlers for setting up and tearing down async resources.
Features
- One decorator, sync or async —
@app.command()and@app.callback()accept both regular andasync deffunctions. The wrapper is transparent; Typer's--help, option parsing, and type conversion all work as normal. - Shared event loop across the command lifecycle — startup handlers,
the command body, and shutdown handlers all run on the same
asyncio.Runner, so async resources created on startup (connection pools, HTTP sessions, etc.) remain usable by the command and by shutdown. - Fully typed — ships with a
py.typedmarker and strict type hints. - Drop-in replacement — re-exports Typer's public API, so
from async_typer import Option, Argument, echo, ...works without a second import line.
Installation
pip install async-typer
# or
uv add async-typer
Requires Python 3.11+.
Quick start
from async_typer import AsyncTyper
app = AsyncTyper()
@app.command()
def sync_hello(name: str = "world") -> None:
print(f"hi {name}")
@app.command()
async def async_hello(name: str = "world") -> None:
# await anything you need here
print(f"hello {name}")
if __name__ == "__main__":
app()
Async callbacks
@app.callback()
async def main(verbose: bool = False) -> None:
if verbose:
print("verbose mode")
Lifecycle event handlers
Register startup and shutdown hooks, sync or async. They run on the
same event loop as the command body, so shared async resources stay alive
across the whole invocation:
import httpx
app = AsyncTyper()
state: dict[str, httpx.AsyncClient] = {}
async def open_client() -> None:
state["client"] = httpx.AsyncClient()
async def close_client() -> None:
await state["client"].aclose()
app.add_event_handler("startup", open_client)
app.add_event_handler("shutdown", close_client)
@app.command()
async def fetch(url: str) -> None:
response = await state["client"].get(url)
print(response.status_code)
The shutdown handler runs even if the command raises — use it to release resources unconditionally.
Migrating from 0.1.x
The separate async_command / async_callback decorators still work but
emit DeprecationWarning. Replace them with the unified command /
callback, which auto-detect async def:
# before
@app.async_command()
async def foo(): ...
# after
@app.command()
async def foo(): ...
Development
This repo uses uv,
ruff, and
ty.
uv sync --dev
uv run pytest
uv run ruff check .
uv run ty check
License
MIT — see LICENSE.txt.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file async_typer-0.2.0.tar.gz.
File metadata
- Download URL: async_typer-0.2.0.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a04aae9bcff6b7da3fe4ae7c07e09e1a0aa39ad338e8ee93bd72cedf3c3f2754
|
|
| MD5 |
fe4c6812719efc1a98a1d66900422863
|
|
| BLAKE2b-256 |
1b6cffab26e1173bea9ee2178833fb7e639e07c5a3d52f695fc086da19944e67
|
File details
Details for the file async_typer-0.2.0-py3-none-any.whl.
File metadata
- Download URL: async_typer-0.2.0-py3-none-any.whl
- Upload date:
- Size: 7.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c9f490336206520715180567eb8d26b8050969002bc9a339b9948f023c2e8bd5
|
|
| MD5 |
aaef03ebb7fecefa4d9601c4f2ebebcb
|
|
| BLAKE2b-256 |
943544b843f038b61596223cb89295e5edd8dad29797e59947c54fcee585a534
|