How do I call an asynchronous corotine within a function that is initialized into a thread?

Asked

Viewed 124 times

0

import telepot, time, threading, asyncio
from telepot.loop import MessageLoop
from datetime import datetime
from telepot.namedtuple import ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup, InlineKeyboardButton
from telethon.sync import TelegramClient
from telethon.tl.types import PeerChannel
...
all_participants = []
pts_organ = {}
...
client = TelegramClient(number, api_id, api_hash).start(bot_token = bot_token)

def getMembros(canal_id):
    global all_participants, pts_organ
    ...
    all_participants =  client.get_participants(PeerChannel(canal_id), aggressive=True)
    ...
    return pts_organ

...
def handle(msg):
     ...
     else:
        if (msg[content_type] == '/todos_os_membros'):
            getMembros(chat_id)

MessageLoop(bot, handle).run_as_thread()

When I run the script, it shows the following error:

Traceback (most recent call last):
  File "C:\Users\Marcelo\AppData\Local\Programs\Python\Python38-32\lib\site-packages\telepot\loop.py", line 37, in run_forever
    self._handle(msg)
  File "C:\Users\Marcelo\Desktop\bot.py", line 103, in handle
    getMembros(chat_id)
  File "C:\Users\Marcelo\Desktop\bot.py", line 26, in getMembros
    all_participants =  client.get_participants(PeerChannel(canal_id), aggressive=True)
  File "C:\Users\Marcelo\AppData\Local\Programs\Python\Python38-32\lib\site-packages\telethon\sync.py", line 35, in syncified
    loop = asyncio.get_event_loop()
  File "C:\Users\Marcelo\AppData\Local\Programs\Python\Python38-32\lib\asyncio\events.py", line 639, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-2'.

Warning (from warnings module):
  File "C:\Users\Marcelo\AppData\Local\Programs\Python\Python38-32\lib\site-packages\telepot\loop.py", line 39
    traceback.print_exc()
RuntimeWarning: coroutine 'ChatMethods.get_participants' was never awaited

I just want my method getMembros(canal_id) return the list of members of a channel, but it seems that I am not succeeding by the fact that the method get_participants() of API Telethon is asynchronous. I don’t know for sure. I don’t understand much about the module asy and Thread.

I’ve tried to:

 ...
 async def getMembros(canal_id):
      ...
      all_participants =  await client.get_participants(PeerChannel(canal_id), aggressive=True)

The mistake:

Warning (from warnings module):
  File "C:\Users\Marcelo\Desktop\bot.py", line 103
    getMembros(chat_id)
RuntimeWarning: coroutine 'getMembros' was never awaited

Afterward:

 ...
 async def handle(msg):
    ...
    await getMembros(chat_id)

And the following mistake:

Warning (from warnings module):
  File "C:\Users\Marcelo\AppData\Local\Programs\Python\Python38-32\lib\site-packages\telepot\loop.py", line 37
    self._handle(msg)
RuntimeWarning: coroutine 'handle' was never awaited

I don’t know what else to do. I know that client.get_participants() it takes some time to finish, so I wanted to know how I can leave it in parallel, for what handle(msg) keep running or something. Or find a way to get the dictionary back pts_organ, of getMembros, who is responsible for returning it, within the handle(msg)

1 answer

1


In general or you use asynchronous code for things in parallel, or uses multi-threaded code. You can mix the two together - and it’s common to have time-consuming tasks running on other threads, and you control that from asynchronous code.

The fact is that a function that is declared with async def, when it is called, it is not executed. Only when you execute await in the item that is returned from such a function is that it is actually executed. (hence the "Warning": not that it gave error - but you prepared the whole environment to execute the code in a certain function, and never called that code, which then was not executed. What calls the code is the expression await)

Only here comes another trick: you can’t just use await in a normal function, which is not created with async def.

If your code is parallelized using threads, not async, and you just want to call this function, one way to do it is to start and close the loop by just making this call. A loop of asyncio started in a thread will be restricted to that thread.

In short, a simple way to call your client.get_participants is pdir for asyncio to initialize a new loop, run to the end, and close the loop with the call asyncio.run:

all_participants =  asyncio.run(client.get_participants(PeerChannel(canal_id), aggressive=True))
  • the call asyncio.run, new to Python 3.7, as opposed to loop.run_until_complete of version 3.4, creates a new event loop automatically even if it is not in the main thread, waits for the completion of the co-routine passed as parameter, and returns its result. In a normal design, it would be the entry point of the routines declared with async def - and would be called once.

May that you still have problems, since the library is designed for a parallelism with async, and you’re forcing a pattern of threads on top. If persisitrem problems, best rewrite all your code to use asyncio (and then you no longer need threads)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.