Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Start async function without importing the asyncio package

Is is possible to start a function like this

async def foo():     while True:         print("Hello!") 

without importing the asyncio package (and getting the event loop)?

I am looking for a principle similar to Go's goroutines, where one can launch a coroutine with only go statement.

Edit: The reason why I'm not importing the asyncio package is simply because I think it should be possible to launch coroutine without event loop (explicit). I don't understand why async def and similar statements are part of core language (even part of syntax) and the way to launch created coroutines is available only through package.

like image 327
J-qak Avatar asked Feb 23 '16 19:02

J-qak


People also ask

How do I run async without await in Python?

One way would be to use create_task function: import asyncio async def handler_message(request): ... loop = asyncio. get_event_loop() loop. create_task(perform_message(x,y,z)) ...

How do I run a Python function asynchronously?

To run an async function (coroutine) you have to call it using an Event Loop. Event Loops: You can think of Event Loop as functions to run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses. Example 1: Event Loop example to run async Function to run a single async function: Python3.

Is Asyncio asynchronous?

asyncio is a library to write concurrent code using the async/await syntax. asyncio is used as a foundation for multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.

How do I start an event loop Asyncio?

The alternative way of starting up your event loop is to call the run_forever() method which will subsequently start your asyncio based event loop and have it run indefinitely until the program comes to an end or the stop() method is called.


2 Answers

Of course it is possible to start an async function without explicitly using asyncio. After all, asyncio is written in Python, so all it does, you can do too (though sometimes you might need other modules like selectors or threading if you intend to concurrently wait for external events, or paralelly execute some other code).

In this case, since your function has no await points inside, it just needs a single push to get going. You push a coroutine by sending None into it.

>>> foo().send(None) Hello! Hello! ... 

Of course, if your function (coroutine) had yield expressions inside, it would suspend execution at each yield point, and you would need to push additional values into it (by coro.send(value) or next(gen)) - but you already know that if you know how generators work.

import types  @types.coroutine def bar():     to_print = yield 'What should I print?'     print('Result is', to_print)     to_return = yield 'And what should I return?'     return to_return  >>> b = bar() >>> next(b) 'What should I print?' >>> b.send('Whatever you want') Result is Whatever you want 'And what should I return?' >>> b.send(85) Traceback... StopIteration: 85 

Now, if your function had await expressions inside, it would suspend at evaluating each of them.

async def baz():     first_bar, second_bar = bar(), bar()     print('Sum of two bars is', await first_bar + await second_bar)     return 'nothing important'  >>> t = baz() >>> t.send(None) 'What should I print?' >>> t.send('something') Result is something 'And what should I return?' >>> t.send(35) 'What should I print?' >>> t.send('something else') Result is something else 'And what should I return?' >>> t.send(21) Sum of two bars is 56 Traceback... StopIteration: nothing important 

Now, all these .sends are starting to get tedious. It would be nice to have them semiautomatically generated.

import random, string  def run_until_complete(t):     prompt = t.send(None)     try:         while True:             if prompt == 'What should I print?':                 prompt = t.send(random.choice(string.ascii_uppercase))             elif prompt == 'And what should I return?':                 prompt = t.send(random.randint(10, 50))             else:                 raise ValueError(prompt)     except StopIteration as exc:         print(t.__name__, 'returned', exc.value)         t.close()  >>> run_until_complete(baz()) Result is B Result is M Sum of two bars is 56 baz returned nothing important 

Congratulations, you just wrote your first event loop! (Didn't expect it to happen, did you?;) Of course, it is horribly primitive: it only knows how to handle two types of prompts, it doesn't enable t to spawn additional coroutines that run concurrently with it, and it fakes events by a random generator.

(In fact, if you want to get philosophical: what we did above that manually, could also be called an event loop: Python REPL was printing prompts to a console window, and it was relying on you to provide events by typing t.send(whatever) into it.:)

asyncio is just an immensely generalized variant of the above: prompts are replaced by Futures, multiple coroutines are kept in queues so each of them eventually gets its turn, and events are much richer and include network/socket communication, filesystem reads/writes, signal handling, thread/process side-execution, and so on. But the basic idea is still the same: you grab some coroutines, juggle them in the air routing the Futures from one to another, until they all raise StopIteration. When all coroutines have nothing to do, you go to external world and grab some additional events for them to chew on, and continue.

I hope it's all much clearer now. :-)

like image 60
Veky Avatar answered Sep 19 '22 17:09

Veky


Python coroutines are a syntactic sugar for generators, with some added restrictions in their behavior (so that their purpose is explicitly different and doesn't mix). You can't do:

next(foo()) TypeError: 'coroutine' object is not an iterator 

because it's disabled explicitly. However you can do:

foo().send(None) Hello Hello Hello ... 

Which is equivalent to next() for a generator.

like image 45
Zah Avatar answered Sep 17 '22 17:09

Zah