Boilerplate reduction for functions that take parameters (fn, *args)
and ultimately invoke fn(*args): just write:
from mypy_extensions import VarArg
@trio_typing.takes_callable_and_args
def start_soon(
async_fn: Callable[[VarArg()], Awaitable[T]],
*args: Any,
other_keywords: str = are_ok_too,
):
# your implementation here
start_soon(async_fn, *args) will raise an error if async_fn(*args)
would do so. You can also make the callable take some non-splatted
arguments; the *args get inserted at whatever position in the
argument list you write VarArg().
The above example will always fail when the plugin is not being
used. If you want to always pass in such cases, you can use a union:
@trio_typing.takes_callable_and_args
def start_soon(
async_fn: Union[
Callable[..., Awaitable[T]],
Callable[[VarArg()], Awaitable[T]],
],
*args: Any,
other_keywords: str = are_ok_too,
):
# your implementation here
Without the plugin, this type-checks fine (and allows inference of
T), since any callable will match the Callable[..., Awaitable[T]] option. With the plugin, the entire union will be
replaced with specific argument types.
Note: due to mypy limitations, we only support a maximum of 5
positional arguments, and keyword arguments can’t be passed in this way;
nursery.start_soon(functools.partial(...)) will pass the type checker
but won’t be able to actually check the argument types.
Mostly-full support for type checking @async_generator functions.
You write the decorated function as if it returned a union of its actual
return type, its yield type wrapped in YieldType[], and its send
type wrapped in SendType[]:
from trio_typing import YieldType, SendType
@async_generator
async def sleep_and_sqrt() -> Union[None, SendType[int], YieldType[float]]:
next_yield = 0.0
while True:
amount = await yield_(next_yield) # amount is an int
if amount < 0:
return None
await trio.sleep(amount)
next_yield = math.sqrt(amount)
# prints: CompatAsyncGenerator[float, int, None]
reveal_type(sleep_and_sqrt())
Calls to yield_ and yield_from_ inside an @async_generator
function are type-checked based on these declarations. If you leave
off either the yield type or send type, the missing one is assumed
to be None; if you leave off both (writing just
async def sleep_and_sqrt() -> None:, like you would if you weren’t
using the plugin), they’re both assumed to be Any.
Note the explicit return None; mypy won’t accept return or
falling off the end of the function, unless you run it with
--no-warn-no-return.