Asyncio (Quart) throwing Task, связанный с другой ошибкой цикла при подключении к MongoDB с мотором

Я создал веб-приложение с Quart, используя MongoDB и Motor.Asyncio. Когда приложение пытается запросить БД, выдается ошибка:

Task <Task pending coro=<ASGIHTTPConnection.handle_request() 
running at /home/user/.local/lib/python3.7/site-packages/quart
/asgi.py:59> cb=[_wait.<locals>._on_completion() at /usr/lib/python3.7
/asyncio/tasks.py:440]> got Future <Future pending cb=[run_on_executor.
<locals>._call_check_cancel() at /home/user/.local/lib/python3.7/site-
packages/motor/frameworks/asyncio/__init__.py:80]> attached to a 
different loop

Я не понимаю, почему это происходит, или не знаю, как это решить.

Приложение работало без проблем, но я решил обновить Python 3.6 (на Ubuntu-18.04) до python 3.7.1. С этим я обновил Quart до 0.9.0. В результате этого обновления произошла вышеуказанная ошибка.

Приложение запускается из командной строки с Hypercorn и Nginx.

Я не уверен, какие части моего кода актуальны в этом случае

Сначала я импортирую Quart, затем Motor:

    # Mongodb / Gridfs with Motor
    import motor.motor_asyncio
    from pymongo import ReturnDocument
    from bson.objectid import ObjectId
    from bson.son import SON

    client = motor.motor_asyncio.AsyncIOMotorClient()
    db = client.myDataBase
    fs = motor.motor_asyncio.AsyncIOMotorGridFSBucket(db)

После этого я добавляю:

    app = Quart(__name__)

Я попытался переместить это, прежде чем блок импорта двигателя ничего не изменил.

Как предложено в вопросе / ответе: RuntimeError: Задача присоединена к другому циклу, который я добавил:

    loop=asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    client = motor.motor_asyncio.AsyncIOMotorClient(io_loop=loop)

Это не решило это.

Это блок, в котором был сделан первый вызов двигателя и где произошла ошибка:

    try:
        session_info = await db.sessions.find_one(
            {
                'session_id': uuid.UUID(session_id)
            },
            {
                'username':True,
                '_id':False
            }
        )
    except Exception as e:
        print('error retrieving session info:', e)

Я могу проигнорировать ошибку и продолжить, но затем делается следующий вызов, и возникает та же ошибка.

Я понимаю, что Quart работает по умолчанию с именем event_loop, и не нужно создавать специальный цикл для двигателя. Это работает без него в предыдущей версии. Так что я в полной растерянности.

1 ответ

Я нашел решение, основываясь на этом вопросе: asyncio.run не работает, когда loop.run_until_complete работает

Ответ, приведенный там, предлагает перенести инициализацию mongoDB внутрь main(). В данном конкретном случае, поскольку это приложение Quart, основной части нет. Но интуиция остается.

Я определил функцию инициализации на уровне модуля, затем перед вызовом в базу данных проверяю, инициализирована ли она, если нет, то я вызываю функцию инициализации.

    import motor.motor_asyncio
    from pymongo import ReturnDocument
    from bson.objectid import ObjectId
    from bson.son import SON

    client = None
    db = None
    fs = None

    async def connect_to_mongo():
        global client, db, fs
        client = motor.motor_asyncio.AsyncIOMotorClient()
        db = client.myDataBase
        fs = motor.motor_asyncio.AsyncIOMotorGridFSBucket(db)

затем перед вызовом в базу данных:

    if db is None:
        await connect_to_mongo()

Это решило мою проблему. Почему мой код работал до обновления? Я не знаю.

Я знаю его поздний ответ, но если он поможет и кому-то другому;

Вы можете использовать Quart-Motor (pip install quart-motor) и использовать его в любом месте вашего приложения.

https://github.com/marirs/quart-motor/

from quart_motor import Motor

app = Quart(__name__)
mongo = Motor(app,  uri='...')

@app.route('/<user_name:str>')
async def user_info(user_name):
    user = await mongo.db.users.find_one_or_404({"username": user_name})
    return render_template("user.html", user=user)

Примечание: я разработчик Quart-Motor

Другие вопросы по тегам