API reference
Creating a portal
In order to use greenback in a particular async task, you must first create
a greenback portal for that task to use. You may choose between:
ensure_portal(): Create a portal to be used by the current task, which lasts for the lifetime of that task. Use case: minimally invasive code change to allowgreenback.await_()in a particular task.bestow_portal(): Create a portal to be used by some other specified task, which lasts for the lifetime of that task. Use case: enabling greenback in a task without that task’s cooperation, which may be useful in some debugging and instrumentation situations. (with_portal_run_tree()is implemented using a Trio instrument that callsbestow_portal()on certain newly spawned tasks.)with_portal_run(): Run an async function (in the current task) that might eventually make calls toawait_(), with a portal available for at least the duration of that call. Use case: less “magical” thanensure_portal(); keeps the portal (and its perforamnce impact) scoped to just the portion of a task that needs it.with_portal_run_sync(): Run a synchronous function (in the current task) that might eventually make calls toawait_(), with a portal available for at least the duration of that call. Use case: same aswith_portal_run(), but the implementation is simpler and will be a bit faster (probably only noticeable if the function you’re running is very short).On Trio, the functions in the next section allow you to automatically propagate the greenback portal to child tasks of a certain function or scope. (These don’t work on asyncio, since asyncio lacks a clear task tree and also lacks the instrumentation features required to implement them.) Use case: minimally invasive code change to allow
greenback.await_()in an entire subsystem of your Trio program.
You can use has_portal() to determine whether a portal has already
been set up.
- await greenback.ensure_portal()
Ensure that the current async task is able to use
greenback.await_().If the current task has called
ensure_portal()previously, calling it again is a no-op. Otherwise,ensure_portal()interposes a “coroutine shim” provided bygreenbackin between the event loop and the coroutine being used to run the task. For example, when running under Trio,trio.lowlevel.Task.corois replaced with a wrapper around the coroutine it previously referred to. (The same thing happens under asyncio, but asyncio doesn’t expose the coroutine field publicly, so some additional trickery is required in that case.)After installation of the coroutine shim, each task step passes through
greenbackon its way into and out of your code. At some performance cost, this effectively provides a portal that allows later calls togreenback.await_()in the same task to access an async environment, even if the function that callsawait_()is a synchronous function.This function is a cancellation point and a schedule point (a checkpoint, in Trio terms) even if the calling task already had a portal set up.
- greenback.bestow_portal(task)
Ensure that the given async task is able to use
greenback.await_().This works like calling
ensure_portal()from within task, with one exception: if you pass the currently running task, then the portal will not become usable until after the task yields control to the event loop.
- await greenback.with_portal_run(async_fn, *args, **kwds)
Execute
await async_fn(*args, **kwds)in a context that is able to usegreenback.await_().If the current task already has a greenback portal set up via a call to one of the other
greenback.*_portal()functions, thenwith_portal_run()simply calls async_fn. If async_fn usesgreenback.await_(), the existing portal will take care of it.Otherwise (if there is no portal already available to the current task),
with_portal_run()creates a new portal which lasts only for the duration of the call to async_fn. If async_fn then callsensure_portal(), an additional portal will not be created: the task will still have just the portal installed bywith_portal_run(), which will be removed when async_fn returns.This function does not add any cancellation point or schedule point beyond those that already exist inside async_fn.
- await greenback.with_portal_run_sync(sync_fn, *args, **kwds)
Execute
sync_fn(*args, **kwds)in a context that is able to usegreenback.await_().If the current task already has a greenback portal set up via a call to one of the other
greenback.*_portal()functions, thenwith_portal_run()simply calls sync_fn. If sync_fn usesgreenback.await_(), the existing portal will take care of it.Otherwise (if there is no portal already available to the current task),
with_portal_run_sync()creates a new portal which lasts only for the duration of the call to sync_fn.This function does not add any cancellation point or schedule point beyond those that already exist due to any
await_()s inside sync_fn.
- greenback.has_portal(task=None)
Return true if the given task is currently able to use
greenback.await_(), false otherwise. If no task is specified, query the currently executing task.
Propagating portals to a task’s children
The functions in this section create inheritable greenback portals, impacting both a task and its children. (“Children” refers to child tasks of nurseries are contained entirely within the portal’s scope, since it is only those tasks that are guaranteed not to outlive the portal.)
The automatic “portalization” of child tasks is implemented using a
Trio instrument (the
AutoPortalInstrument), which has a small performance impact
on task spawning for the entire Trio run. To minimize this impact, a
single instrument is used even if you have multiple inheriting-portal
contexts active simultaneously, and the instrument will be removed as
soon as all of them have completed.
- await greenback.with_portal_run_tree(async_fn, *args, **kwds)
Execute
await async_fn(*args, **kwds)in a context that allows use ofgreenback.await_()both in async_fn itself and in any tasks that are spawned into child nurseries of async_fn, recursively.You can use this to create an entire Trio run (except system tasks) that runs with
greenback.await_()available: saytrio.run(with_portal_run_tree, main). (If you want to wrap the system tasks too, pass anAutoPortalInstrumenttotrio.run()instead.)This function does not add any cancellation point or schedule point beyond those that already exist inside async_fn.
Availability: Trio only.
- async with greenback.portals_for_tree()
Return an asynchronous context manager that enables
greenback.await_()within its scope, for both the task in which the context manager was entered and any child tasks of nurseries created while the context manager is active.Both the entry and the exit of the context manager are schedule points (other tasks can run), but neither is a cancellation point (this task cannot be cancelled at them). If you don’t even want a schedule point, then use
with_portal_run_tree()instead.Availability: Trio only.
- with greenback.portals_for_children()
Return a synchronous context manager that enables
greenback.await_()for all child tasks of nurseries created within its scope. The task in which you enter the context manager does not receive a portal, which is what allows the context manager to be synchronous.This is a building block for
portals_for_children()andwith_portal_run_tree(). You probably want to use one of those instead.Availability: Trio only.
- class greenback.AutoPortalInstrument
A Trio
Instrumentthat automatically gives newly spawned tasks a greenback portal where appropriate. This is used in the implementations ofwith_portal_run_tree()andportals_for_tree(). You can also pass an instance of this class directly as an instrument totrio.run()if you want to give every task in the run (including system tasks) a portal.
Using the portal
Once you’ve set up a portal using any of the above functions, you can use it
to run async functions by making calls to greenback.await_():
- greenback.await_(awaitable)
Run an async function or await an awaitable from a synchronous function, using the portal set up for the current async task by
ensure_portal(),bestow_portal(),with_portal_run(), orwith_portal_run_sync().greenback.await_(foo())is equivalent toawait foo(), except that thegreenbackversion can be written in a synchronous function while the native version cannot.
Additional utilities
greenback comes with a few tools (built atop await_()) which may
be helpful when adapting async code to work with synchronous interfaces.
- @greenback.autoawait
Decorator for an async function which allows (and requires) it to be called from synchronous contexts without
await.For example, this can be used for magic methods, property setters, and so on.
- @greenback.decorate_as_sync(decorator: Callable[[F], F]) Callable[[AF], AF]
- @greenback.decorate_as_sync(decorator: Callable[[...], Any]) Callable[[Callable[[...], Awaitable[Any]]], Callable[[...], Awaitable[Any]]]
Wrap the synchronous function decorator decorator so that it can be used to decorate an async function.
This can be used, for example, to apply an async-naive decorator such as
@functools.lru_cache()to an async function:@greenback.decorate_as_sync(functools.lru_cache(maxsize=128)) async def some_fn(...): ...
Without the wrapping in
decorate_as_sync(), the LRU cache would treat the inner function as a synchronous function, and would therefore unhelpfully cache the coroutine object that is returned when an async function is called withoutawait.Internally, the “inner” async function is wrapped in a synchronous function that invokes that async function using
greenback.await_(). This synchronous function is then decorated with the decorator.decorate_as_sync()returns an “outer” async function which invokes the internal decorated synchronous function usinggreenback.with_portal_run_sync().In other words, the following two calls behave identically:
result = await greenback.decorate_as_sync(decorator)(async_fn)(*args, **kwds) result = await greenback.with_portal_run_sync( decorator(greenback.autoawait(async_fn)), *args, **kwds, )
- with greenback.async_context(async_cm)
Wraps an async context manager so it is usable in a synchronous
withstatement. That is,with async_context(foo) as bar:behaves equivantly toasync with foo as bar:as long as a portal has been created somewhere up the callstack.
- for ... in greenback.async_iter(async_iterable)
Wraps an async iterable so it is usable in a synchronous
forloop,yield fromstatement, or similar synchronous iteration context. That is,for elem in async_iter(foo):behaves equivantly toasync for elem in foo:as long as a portal has been created somewhere up the callstack.If the obtained async iterator implements the full async generator protocol (
asend(),athrow(), andaclose()methods), then the returned synchronous iterator implements the corresponding methodssend(),throw(), andclose(). This allows for better interoperation withyield from, for example.