0

I have a process that should accept requests from two different sources. It is not important, what those requests are, consider them simple string messages for instance. Requests can come from two sources and are filed into a PriorityQueue. The main process handles the requests in the queue. One of the two sources for requests is a telegram bot created by the python-telegram-bot package. Every source needs to run its "event" loop to provide requests. Thus, I want to launch them in separate threads.

The (pseudo) code to show the intention would read as follows:

queue = PriorityQueue()
handler = RequestHandler(queue)
telegramRequester = TelegramRequester(queue)
anotherRequester = SomeOtherSourceRequester(queue)

telegramRequester.start()        # launches telegram bot polling/idle loop
anotherRequester.start()         # launches the loop of another request source
handler.handleRequestsLoop()     # launches the loop that handles incoming requests

The telegram bot and corresponding requester look something like this:

class Bot:
    def __init__(self):
        self._updater = telegram.ext.Updater("my api token", use_context=True)
    
    def run(self):
        self._updater.start_polling(drop_pending_updates=True)
        self._updater.idle()

    def otherFunctions(self):
        # like registering commands, command handlers etc.
        # I've got my bot working and tested as I want it.


class TelegramRequester:
    def __init__(self, queue:RequestQueue) -> None:
        self._queue:RequestQueue = requestQueue
        self._bot = Bot()
        self._thread:threading.Thread = threading.Thread(target=self._bot.run)

    def start(self):
        if not self._thread.is_alive():
            self._thread.start()

However, when running this, I receive the following error messsage:

File "...\myscript.py", line 83, in run
    self._updater.idle()
File "...\env\lib\site-packages\telegram\ext\updater.py", line 885, in idle
    signal(sig, self._signal_handler)
File "C:\Program Files\aaaProgrammieren\Python\Python3_9\lib\signal.py", line 47, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread of the main interpreter

It's the first time I use the telegram api and that I have multiple threads running in parallel. Plus, I have no experience in "web/network/etc." programming. Is it that simple? You shall not run a telegram bot in a separate thread! Or is there something very simple I am missing that would make a construct like mine possible?

LCsa
  • 607
  • 9
  • 30
  • Maybe there's something trying to start a process on the main thread even though it's in its own thread? Check out this question: https://stackoverflow.com/questions/31264826/start-a-flask-application-in-separate-thread – Preston Hager Jun 01 '22 at 20:44

1 Answers1

1

Just some general hints on the usage of PTB (v13.x) in this context:

  • Updater.idle() is intended to keep the main thread alive - nothing else. This is because Updater.start_polling starts a few background threads that do the actual work but don't prevent the main thread from ending. If you have multiple things going on in the main thread, you'll probably have a custom "keep alive" and "shutdown" logic, so you'll likely not need Updater.idle() at all. Instead, you can just call Updater.stop() when you want it to shut down.
  • Updater.idle() allows you to customize which signals it sets. You can pass an empty list to not set any signal handlers.

Dislaimer: I'm currently the maintainer of PTB

CallMeStag
  • 5,467
  • 1
  • 7
  • 22
  • Oh what a first-rate support! :D I think @PrestonHager's comment brought me on the right track already, I had time only now to post here. It seems that when I removed the call of the `Updater.idle()` method inside the `Bot.run()` method, everything works fine and as expected. I assume the default signals in the `Updater.idle()` method could be the cuplrit as it might to start? I do indeed have a `Bot.stop()` method that calls `Updater.stop()`. Would you pass an empty list to `Updater.idle()` or not call it at all? – LCsa Jun 03 '22 at 15:31