Hi.
As people know I prefer explicit asyncio loop and pass it everywhere.
That's how I build libraries like aiohttp and aiopg, do writing private
source code.
I promote this way of code writing on conferences etc.
Push `asyncio.set_event_loop(None)` at very begin of your code to avoid
mysterious bugs!
But I feel temptation of using implicit loop -- and users of my libraries
do it very often.
Now, after four years of working with asyncio I almost agree with it -- if
implicit loop is used *from coroutine*.
But outside of *task context* (every asyncio coroutine is executed by a
task, you know) it's still dangerous sometimes.
Let's take a close look on the following example:
import motor.motor_asyncio
class A:
client = motor.motor_asyncio.AsyncIOMotorClient()
What's wrong with it? The code is untestable. AsyncIOMotorClient accepts
is_loop parameter which should be event loop instance or None. If the
parameter is None (as in our case) default event loop will be given.
Then we run a test suite. Every test should have own loop for sake of test
isolation. In practice it means that every test tool creates an event loop,
runs test with it and closes the loop after test finishing. It's very true
strategy for testing async code, I know no alternatives.
But our client was coupled with *default* loop created on module import
stage.
As a consequence `await client['db'].find(...)` call will be never finished
because client's loop is not iterated by test runner.
It's not specific Motor's (async MongoDB client for Torrnado and Asyncio)
problem -- aioes.Client, aiohttp.ClientSession and many others does the
same.
In opposite aiopg, asyncpg and aiomysql have no this problem -- their DB
connections are created by `await connect(params)` coroutine call. `await`
cannot be used in global/class namespace, thus user will always call it
from task with proper loop.
Sure, I could forbid aiohttp.ClientSession creation without current task
and make usage of this particular class safe (it will break backward
compatibility for inaccurate users but better to break code early, right).
Asyncio could introduce `asyncio.get_current_loop()` function which will
raise exception if implicit loop exists but there is no running task. After
that third-party libraries may switch to this function in any case where
the loop is required. asyncio itself could do the same.
At the end.
Accessing implicit event loop is very handy but it's safe only inside
running task.
Getting the loop in global namespace may lead to very strange and cryptic
errors (most likely the next usage will just hang).
Thoughts?