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