Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client instances conflict in PyQT5 thread #4513

Open
3 tasks done
MiHaKun opened this issue Dec 6, 2024 · 1 comment
Open
3 tasks done

Client instances conflict in PyQT5 thread #4513

MiHaKun opened this issue Dec 6, 2024 · 1 comment

Comments

@MiHaKun
Copy link

MiHaKun commented Dec 6, 2024

Code that causes the issue

I don't know whether it is a "bug"( I have upgrade) . If I shouldn't have posted this question in this section, I apologize. Please let me know where it would be more appropriate. This issue seems to be related to a conflict.


To avoid blocking the normal operation of the UI, I start a thread when the user clicks a button. The thread uses asyncio event loop execute an asynchronous entry program.

The following code is a simplified version of the implementation of this process.

WinMain

    def OnBtnStartClick(self):
        self.btnStart.setEnabled(False)
        for session in self.sessions:
             self.session_queue.put(session) # type: queue.Queue()
        threading.Thread(target=self.BGThreadBody, args=(features,)).start()

    def BGThreadBody(self, features):
        self.loop = asyncio.new_event_loop()
        self.loop.run_until_complete(self._run_tasks(features))

    async def _run_tasks(self, features):
        cs = []
        for idx in range(self.uiThreadCount.value()): # 1 : works well , 2: exception popup ..
            task = TelegramFeatureTask(
                name=str(idx + 1),
                session_queue=self.session_queue,
            )
            c = self.loop.create_task(task._run_async())
            self.tasks.append(task)
            cs.append(c)
        await asyncio.gather(*cs)

feature

class TelegramFeatureTask:
    def __init__(
        self,
        name: str,
        session_queue: Queue):
        self.session_queue = session_queue
        self.name = name


    async def _run_async(self):
        while not self.session_queue.empty():
            s = self.session_queue.get(timeout=1)
            if not s:
                break
            tg_session = StringSession(s)
            client = TelegramClient(
                session=tg_session,
                **config['login_param'] # simple read from config file
            )        
            await asyncio.wait_for(client.connect(), timeout=10)
            # await client.connect()
            me = await client.get_me()
            if not me:
                raise NoMeError()
            upload = await self.client.upload_file(config['avatars'][self.name])
            photo = await self.client(functions.photos.UploadProfilePhotoRequest(file=upload))
            await client.disconnect()
            

these code works well in console multithread . But failed in PyQT thread .

upload = await client.upload_file(avatar)
photo = await client(functions.photos.UploadProfilePhotoRequest(file=upload))

Exception detail .

File part 0 missing (caused by UploadProfilePhotoRequest)
Sleeping for 4s (0:00:04) on UpdateProfilePhotoRequest flood wait

Expected behavior

no exceptions .

Actual behavior

File part 0 missing (caused by UploadProfilePhotoRequest)
Sleeping for 4s (0:00:04) on UpdateProfilePhotoRequest flood wait

Traceback

I debug this problem for days .
I add log in telethon's UserMethods call and found there mightbe a conflict .

the log code added in telethon/client/users.py:

class UserMethods:
    async def __call__(self: 'TelegramClient', request, ordered=False, flood_sleep_threshold=None):
        return await self._call(self._sender, request, ordered=ordered)

    async def _call(self: 'TelegramClient', sender, request, ordered=False, flood_sleep_threshold=None):
        # print(f"self: {id(self)} equl:{id(asyncio.get_running_loop()) == id(self._loop)} running_loop: {id(asyncio.get_running_loop())} , self._loop: {id(self._loop)} request: {type(request)}`{id(request)}` sender: {type(sender)}`{id(sender)}`")
        print(f"self: {id(self)}  request: {type(request)}`{id(request)}` sender: {type(sender)}`{id(sender)}`")
        if self._loop is not None and self._loop != helpers.get_running_loop():
            raise RuntimeError('The asyncio event loop must not change after connection (see the FAQ for details)')
        # if the loop is None it will fail with a connection error later on
        if flood_sleep_threshold is None:
            flood_sleep_threshold = self.flood_sleep_threshold
        requests = (request if utils.is_list_like(request) else (request,))
        for r in requests:
            if not isinstance(r, TLRequest):
                raise _NOT_A_REQUEST()
            await r.resolve(self, utils)

# ...............
# ...............

I print the self(client) 's id, self._loop id (never changed) , runningloop is equl self._loop ( they are always the same ) request type and id , sender's id .

the confilct log is here :
image

  • the task count is 2 .
  • the get_me/ upload 's request log is right .
  • the uploadprofilerequest's log is confilct . the second task's instance changed to the first task's instance and caused error TypeInputPhoto and flood rpc error .

I read the FAQ and Compatibility and Convenience document .
I understand that the esteemed author has advised everyone to avoid using threads whenever possible. However, it seems that in GUI programs, the only way to prevent blocking is to start a thread. Therefore, I would still appreciate any suggestions regarding this issue. Thank you.

Btw: In fact , my oldest code was run each client in thread's eventloop (multithread which have one loop each) . it caused " RunningTimeError(The asyncio event loop must not change after connection (see the FAQ for details)) " , but the code works well in console application too ....

Telethon version

1.38.1

Python version

3.12.7

Operating system (including distribution name and version)

Windows 11

Other details

PyQt5==5.15.11
PyQt5-Qt5==5.15.2
PyQt5_sip==12.15.0

Checklist

  • The error is in the library's code, and not in my own.
  • I have searched for this issue before posting it and there isn't an open duplicate.
  • I ran pip install -U https://github.com/LonamiWebs/Telethon/archive/v1.zip and triggered the bug in the latest version.
@Lonami
Copy link
Member

Lonami commented Dec 14, 2024

Does https://stackoverflow.com/a/62112712 or the other answer help?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants