47

The documentation for asyncio gives two examples for how to print "Hello World" every two seconds: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio-hello-world-callback https://docs.python.org/3/library/asyncio-task.html#asyncio-hello-world-coroutine

I can run those from the interpreter, but if I do I lose access to the interpreter. Can an asyncio event loop be run in the background, so that I can keep typing commands at the interpreter?

3 Answers 3

69

Edit:

If using Python 3.8 or above, you should use the asyncio repl, as explained in zeronone's answer. If using 3.7 or lower, you can use this answer.


You can run the event loop inside a background thread:

import asyncio

@asyncio.coroutine
def greet_every_two_seconds():
    while True:
        print('Hello World')
        yield from asyncio.sleep(2)

def loop_in_thread(loop):
    asyncio.set_event_loop(loop)
    loop.run_until_complete(greet_every_two_seconds())


loop = asyncio.get_event_loop()
import threading
t = threading.Thread(target=loop_in_thread, args=(loop,))
t.start()

# Hello World
# 2 second later
# Hello World

Note that you must call asyncio.set_event_loop on the loop, otherwise you'll get an error saying that the current thread doesn't have an event loop.

If you want to interact with the event loop from the main thread, you'll need to stick to loop.call_soon_threadsafe calls.

While this kind of thing is an ok way to experiment in the interpreter, in actual programs, you'll probably want all your code running inside the event loop, rather than introducing threads.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you so much. Someone elsewhere said not to mix threads and asyncio. Do you disagree? The process would background is an OSC server. From the interpreter I would tell it what signals to send. The Python process would be lightweight, sending instructions (maybe 20 bundles a second) to synthesizers that do all the audio math. Given that motivation, do you continue to suggest putting everything in the main loop?
@user916142 Well, normally the whole point of using an asynchronous framework like asyncio is to avoid using threads. If you're always going to run this code inside an interactive interpreter, then I guess it makes sense. But normally I would say you should implement an asyncio Procotol or Stream that handles incoming network connections, and then tell it what signals to send via network requests.
Here is some of my code that handles messages over serial, WebSockets, stdin, and OSC with asyncio. It doesn't give you access to the interpreter, but my solution was to handle keyboard input with a callback: loop.add_reader(sys.stdin.fileno(), read_stdin)
@dano "you'll probably want all your code running inside the event loop, rather than introducing threads" -- Would you make an exception for a situation where you have large codebase that you can't / don't want to convert entirely to async style; and at the same time, you do want some capabilities of asyncio (such as scheduled background operations)? It seems running an event loop in a background thread would fit perfectly in this scenario, but I may be missing some hidden complications.
@max Yes, I think that's ok. You just need to be careful about sharing data between the asyncio eventloop thread and whatever other threads you have running.
|
48

With Python 3.8, you can use the new asyncio REPL.

$ python -m asyncio
>>> async def greet_every_two_seconds():
...     while True:
...         print('Hello World')
...         await asyncio.sleep(2)
...
>>> # run in main thread (Ctrl+C to cancel)
>>> await greet_every_two_seconds()
...
>>> # run in background
>>> asyncio.create_task(greet_every_two_seconds())

4 Comments

I get "SyntaxError: invalid syntax" pointing at the from in "await from asyncio.sleep(2). I'm currently running python 3.8
@avenmia remove the from after await
RuntimeError: no running event loop. Looks like one needs to create a new loop with loop = asyncio.get_event_loop() and then task = loop.create_task(greet_every_two_seconds())
Is there a way to make this work with ipython as well?
0

@raisyn

From ipython, this is working:

import asyncio
from traitlets.config.application import Application

async def greet_every_two_seconds():
    while True:
        print('Hello World')
        await asyncio.sleep(2)
        
# Create coroutine
coro = greet_every_two_seconds()

# Create and get an adequate asyncio event loop
Application.instance().shell.enable_gui('asyncio')
loop = asyncio.get_event_loop()

loop.create_task(coro)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.