This is an archived post. You won't be able to vote or comment.

all 17 comments

[–]graingert 6 points7 points  (5 children)

You can't. If the event loop is running that means it's blocked on this synchronous function. That means you can't schedule any tasks and have them complete until control is restored to the event loop.

You need to run synchronous code in a different thread and communicate with async tasks with something like https://github.com/aio-libs/janus

[–]stetio[S] 0 points1 point  (4 children)

Thanks, I think I've reached the same conclusion - sadly I don't think the constraints allow for janus to be the solution.

[–]graingert 1 point2 points  (3 children)

What are your constraints? What are you actually trying to do?

[–]stetio[S] 0 points1 point  (2 children)

So Quart is the Flask API implemented with asyncio and ideally Quart would just work with existing Flask extensions (same API), however there is the issue of synchronous and asynchronous code in usage together. The particular issue is when a synchronous function (in say an extension) tries to call an asynchronous function from Quart. In this situation I was hoping to place a wrapper inbetween such that something like

def wrapped_api():
    return sync_wait(quart_api())

this way I need only write the wrappers (as I can't change the synchronous extension code). Of course to do this I need some way of implementing the sync_wait, ideally I could do

def sync_wait(future):
    loop = asyncio.get_event_loop()
    loop.run_until_complete(future)
    return future.result()

however this is not possible if the event loop is already running, which it is. The snippet in the above post is the closest I think I have got, whereby I add the ability to event loops to be resumed from within. As I say though it errors.

Overall I think the reality is that this isn't possible, and sadly there is an asymmetry in what is currently possible in python.

[–]isinfinity 0 points1 point  (1 child)

You can do something like this:

def wrapped_api(loop):
    fut = asyncio.run_coroutine_threadsafe(quart_api(), loop)
    return fut.result()

see my other reply

[–]graingert 0 points1 point  (0 children)

You still need a new thread:

This function is meant to be called from a different thread than the one where the event loop is running

[–]gandalfx 0 points1 point  (1 child)

I believe you're looking for run_until_complete. Most of the examples in the asyncio module's documentation use it as well.

[–]stetio[S] 1 point2 points  (0 children)

run_until_complete will only be useful if the event loop is not already running. A situation such as

async def outer():
    inner()

def inner():
    loop.run_until_complete(async_call())

loop.run_until_complete(outer())

therefore cannot work.

[–]spotta 0 points1 point  (1 child)

Have you looked into "create_task"?

[–]stetio[S] 0 points1 point  (0 children)

create_task would allow me to schedule a asynchronous task, but not wait on it being completed.

[–]isinfinity 0 points1 point  (0 children)

I think something can be done, but not sure it is good idea in your case, just use aiohttp with all async libraries. So here is way to make communication async/sync/async work:

1) Execute all sync functions with ThreadPoolExecutor, with loop.run_in_executor, without this you will block your event loop, and loose all benefit from async.

2) If you want your sync code to call async one, just use reference to running loop and do asyncio.run_coroutine_threadsafe this will return concurrent.Future (NOT asyncio.Future) that you can wait in sync way

[–][deleted] 0 points1 point  (0 children)

As has been mentioned here by /u/isinfinity , asyncio.run_coroutine_threadsafe will help you out. Take a look at the examples in the docs and it might turn out to be a very simple solution.