2

我使用 MongoDB 和 Motor.Asyncio 创建了一个带有 Quart 的 webapp。当应用程序尝试查询数据库时,会引发错误:

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: Task attach to a different loop 我添加了:

    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 上工作,应该不需要为电机创建一个特殊的循环。它在以前的版本中没有它的工作。所以我完全不知所措。

4

2 回答 2

4

我找到了解决方案,基于这个问题: asyncio.run failed when loop.run_until_complete works

那里提供的答案建议将 mongoDB 的初始化移动到 main() 内部。在这种特定情况下,因为这是一个 Quart 应用程序,所以没有 main 本身。但直觉仍然存在。

我在模块级别定义了一个初始化函数,然后在调用数据库之前检查它是否已经初始化,如果没有,我调用初始化函数。

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

这解决了我的问题。为什么我的代码在升级之前可以工作?我不知道。

于 2019-04-25T03:47:37.793 回答
1

我知道它的回复晚了,但如果它也对其他人有帮助的话;

您可以使用 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 的开发者

于 2020-07-12T10:11:54.063 回答