1

This code fails:

import asyncio
from motor import motor_asyncio


_client = motor_asyncio.AsyncIOMotorClient()
_db = _client.db

users = _db.users


async def main():
    await users.create_index(
        'login',
        unique=True
    )


if __name__ == '__main__':
    #loop = asyncio.get_event_loop()
    #loop.run_until_complete(main())
    asyncio.run(main())

With this error:

Traceback (most recent call last):
  File "/home/sanyash/myrepos/TKP/db.py", line 21, in <module>
    asyncio.run(main())
  File "/usr/local/lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
    return future.result()
  File "/home/sanyash/myrepos/TKP/db.py", line 14, in main
    unique=True
RuntimeError: Task <Task pending coro=<main() running at /home/sanyash/myrepos/TKP/db.py:14> cb=[_run_until_complete_cb() at /usr/local/lib/python3.7/asyncio/base_events.py:158]> got Future <Future pending cb=[run_on_executor.<locals>._call_check_cancel() at /usr/local/lib/python3.7/motor/frameworks/asyncio/__init__.py:80]> attached to a different loop

When I uncomment two lines with loop and comment asyncio.run it works well. What the matter? I thought asyncio.run is a shortcut for this two lines.

The problem is something in the motor_asyncio implementation, because when I changed main to simple return 42, asyncio.run works well too.

sanyassh
  • 8,100
  • 13
  • 36
  • 70

1 Answers1

9

What the matter? I thought asyncio.run is a shortcut for this two lines.

No, it does much more. In particular it creates and sets an new event loop. And this is why you get error: AsyncIOMotorClient() creates some async stuff for default event loop, but another loop created by asyncio.run tries to use it.

If you want to preserve asyncio.run you should move init stuff inside main():

# ...

_client = None
_db = None
users = None


async def main():
    global _client, _db, users
    _client = motor_asyncio.AsyncIOMotorClient()
    _db = _client.db
    users = _db.users

    # ...

It's a good idea in general to start things when event loop is already set and running instead of doing something at module-level.

Mikhail Gerasimov
  • 36,989
  • 16
  • 116
  • 159