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. A test asserts the re-export list matches the targeted typer release exactly, so it cannot silently drift.
Installation
pip install async-typer
# or
uv add async-typer
Requires Python 3.11+.
Versioning
async-typer's version is the typer version it targets. async-typer 0.27.2
is built and tested against typer 0.27.2, and its dependency range is pinned
to that minor series (typer>=0.27.2,<0.28.0). To find the right release, look
up the typer version you are on:
| typer | async-typer |
|---|---|
| 0.27.x | 0.27.x |
When async-typer needs a release of its own without a matching typer release, a
fourth segment is appended — 0.27.2.1, 0.27.2.2 — which still targets
typer 0.27.2 and sorts between 0.27.2 and 0.27.3.
Releases before 0.27.2 (0.1.x, 0.2.x) predate this policy and carry a
wide typer>=0.9.0,<1.0.0 range instead.
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.2.x
0.27.2 is not a huge leap in scope — it is the first release under the
version policy above. Two things changed alongside it:
- typer is now pinned to the
0.27.xseries. TyperException, added in typer0.27.2, is now re-exported.- Six symbols typer dropped in
0.26.0are no longer re-exported:clear,echo_via_pager,edit,open_file,pause, andunstyle. They are click helpers, and typer no longer depends on click — addclickto your own dependencies and import them from there.
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.
Metadata
Release files for async-typer 0.27.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| async_typer-0.27.2.tar.gz | 15.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| async_typer-0.27.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.0 kB
Release files / async_typer-0.27.2.tar.gz
| Download URL | async_typer-0.27.2.tar.gz |
|---|---|
| Size | 15.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
18425d2e7e5ad626013afd414c0f37081c0ff493eb3685567d8ed94764aa3b1b
|
|
BLAKE2b-256 checksum How to use checksums |
a0b4376bca99068ec7a8186c02cf6800c19c5c5487010632924dac583533ddcc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / async_typer-0.27.2-py3-none-any.whl
| Download URL | async_typer-0.27.2-py3-none-any.whl |
|---|---|
| Size | 8.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2546ce0a10729fb1ece099cbd81551baa6822495f3dad3900737a77d76ebe41b
|
|
BLAKE2b-256 checksum How to use checksums |
3611fcbff0a648e022fbc5046ee408b72e0a4fc251dd11cc832102e6f27f2a21
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|