Package: trio-typing
trio-typing: static typing for Trio and related projectsThis repository provides:PEP 561 typing stubs packages for the Trio project packages:trio(trio-stubs)outcome(outcome-stubs)async_generator(async_generator-stubs)A packagetrio_typingcontaining types that Trio programs often want
to refer to (AsyncGenerator[Y, S]andTaskStatus[T])
(Nurseryis exported publicly by mainline Trio as of version 0.12.0.)A mypy plugin that smooths over some limitations in the basic type hints.Supported platformsLike Trio, we require Python 3.7 or later. Both PyPy and CPython are
supported at runtime, but type checking must occur on CPython (due to
limitations of mypy).  We test on Linux using the latest releases
from the 3.7, 3.8, 3.9, and 3.10 branches. We’re
not knowingly doing anything OS-specific, so other OSes should work
too.Type checkers other than mypy are not supported, but might work.
Experience reports and patches to add support are welcome.Quickstartruntime onlyInstall with:pip install trio-typingThen import some typing names fromtrio_typing, likeTaskStatus;
see below for more details.with mypy supportInstall trio-typing with mypy extras:pip install trio-typing[mypy]Note that due to recent plugin API changes, trio-typing 0.10.0+ requires mypy 1.0+.Enable the plugin in yourmypy.ini:[mypy]
plugins = trio_typing.pluginStart running mypy on your Trio code!NOTE:trio-typingis the correct dependency to list in the requirementsof your own library using thetrio_typingmodule.  Don’t usetrio-typing[mypy], since that would needlessly add a mypy dependency to
every app having a transitive dependency on your library.What’s in the box?The stubs packages provide types for all public non-deprecated APIs oftrio,outcome, andasync_generator, as of the release date
of the correspondingtrio-typingdistribution. You don’t need to
explicitly configure these; just sayimport trio(for example)
and mypy will know to look intrio-stubsfor the type information.Thetrio_typingpackage provides:TaskStatus[T], the type of the object passed as thetask_statusargument to a task started withnursery.start(). The type parameterTis the type of the value the task provides to be returned fromnursery.start(). This is implemented as an ABC, and the actual
private types inside Trio are registered as virtual subclasses
of it. So, you can’t instantiatetrio_typing.TaskStatus, butisinstance(task_status, trio_typing.TaskStatus)wheretask_statusis a Trio task status object does return True.(Previous versions oftrio_typingprovided an analogous ABC forNursery, but the actual class is available astrio.Nurseryas of
Trio 0.12.0; you should use that instead.)A backport oftyping.AsyncGenerator[YieldT, SendT]to Python 3.5.
(YieldTis the type of values yielded by the generator, andSendTis the type of values it accepts as an argument toasend().)
This is an abstract class describing the async generator interface:AsyncIteratorplusasend,athrow,aclose, and theag_*introspection attributes. On 3.6+,trio_typing.AsyncGeneratoris just a reexport oftyping.AsyncGenerator.CompatAsyncGenerator[YieldT, SendT, ReturnT],
a name for the otherwise-anonymous concrete async generator type
returned by@async_generatorfunctions. It is a subtype ofAsyncGenerator[YieldT, SendT]and provides the same methods.
(Native async generators don’t have aReturnT; it is only relevant
in determining the return type ofawait async_generator.yield_from_().)A few types that are only useful with the mypy plugin:YieldType[T],SendType[T], and the decorator@takes_callable_and_args.Thetrio_typing.pluginmypy plugin provides:Inference of more specifictrio.open_file()andtrio.Path.open()return types based on constantmodeandbufferingarguments, soawaittrio.open_file("foo","rb", 0)returns an unbuffered async
file object in binary mode andawaittrio.open_file("bar")returns
an async file object in text modeBoilerplate reduction for functions that take parameters(fn, *args)and ultimately invokefn(*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 herestart_soon(async_fn, *args)will raise an error ifasync_fn(*args)would do so. You can also make the callable take some non-splatted
arguments; the*argsget inserted at whatever position in the
argument list you writeVarArg().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 hereWithout the plugin, this type-checks fine (and allows inference ofT), since any callable will match theCallable[...,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_generatorfunctions.
You write the decorated function as if it returned a union of its actual
return type, its yield type wrapped inYieldType[], and its send
type wrapped inSendType[]: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 toyield_andyield_from_inside an@async_generatorfunction are type-checked based on these declarations. If you leave
offeitherthe yield type or send type, the missing one is assumed
to beNone; if you leave offboth(writing justasync def sleep_and_sqrt()->None:, like you would if you weren’t
using the plugin), they’re both assumed to beAny.Note the explicitreturn None; mypy won’t acceptreturnor
falling off the end of the function, unless you run it with--no-warn-no-return.LimitationsCalls to variadic Trio functions liketrio.run(),nursery.start_soon(), and so on, only can type-check up to five
positional arguments. (This number could be increased easily, but
only at the cost of slower typechecking for everyone; mypy’s current
architecture requires that we generate overload sets initially for
every arity we want to be able to use.) You can work around this with
a# type: ignorecomment.outcome.capture()andoutcome.acapture()currently don’t typecheck
their arguments at all.Running the teststrio-typingcomes with a fairly extensive testsuite; it doesn’t test all
the mechanical parts of the stubs, but does exercise most of the interesting
plugin behavior. You can run it after installing, with:pytest -p trio_typing._tests.datadriven --pyargs trio_typingLicenseYour choice of MIT or Apache 2.0.