Async support¶
Engines¶
Typer supports asyncio out of the box. Trio is supported through
anyio, which can be installed as optional dependency:
$ pip install typer[anyio]
---> 100%
Successfully installed typer anyio
Default engine selection¶
| none | anyio | trio | anyio + trio |
|---|---|---|---|
| asyncio | asyncio via anyio | asyncio* | trio via anyio |
* If you don't want to install anyio when using trio, provide your own async_runner function
Using with run()¶
Async functions can be run just like normal functions:
import asyncio
import typer
app = typer.Typer()
async def main():
await asyncio.sleep(1)
typer.echo("Hello World")
if __name__ == "__main__":
typer.run(main)
Or using anyio:
import anyio
import typer
app = typer.Typer()
async def main():
await anyio.sleep(1)
typer.echo("Hello World")
if __name__ == "__main__":
typer.run(main)
Using with commands¶
Async functions can be registered as commands explicitely just like synchronous functions:
import asyncio
import typer
app = typer.Typer(async_runner=asyncio.run)
@app.command()
async def wait(seconds: int):
await asyncio.sleep(seconds)
typer.echo(f"Waited for {seconds} seconds")
if __name__ == "__main__":
app()
Or using anyio:
import anyio
import typer
app = typer.Typer()
@app.command()
async def wait(seconds: int):
await anyio.sleep(seconds)
typer.echo(f"Waited for {seconds} seconds")
if __name__ == "__main__":
app()
Or using trio via anyio:
import trio
import typer
app = typer.Typer()
@app.command()
async def wait(seconds: int):
await trio.sleep(seconds)
typer.echo(f"Waited for {seconds} seconds")
if __name__ == "__main__":
app()
Customizing async engine¶
You can customize the async engine by providing an additional parameter async_runner to the Typer instance or to the command decorator.
When both are provided, the one from the decorator will take precedence over the one from the Typer instance.
Customize a single command:
import asyncio
import trio
import typer
app = typer.Typer()
@app.command()
async def wait_trio(seconds: int):
await trio.sleep(seconds)
typer.echo(f"Waited for {seconds} seconds using trio (default)")
@app.command(async_runner=asyncio.run)
async def wait_asyncio(seconds: int):
await asyncio.sleep(seconds)
typer.echo(f"Waited for {seconds} seconds using asyncio (custom runner)")
if __name__ == "__main__":
app()
Customize the default engine for the Typer instance:
import asyncio
import anyio
import typer
app = typer.Typer(async_runner=lambda c: anyio.run(lambda: c, backend="asyncio"))
@app.command()
async def wait_anyio(seconds: int):
await anyio.sleep(seconds)
typer.echo(f"Waited for {seconds} seconds using asyncio via anyio")
@app.command()
async def wait_asyncio(seconds: int):
await asyncio.sleep(seconds)
typer.echo(f"Waited for {seconds} seconds using asyncio")
if __name__ == "__main__":
app()
Using with callback¶
The callback function supports asynchronous functions with the async_runner parameter as well:
import asyncio
import trio
import typer
app = typer.Typer()
@app.command()
async def wait_trio(seconds: int):
await trio.sleep(seconds)
typer.echo(f"Waited for {seconds} seconds using trio (default)")
@app.callback(async_runner=lambda c: asyncio.run(c))
async def wait_asyncio(seconds: int):
await asyncio.sleep(seconds)
typer.echo(
f"Waited for {seconds} seconds before running command using asyncio (customized)"
)
if __name__ == "__main__":
app()
Because the asynchronous functions are wrapped in a synchronous context before being executed, it is possible to mix async engines between the callback and commands.