RuntimeError: Задача получила Future <Future pending>, привязанную к другому циклу

Как вызвать асинхронный метод, который получает цикл обработки событий в главном потоке внутри другого асинхронного метода в Quart?

t.py

from telethon import TelegramClient, functions, types

client2 = TelegramClient(sn, api_id, api_hash).start()

async def create_contact():
    return await client2(functions.contacts.ImportContactsRequest([
        types.InputPhoneContact(0, '8', 'first_name', 'last_name')
    ]))

app.py

from quart import Quart, websocket,render_template,request
import t2
app = Quart(__name__)

@app.route('/wa2tg')
def wa2tg():
    return render_template('wa2tg.html',nm=request.args.get('nm',''))

@app.websocket('/wa2tg2')
async def wa2tg2():
    while True:
        data = await websocket.receive()
        await t2.create_contact()

# Thread(target=tele.client2.run_until_disconnected).start()
app.run(debug=1)        

Ошибка:

Running on http://127.0.0.1:5000 (CTRL + C to quit)
[2019-06-21 16:31:42,035] 127.0.0.1:51696 GET /wa2tg 1.1 200 553 12995
[2019-06-21 16:31:42,486] 127.0.0.1:51698 GET /wa2tg2 1.1 101 - 999
[2019-06-21 16:31:42,490] ERROR in app: Exception on websocket /wa2tg2
Traceback (most recent call last):
  File "C:\Users\Smart\AppData\Local\Programs\Python\Python37-32\lib\site-packages\quart\app.py", line 1629, in handle_websocket
    return await self.full_dispatch_websocket(websocket_context)
  File "C:\Users\Smart\AppData\Local\Programs\Python\Python37-32\lib\site-packages\quart\app.py", line 1651, in full_dispatch_websocket
    result = await self.handle_user_exception(error)
  File "C:\Users\Smart\AppData\Local\Programs\Python\Python37-32\lib\site-packages\quart\app.py", line 948, in handle_user_exception
    raise error
  File "C:\Users\Smart\AppData\Local\Programs\Python\Python37-32\lib\site-packages\quart\app.py", line 1649, in full_dispatch_websocket
    result = await self.dispatch_websocket(websocket_context)
  File "C:\Users\Smart\AppData\Local\Programs\Python\Python37-32\lib\site-packages\quart\app.py", line 1694, in dispatch_websocket
    return await handler(**websocket_.view_args)
  File "D:\SmartBot\my_env\SmartBot\t.py", line 13, in wa2tg2
    await t2.create_contact()
  File "D:\SmartBot\my_env\SmartBot\t2.py", line 22, in create_contact
    types.InputPhoneContact(0, '8807328487', 'first_name', 'last_name')
  File "C:\Users\Smart\AppData\Local\Programs\Python\Python37-32\lib\site-packages\telethon\client\users.py", line 60, in __call__
    result = await future
RuntimeError: Task <Task pending coro=<ASGIWebsocketConnection.handle_websocket() running at C:\Users\Smart\AppData\Local\Programs\Python\Python37-32\lib\site-packages\quart\asgi.py:135> cb=[_wait.<locals>._on_completion() at C:\Users\Smart\AppData\Local\Programs\Python\Python37-32\lib\asyncio\tasks.py:440]> got Future <Future pending> attached to a different loop

Как передать цикл событий в кварту? Я пытался получить цикл и установить внутри функции, но все равно получил ошибку

loop = asyncio.get_event_loop()
@app.websocket('/wa2tg2')
async def wa2tg2():
  while True:
    asyncio.set_event_loop(loop)
    data = await websocket.receive()
    await t2.create_contact()

3 ответа

Решение

Решено после прохождения цикла до app.run

loop = asyncio.get_event_loop()
app.run(debug=1,loop=loop)        

Нить для client.run_until_disconnected() не требуется, так как мы передаем цикл в метод run

Больше на Telethon и Quart

Когда вы делаете:

TelegramClient(sn, api_id, api_hash)

Телефон должен asyncio.get_event_loop(), Этот метод возвращает цикл событий для текущего потока, который в вашем коде является основным потоком. Затем Telethon запоминает и использует цикл из основного потока.

Когда вы делаете:

Thread(target=tele.client2.run_until_disconnected).start()

Вы создаете новый поток, и по причинам, которые я объяснил, вы получаете соответствующую ошибку "Задача получила будущее, присоединенное к другому циклу".

Когда используешь asyncio вы вообще не должны использовать threading если вы действительно не знаете, что делаете.

Фактически, код может быть переписан как (удаляя все ненужные операции импорта, которые, вероятно, были добавлены без особого обдумывания):

Tele.py

from telethon import TelegramClient, functions, types

client = TelegramClient(sn, api_id, api_hash).start()

async def create_contact():
    return await client2(functions.contacts.ImportContactsRequest([
        types.InputPhoneContact(0, phone_number, first_name, last_name)
    ]))

app.py

from quart import Quart, websocket

app = Quart(__name__)

@app.websocket('/wa2tg2')
async def wa2tg2():
    while True:
        data = await websocket.receive()
        await tele.create_contact()

Некоторые вещи, на которые стоит обратить внимание:

  • client.start() может работать без await,
  • client.run_until_disconnected() нужно только позвонить, если вам нужно.
  • async def вызываются с помощью await на них, не создавая отдельную ветку.
  • 0 Вы используете входной контакт будет работать только один раз (или не работать вообще), так как это должно быть случайное число.
  • Никогда не копируйте пасту вслепую. Понять, что он делает в первую очередь.

На самом деле это происходит потому, что Uvicorn имеет тенденцию создавать свой собственный цикл событий.

В двух словах, поместите экземпляр приложения FastAPI и вызов Uvicorn в два отдельных файла. Это решит проблему.

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