asyncio.run завершается ошибкой, когда loop.run_until_complete работает - PullRequest
1 голос
/ 30 марта 2019

Этот код не работает:

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())

С этой ошибкой:

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

Когда я раскомментирую две строки с loop и комментирую asyncio.run, это работает хорошо. В чем дело? Я думал, что asyncio.run является ярлыком для этих двух строк.

Проблема заключается в реализации motor_asyncio, потому что когда я изменил main на простой return 42, asyncio.run тоже хорошо работает.

1 Ответ

4 голосов
/ 30 марта 2019

В чем дело? Я думал, что asyncio.run является ярлыком для этих двух линии.

Нет, это намного больше. В частности, создает и устанавливает новый цикл обработки событий. И вот почему вы получаете ошибку: AsyncIOMotorClient() создает некоторые асинхронные элементы для цикла событий по умолчанию, но другой цикл, созданный asyncio.run, пытается использовать его.

Если вы хотите сохранить asyncio.run, вы должны переместить инициализацию внутри main():

# ...

_client = None
_db = None
users = None


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

    # ...

В общем, неплохо бы начинать все, когда цикл событий уже установлен и работает, вместо того, чтобы что-то делать на уровне модуля.

...