Home > database >  Running trivial async code from sync in Python
Running trivial async code from sync in Python

Time:10-02

I'm writing some parser code to be used with async IO functions (using Trio). The parser gets passed an object that exports an async read() method, and calls that method in the course of parsing.

Ordinarily, this code will be run using data straight off the network, and use Trio's network functions. For this purpose, Trio is obviously required. However, I'd also like to be able to call the parser with a full message already in hand. In this case, the network code can be effectively replaced by a trivial async reimplementation of BytesIO or similar.

Because it awaits the implementation's async functions, the parser code must therefore also be async. Is there an easy way to run this async code from a sync function, without running a full event loop, in the case where the read() method is guaranteed never to block?

E.g.

async def parse(reader):
    d = await reader.read(2)
    # parse data
    d2 = await reader.read(4)
    # more parsing
    return parsed_obj

Can you create an object with a never-blocking async read() method, then easily call parse() from sync code, without using an event loop?

CodePudding user response:

Sure you can.

>>> async def x():
...     return 42
... 
>>> v=x()
>>> v.send(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: 42
>>> 

Thus,

>>> def sync_call(p, *a, **kw):
...     try:
...             p(*a, **kw).send(None)
...     except StopIteration as exc:
...             return exc.value
...     raise RuntimeError("Async function yielded")
... 
>>> sync_call(x)
42
>>> 

This does work across function calls, assuming that nothing in your call chain yields to a (non-existing) main loop:

>>> async def y(f):
...     return (await f())/2
... 
>>> sync_call(y,x)
21.0
  • Related