score.asyncio

A module that manages a configured asyncio.AbstractEventLoop.

Quickstart

If you have no preferences about the library to use, initializing the module with its defaults is sufficient:

[score]
modules =
    score.asyncio

You can now start/stop the loop from anywhere by grabbing a loop token:

token = configured_asyncio.start_loop()
# ... the loop is guaranteed to be running now ...
token.release()
# if this was the last token, the loop will stop.
# otherwise it will keep running until all
# tokens have been released.

Configuration

score.asyncio.init(confdict)[source]

Initializes this module according to the SCORE module initialization guidelines with the following configuration keys:

backend “builtin”
The library to use for creating the event loop. Current valid values are uvloop and builtin.
use_global_loop False
Whether the global loop object should be used. The “global” loop is the one returned by asyncio.get_event_loop().
stop_timeout None

Defines how long the module will wait for all tasks running in the loop to finish when stopping the loop. The value will be interpreted through a call to score.init.parse_time_interval().

The default value None indicates that the module will wait indefinitely. If you want to the loop to terminate immediately, without waiting for tasks at all, you must pass “0”.

Details

Starting the Loop

Since the sole purpose of this module is to provide a common asyncio.AbstractEventLoop to different, potentially unrelated SCORE modules, this module must decide when the loop must be started and when it can be stopped.

This is why any module using this one must indicate its intention to use the loop by calling the configured module’s start_loop method.

The configured module will make sure that the loop is running after the first call to its start_loop method and will stop the loop automatically when all loop tokens have been released.

The following is an excerp from one of the unit tests that demonstrates this behavior:

assert not configured_asyncio.loop.is_running()
for _ in range(10):
    token = configured_asyncio.start_loop()
    assert configured_asyncio.loop.is_running()
    tokens.append(token)
for token in tokens:
    assert configured_asyncio.loop.is_running()
    token.release()
assert not configured_asyncio.loop.is_running()

There are three different, identical ways to stop a running loop:

  • Invoking the token’s release method,
  • passing the token to the configured module’s release_loop method and
  • using the token as a context manager (i.e. in a with statement)
# method 1
token = configured_asyncio.start_loop()
try:
    do_something()
finally:
    token.release()

# method 2
token = configured_asyncio.start_loop()
try:
    do_something()
finally:
    configured_asyncio.release_loop(token)

# method 3
with configured_asyncio.start_loop():
    do_something()

Note

The call to start_loop just starts the loop, which then runs in a different thread. You must use the asyncio.AbstractEventLoop’s thread-safe methods to interact with the running loop:

from asyncio import run_coroutine_threadsafe

with configured_asyncio.start_loop():
    run_coroutine_threadsafe(some_coroutine, configured_asyncio.loop)

API

score.asyncio.init(confdict)[source]

Initializes this module according to the SCORE module initialization guidelines with the following configuration keys:

backend “builtin”
The library to use for creating the event loop. Current valid values are uvloop and builtin.
use_global_loop False
Whether the global loop object should be used. The “global” loop is the one returned by asyncio.get_event_loop().
stop_timeout None

Defines how long the module will wait for all tasks running in the loop to finish when stopping the loop. The value will be interpreted through a call to score.init.parse_time_interval().

The default value None indicates that the module will wait indefinitely. If you want to the loop to terminate immediately, without waiting for tasks at all, you must pass “0”.

class score.asyncio.ConfiguredAsyncioModule[source]

This module’s configuration class.

loop

This is the configured asyncio.AbstractEventLoop. You should not start this loop using the default run_forever() and run_until_complete() methods. Use the provided start_loop() instead.

See Starting the Loop for details.

start_loop()[source]

Makes sure the configured loop is running. Will possibly start the loop in a different thread and return a loop token.

This method is thread-safe.

See Starting the Loop for usage details.

release_loop(token)[source]

Releases a previously acquired token.

See Starting the Loop for usage details.

await_(coroutine)[source]

Blocks until given coroutine is finished and returns the result (or raises the exception).

This method will acquire a loop token, schedule the coroutine for execution in the configured event loop, await its termination and return the result (or raise the exception).

This is very similar to the builtin method asyncio.AbstractEventLoop.run_until_complete(), but will work when different clients try to execute a coroutine simultanously, regardless of the current loop state:

>>> import asyncio
>>> @asyncio.coroutine
... def foo():
...     return 1
...
>>> @asyncio.coroutine
... def bar():
...     return 1 / 0
...
>>> foo()
<generator object foo at 0x7fea86b20e08>
>>> score.asyncio.await_(foo())
1
>>> score.asyncio.await_.(bar())
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/can/Projects/score/py.asyncio/score/asyncio/_init.py", line 107, in await_
    return self.loop.run_until_complete(coroutine)
  File "/usr/lib/python3.6/asyncio/base_events.py", line 467, in run_until_complete
    return future.result()
  File "/usr/lib/python3.6/asyncio/coroutines.py", line 210, in coro
    res = func(*args, **kw)
  File "<console>", line 3, in bar
ZeroDivisionError: division by zero
await_multiple(coroutines)[source]

Just like await_(), but awaits the completion of multiple coroutines. The return value is different though: the method will provide a list of 2-tuples, where the first value is a bool indicating successful execution of the coroutine and the second value is the exception itself or the return value.

Example with two coroutines, the first successfully returning 1, while the other raising a ZeroDivisionError:

[
    (True, 1),
    (False, ZeroDivisionError('division by zero',)),
]
class score.asyncio.LoopToken[source]

A token provided by the configured score.asyncio module. The configured asyncio will keep running as long as this token is held. Use release() to indicate that you’re done using the loop.

release()[source]

Releases this token.

See Starting the Loop for details.