I'm working on making a previously sync webserver as sync one.
Most of my functions are sync and I'd like to simply make async calls from existing code to avoid async creep. nest_asyncio appears to allow this by making run_until_complete re-entrant.
However, while this works for 1 reentrant call, I get a deadlock with two:
import asyncio
import functools
import time
from random import random
import nest_asyncio
nest_asyncio.apply()
def sync(corot, loop=None):
"""
Make a synchronous function from an asynchronous one.
:param corot:
:return:
"""
if loop is None:
loop = asyncio.get_event_loop()
result, = loop.run_until_complete(asyncio.gather(corot))
return result
async def sync_to_corountine(func, *args, **kw):
"""
Make a coroutine from a synchronous function.
"""
try:
return func(*args, *kw)
finally:
# every async needs an await.
await asyncio.sleep(0)
def main():
async def background(timeout):
await asyncio.sleep(timeout)
print(f"Background: {timeout}")
loop = asyncio.get_event_loop()
# Run some background work to check we are never blocked
bg_tasks = [
loop.create_task(background(i))
for i in range(10)
]
async def long_running_async_task(result):
# Simulate slow IO
print(f"...START long_running_async_task [{result}]")
await asyncio.sleep(1)
print(f"...END long_running_async_task [{result}]")
return result
def sync_function_with_async_dependency(result):
print(f"...START sync_function_with_async_dependency [{result}]")
result = sync(long_running_async_task(result), loop=loop)
print(f"...END sync_function_with_async_dependency [{result}]")
return result
# Call sync_function_with_async_dependency
# One reentrant task is OK
# Multiple reentrant tasks=>fails to exit
n = 2
for i in range(n):
bg_tasks.append(sync_to_corountine(sync_function_with_async_dependency, i))
# for i in range(n):
# bg_tasks.append(long_running_async_task(i))
# OK
# bg_tasks.append(long_running_async_task(123))
# bg_tasks.append(long_running_async_task(456))
task = asyncio.gather(*bg_tasks) # , loop=loop)
loop.run_until_complete(task)
if __name__ == '__main__':
main()
...START sync_function_with_async_dependency [0]
...START sync_function_with_async_dependency [1]
Background: 0
...START long_running_async_task [0]
...START long_running_async_task [1]
Background: 1
...END long_running_async_task [0]
...END long_running_async_task [1]
...END sync_function_with_async_dependency [1]
Background: 2
Background: 3
Background: 4
... we are missing
...END sync_function_with_async_dependency [0]
Am I using nest_asyncio correctly?
You want to use gevent if you're going to nest async calls inside synchronous calls, otherwise it can lead to starvation issues.
The whole design of asyncio is async routines call async routines, but sync routines can not (except via run). They do this as they feel that anything else will lead to overly complex code that is difficult to follow. Probably bugs in asyncio are harder to fix as well.
The monkey patch nest_async tries to break that design principle, but it doesn't do a very good job of it as nested runs will not give time to tasks scheduled outside the nested run. This can potentially leading to starvation if you spend too much time on code inside the nested run.
I tried to explain this to the author, but he's still struggling:
https://github.com/erdewit/nest_asyncio/issues/36
Related
In short, the problem is that the future returned by asyncio.run_coroutine_threadsafe is blocking when I call future.result()
The problem is also documented in the following question with (currently) no satisfactory answer: Future from asyncio.run_coroutine_threadsafe hangs forever?
What I'm trying to achieve is to call async code from sync code, where the sync code is actually itself wrapped in async code with an existing running event loop (to make things more concrete: it's a Jupyter notebook).
I would want to send async tasks from nested sync code to the existing 'outer' event loop and 'await' its results within the nested sync code. Implied constraint: I do not want to run those tasks on a new event loop (multiple reasons).
Since it's not possible to just 'await' an async result from sync code without blocking and without using asyncio.run which creates a new event loop, I thought using a separate thread would somehow help.
From the documentation description, asyncio.run_coroutine_threadsafe sounds like the perfect candidate.
But it's still blocking...
Bellow full snippet, with a timeout when calling the future's result.
How can I get this code to work correctly?
import asyncio
from concurrent.futures import ThreadPoolExecutor
async def gather_coroutines(*coroutines):
return await asyncio.gather(*coroutines)
def run_th_safe(loop, coroutines):
future = asyncio.run_coroutine_threadsafe(gather_coroutines(*coroutines), loop)
res = future.result(timeout=3) # **** BLOCKING *****
return res
def async2sync(*coroutines):
try:
loop = asyncio.get_running_loop()
except RuntimeError:
return asyncio.run(gather_coroutines(*coroutines))
# BLOW DOESN'T WORK BECAUSE run_th_safe IS BLOCKING
with ThreadPoolExecutor(max_workers=1) as ex:
thread_future = ex.submit(run_th_safe, loop, coroutines)
return thread_future.result()
# Testing
async def some_async_task(n):
"""Some async function to test"""
print('Task running with n =', n)
await asyncio.sleep(n/10)
print('Inside coro', n)
return list(range(n))
async def main_async():
coro3 = some_async_task(30)
coro1 = some_async_task(10)
coro2 = some_async_task(20)
results = async2sync(coro3, coro1, coro2)
return results
def main_sync():
coro3 = some_async_task(30)
coro1 = some_async_task(10)
coro2 = some_async_task(20)
results = async2sync(coro3, coro1, coro2)
return results
if __name__ == '__main__':
# Testing functionnality with asyncio.run()
# This works
print(main_sync())
# Testing functionnality with outer-loop (asyncio.run) and nested asyncio.run_coroutine_threadsafe
# **DOESN'T WORK**
print(asyncio.run(main_async()))
I am using FastApi and have one endpoint.
I have two long running functions which I want to run concurrently using asyncio
Therefore, I have created two functions:
async def get_data_one():
return 'done_one'
async def get_data_two():
return 'done_two'
These functions get data from external webservices.
I want to execute them concurrently so I have created a another function that does it:
async def get_data():
loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)
task_1 = loop.create_task(get_data_one)
task_2 = loop.create_task(get_data_two)
tasks = (task_1, task_2)
first, second = loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
# I will then perform cpu intensive computation on the results
# for now - assume i am just concatenating the results
return first + second
Finally, I have my endpoint:
#app.post("/result")
async def get_result_async():
r = await get_data()
return r
Even this simple example breaks and I get the following exception when I hit the endpoint:
RuntimeError: This event loop is already running
ERROR: _GatheringFuture exception was never retrieved
future: <_GatheringFuture finished exception=AttributeError("'function' object has no attribute 'send'",)>
AttributeError: 'function' object has no attribute 'send'
This is a simplified code but I would really appreciate how to do it the right way.
When in FastAPI context, you never need to run an asyncio loop; it's always running for as long as your server process lives.
Therefore, all you need is
import asyncio
async def get_data_one():
return "done_one"
async def get_data_two():
return "done_two"
async def get_data():
a, b = await asyncio.gather(get_data_one(), get_data_two())
return a + b
##route decorator here...
async def get_result_async():
r = await get_data()
print("Get_data said:", r)
return r
# (this is approximately what is done under the hood,
# presented here to make this a self-contained example)
asyncio.run(get_result_async())
It's as simple as:
async def get_data():
first, second = await asyncio.gather(
get_data_one(),
get_data_two(),
)
return first + second
Basically I have synchronous code something similar to this:
def f(.):
...
def call(.):
..Some sync code..
try:
resp = f(..)
...some more transformations of resp...
return ..
except:
..Some error handling..
async def af(.):
...
Basically I want to dynamically alter the code of call so that it can call and await af function instead of f. Is there a way around this? I have found syncit on github, but it doesn't seem to be a solution to me as you have to rewerite the code to async first and then downgrade it to sync. Any help will be appreciated.
In asyncio world every coroutine can be executied either inside other coroutine (using await) or by event loop blocking call (using run_until_complete()).
You can't await coroutine inside regular function since this function's call would be blocking, while coroutine to be awaited needs blocking event loop execution. This is how asyncio designed to work.
If you ok with idea of your call() function being blocking and have access to f() implementation, you can execute coroutine inside f() runnning event loop:
async def af(.):
...
def f(.):
loop = asyncio.get_event_loop()
return loop.run_until_complete(af())
def call(.):
..Some sync code..
try:
resp = f(..)
...some more transformations of resp...
return ..
except:
..Some error handling..
If you don't have access to f() implementation, I don't believe you would be able to alter call() to await coroutines (without some ugly monkey-patching).
I think rewriting call() to be async would be only good option then.
Here are two options mixing synchronous and asynchronous code.
Given
import asyncio
import trio
from unsync import unsync
Code
Option 1 - trio
Given sync code (call), call an async function via trio (async_div):
# Sync code
def call(x, y):
"""Return an awaited result."""
try:
resp = async_div(x, y)
except ZeroDivisionError:
return "Caught an exception."
return resp
# Wrapper
def async_div(*args):
"""Return results from an async function."""
return trio.run(_async_div, *args)
# Async code
async def _async_div(x, y):
"""Return an async result."""
await trio.sleep(3)
return x / y
Option 2 - unsync
Decorate the async code with #unsync and call the result():
# Sync code
def call(x, y):
"""Return an awaited result."""
try:
resp = async_div(x, y).result()
except ZeroDivisionError:
return "Caught an exception."
return resp
# Async code
#unsync
async def async_div(x, y):
"""Return an async result."""
await asyncio.sleep(3)
return x / y
Demo
After the prescribed delay (3 secs), the results are the same:
call(0, 1)
# 0.0
call(1, 0)
# 'Caught an exception.'
See Also
Some additional resources:
docs on trio
Talk Python interview with trio creator N. Smith for more details and his philosophy on asynchronous programming.
Python Bytes episode 73, item 6 on quick unsync details
M. Kennedy's webinar demo on using unsync to speed up requests
I am trying to translate this key "debouncing" logic from Javascript to Python.
function handle_key(key) {
if (this.state == null) {
this.state = ''
}
this.state += key
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
console.log(this.state)
}, 500)
}
handle_key('a')
handle_key('b')
The idea is that subsequent key presses extend the timeout. The Javascript version prints:
ab
I don't want to translate the JS timeout functions, I'd rather have idiomatic Python using asyncio. My attempt in Python (3.5) is below, but it doesn't work as global_state is not actually updated when I expect.
import asyncio
global_state = ''
#asyncio.coroutine
def handle_key(key):
global global_state
global_state += key
local_state = global_state
yield from asyncio.sleep(0.5)
#if another call hasn't modified global_state we print it
if local_state == global_state:
print(global_state)
#asyncio.coroutine
def main():
yield from handle_key('a')
yield from handle_key('b')
ioloop = asyncio.get_event_loop()
ioloop.run_until_complete(main())
It prints:
a
ab
I have looked into asyncio Event, Queue and Condition but it isn't clear to me how to use them for this. How would you implement the desired behavior using Python's asyncio?
EDIT
Some more details on how I'd like to use handle_keys. I have an async function that checks for key presses.
#asyncio.coroutine
def check_keys():
keys = driver.get_keys()
for key in keys:
yield from handle_key(key)
Which in turn is scheduled along with other program tasks
#asyncio.coroutine
def main():
while True:
yield from check_keys()
yield from do_other_stuff()
ioloop = asyncio.get_event_loop()
ioloop.run_until_complete(main())
Qeek's use of asyncio.create_task and asyncio.gather makes sense. But how would I use it within a loop like this? Or is there another way to schedule the async tasks that would allow handle_keys calls to "overlap"?
Actual code on GitHub if you are interested.
Why your code doesn't work now?
Both handle_key javascript functions don't block execution. Each just clear timeout callback and set new one. It happens immediately.
Coroutines work another way: using yield from or newer syntax await on coroutine means that we want to resume execution flow only after this coroutine if fully done:
async def a():
await asyncio.sleep(1)
async def main():
await a()
await b() # this line would be reached only after a() done - after 1 second delay
asyncio.sleep(0.5) in your code - is not setting callback by timeout, but code that should be done before handle_key finsihed.
Let's try to make code work
You can create task to start execution some coroutine "in background". You can also cancel task (just like you do with clearTimeout(this.timeout)) if you don't want it to be finished.
Python version that emulates your javascript snippet:
import asyncio
from contextlib import suppress
global_state = ''
timeout = None
async def handle_key(key):
global global_state, timeout
global_state += key
# cancel previous callback (clearTimeout(this.timeout))
if timeout:
timeout.cancel()
with suppress(asyncio.CancelledError):
await timeout
# set new callback (this.timeout = setTimeout ...)
async def callback():
await asyncio.sleep(0.5)
print(global_state)
timeout = asyncio.ensure_future(callback())
async def main():
await handle_key('a')
await handle_key('b')
# both handle_key functions done, but task isn't finished yet
# you need to await for task before exit main() coroutine and close loop
if timeout:
await timeout
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
Idiomatic?
While code above works, it is not how asyncio should be used. Your javascript code based on callbacks, while asyncio usually is about to avoid using of callbacks.
It's hard to demonstrate difference on your example since it's callback based by nature (key handling - is some sort of global callback) and doesn't have more async logic. But this understanding would be important later when you'll add more async operations.
Right now I advice you to read about async/await in modern javascript (it's similar to Python's async/await) and look at examples comparing it to callbacks/promises. This article looks good.
It'll help you understand how you can use coroutine-based approach in Python.
Upd:
Since buttons.check needs to periodically call driver.get_buttons() you'll have to use loop. But it can be done as task along with your event loop.
If you had some sort of button_handler(callback) (this is usually how different libs allow to handle user input) you could use it to set some asyncio.Future directly and avoid loop.
Consider possibility write some little gui app with asyncio from the beginning. I think it may help you to better understand how you can adapt your existing project.
Here's some pseudo-code that shows background task to handle
buttons and using asyncio to handle some simple UI events/states logic:
.
import asyncio
from contextlib import suppress
# GUI logic:
async def main():
while True:
print('We at main window, popup closed')
key = await key_pressed
if key == 'Enter':
print('Enter - open some popup')
await popup()
# this place wouldn't be reached until popup is not closed
print('Popup was closed')
elif key == 'Esc':
print('Esc - exit program')
return
async def popup():
while True:
key = await key_pressed
if key == 'Esc':
print('Esc inside popup, let us close it')
return
else:
print('Non escape key inside popup, play sound')
# Event loop logic:
async def button_check():
# Where 'key_pressed' is some global asyncio.Future
# that can be used by your coroutines to know some key is pressed
while True:
global key_pressed
for key in get_buttons():
key_pressed.set_result(key)
key_pressed = asyncio.Future()
await asyncio.sleep(0.01)
def run_my_loop(coro):
loop = asyncio.get_event_loop()
# Run background task to process input
buttons_task = asyncio.ensure_future(button_check())
try:
loop.run_until_complete(main())
finally:
# Shutdown task
buttons_task.cancel()
with suppress(asyncio.CancelledError):
loop.run_until_complete(buttons_task)
loop.close()
if __name__ == '__main__':
run_my_loop(main())
What's wrong
Basically the yield from xy() is very similar to the normal function call. The difference between function call and yield from is that the function call immediately start processing called function. The yield from statement insert called coroutine into queue inside event loop and give control to event loop and it decide which coroutine in it's queue will be processed.
Here is the explanation of what you code does:
It adds the main into event loop's queue.
The event loop start processing coroutine in the queue.
The queue contains only the main coroutine so it starts that.
The code hits the yield from handle_key('a').
It adds the handle_key('a') in the event loop's queue.
The event loop now contains the main and handle_key('a') but the main cannot be started because it is waiting for the result of the handle_key('a').
So the event loop starts the handle_key('a').
It will do some stuff until it hits the yield from asyncio.sleep(0.5).
Now the event loop contains main(), handle_key('a') and sleep(0.5).
The main() is waiting for result from handle_key('a').
The handle_key('a') is waiting for result from sleep(0.5).
The sleep has no dependency so it can be started.
The asyncio.sleep(0.5) returns None after 0.5 second.
The event loop takes the None and return it into the handle_key('a') coroutine.
The return value is ignored because it isn't assign into anything
The handle_key('a') prints the key (because nothing change the state)
The handle_key coroutine at the end return None (because there isn't return statement).
The None is returned to the main.
Again the return value is ignored.
The code hits the yield from handle_key('b') and start processing new key.
It run same steps from step 5 (but with the key b).
How to fix it
The main coroutinr replace with this:
#asyncio.coroutine
def main(loop=asyncio.get_event_loop()):
a_task = loop.create_task(handle_key('a'))
b_task = loop.create_task(handle_key('b'))
yield from asyncio.gather(a_task, b_task)
The loop.create_task adds into the event loop's queue the handle_key('a') and handle_key('b') and then the yield from asyncio.gather(a_task, b_task) give control to the event loop. The event loop from this point contains handle_key('a'), handle_key('b'), gather(...) and main().
The main() wiating for result from gather()
The gather() waiting until all tasks given as parameters are finished
The handle_key('a') and handle_key('b') has no dependencies so they can be started.
The event loop now contains 2 coroutine which can start but which one will it pick? Well... who knows it is implementation depended. So for better simulation of pressed keys this one replace should be a little better:
#asyncio.coroutine
def main(loop=asyncio.get_event_loop()):
a_task = loop.create_task(handle_key('a'))
yield from asyncio.sleep(0.1)
b_task = loop.create_task(handle_key('b'))
yield from asyncio.gather(a_task, b_task)
Python 3.5 bonus
From the documentation:
Coroutines used with asyncio may be implemented using the async def statement.
The async def type of coroutine was added in Python 3.5, and is recommended if there is no need to support older Python versions.
It means that you can replace:
#asyncio.coroutine
def main():
with newer statement
async def main():
If you start using the new syntax then you have to also replace yield from with await.
Let's say I have a class which uses asyncio loop internally and doesn't have async interface:
class Fetcher:
_loop = None
def get_result(...):
"""
After 3 nested sync calls async tasks are finally called with *run_until_complete*
"""
...
I use all advantages of asyncio internally and don't have to care about it in the outer code.
But then I want to call 3 Fetcher instances in one event loop. If I had async def interface there would be no problem: asyncio.gather could help me. Is there really no other way to do it without supporting both interfaces? Come on! It makes you change all your project because of one asyncio usage. Tell me this is not true.
Come on! It makes you change all your project because of one asyncio
usage. Tell me this is not true.
It's true
Whole idea of using await keyword is to execute concurrent jobs in one event loop from different places of the code (which you can't do with regular function code).
asyncio - is not some utility, but whole style of writing asynchronous programs.
On the other hand Python is very flexible, so you can still try to hide using of asyncio. If you really want to get sync result of 3 Fetcher instances, you can for example do something like this:
import asyncio
def sync_exec(coro):
loop = asyncio.get_event_loop()
return loop.run_until_complete(coro)
class Fetcher:
async def async_get_result(self):
# async interface:
async def async_job():
await asyncio.sleep(1)
return id(self)
return (await async_job())
def get_result(self):
# sync interface:
return sync_exec(self.async_get_result())
#classmethod
def get_results(cls, *fetchers):
# sync interface multiple:
return sync_exec(
asyncio.gather(*[fetcher.async_get_result() for fetcher in fetchers])
)
# single sync get_result:
f1 = Fetcher()
print('Result: ', f1.get_result())
# multiple sync get_result:
f2 = Fetcher()
f3 = Fetcher()
print('Results: ', Fetcher.get_results(f1, f2, f3))
Output:
Result: 2504097887120
Results: [2504097887120, 2504104854416, 2504104854136]
But, again, you'll really regret someday if you continue to write code this way, believe me. If you want to get full advantage of asynchronous programming - use coroutines and await explicitly.