Skip to content

[QUESTION] What is the correct shutdown procedure, when running inside event loop? #4125

@kramarov-evg

Description

@kramarov-evg

Issue I am facing

I need to keep a persistet aiohttp session, which I try to store inside bot_data and, given it should only be created in async context, I have an async function wrapper around session and application creation. So I need to launch the bot inside an already existing async context, hence, running event loop. I've referred to #3310, #3711 and wiki on running with other asyncio FWs. I've understood the following points:

  • To launch application in an already running event loop I need to stop using Application.run_polling and stick with the sequence of calls it replaces (Application.initalize -> Application.start -> Application.updater.start_polling -> Application.updater.stop -> Application.stop -> Application.shutdown)
  • First and last calls can be replaced with a context manager (with application) making shortening call sequence to Application.start -> Application.updater.start_polling -> Application.updater.stop -> Application.stop
  • Between start_polling and updater.stop should be something that would keep the event loop alive to prevent application fom stopping immediately after starting

So I came up with the code below, which for some reason doesn't quit upon Ctrl-C event. Moreover, it takes 2 Ctrl-Cs to stop the program, and the second produces the stacktrace below. And my question is, how to correctly handle termination when running inside already existing event loop?

Traceback to the issue

Traceback (most recent call last):
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 629, in run_until_complete
    self.run_forever()
  File "/usr/lib/python3.9/asyncio/base_events.py", line 596, in run_forever
    self._run_once()
  File "/usr/lib/python3.9/asyncio/base_events.py", line 1854, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.9/selectors.py", line 452, in select
    def select(self, timeout=None):
  File "/home/egor/.vscode-server/extensions/ms-python.debugpy-2024.0.0-linux-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_trace_dispatch_regular.py", line 374, in __call__
    return None if event == 'call' else NO_FTRACE
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/egor/.vscode-server/extensions/ms-python.debugpy-2024.0.0-linux-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 39, in <module>
    cli.main()
  File "/home/egor/.vscode-server/extensions/ms-python.debugpy-2024.0.0-linux-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 430, in main
    run()
  File "/home/egor/.vscode-server/extensions/ms-python.debugpy-2024.0.0-linux-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 284, in run_file
    runpy.run_path(target, run_name="__main__")
  File "/home/egor/.vscode-server/extensions/ms-python.debugpy-2024.0.0-linux-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 321, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "/home/egor/.vscode-server/extensions/ms-python.debugpy-2024.0.0-linux-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 135, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/home/egor/.vscode-server/extensions/ms-python.debugpy-2024.0.0-linux-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 124, in _run_code
    exec(code, run_globals)
  File "/home/egor/dev/python/misc/bot_scripts/bot_for_gpt.py", line 67, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.9/asyncio/runners.py", line 47, in run
    _cancel_all_tasks(loop)
  File "/usr/lib/python3.9/asyncio/runners.py", line 63, in _cancel_all_tasks
    loop.run_until_complete(
  File "/usr/lib/python3.9/asyncio/base_events.py", line 629, in run_until_complete
    self.run_forever()
  File "/usr/lib/python3.9/asyncio/base_events.py", line 596, in run_forever
    self._run_once()
  File "/usr/lib/python3.9/asyncio/base_events.py", line 1854, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.9/selectors.py", line 469, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt
2024-02-22 11:46:07,802 - asyncio - ERROR - Task exception was never retrieved
future: <Task finished name='Task-1' coro=<main() done, defined at /home/egor/dev/python/misc/bot_scripts/bot_for_gpt.py:42> exception=RuntimeError('This Application is still running!')>
Traceback (most recent call last):
  File "/home/egor/dev/python/misc/bot_scripts/bot_for_gpt.py", line 60, in main
    await asyncio.sleep(0)
  File "/usr/lib/python3.9/asyncio/tasks.py", line 639, in sleep
    await __sleep0()
  File "/usr/lib/python3.9/asyncio/tasks.py", line 633, in __sleep0
    yield
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/egor/dev/python/misc/bot_scripts/bot_for_gpt.py", line 60, in main
    await asyncio.sleep(0)
  File "/home/egor/dev/python/misc/bot_scripts/.env/lib/python3.9/site-packages/telegram/ext/_application.py", line 393, in __aexit__
    await self.shutdown()
  File "/home/egor/dev/python/misc/bot_scripts/.env/lib/python3.9/site-packages/telegram/ext/_application.py", line 542, in shutdown
    raise RuntimeError("This Application is still running!")
RuntimeError: This Application is still running!
2024-02-22 11:46:07,806 - asyncio - ERROR - Task was destroyed but it is pending!
task: <Task pending name='Application:XXXXXXXXXX:update_fetcher' coro=<Application._update_fetcher() running at /home/egor/dev/python/misc/bot_scripts/.env/lib/python3.9/site-packages/telegram/ext/_application.py:1179> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f90e80430>()]> cb=[gather.<locals>._done_callback() at /usr/lib/python3.9/asyncio/tasks.py:764]>
Exception ignored in: <coroutine object Application._update_fetcher at 0x7f91791c40>
Traceback (most recent call last):
  File "/home/egor/dev/python/misc/bot_scripts/.env/lib/python3.9/site-packages/telegram/ext/_application.py", line 1179, in _update_fetcher
  File "/usr/lib/python3.9/asyncio/queues.py", line 168, in get
  File "/usr/lib/python3.9/asyncio/base_events.py", line 746, in call_soon
  File "/usr/lib/python3.9/asyncio/base_events.py", line 510, in _check_closed
RuntimeError: Event loop is closed

Related part of your code

import aiohttp
import asyncio
import logging
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
from creds import BOT_TOKEN

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await context.bot.send_message(chat_id=update.effective_chat.id, text="How can I help you?")

async def main():
    async with aiohttp.ClientSession() as aiohttp_session:
        application = ApplicationBuilder().token(BOT_TOKEN).build()

        application.bot_data['aiohttp_session'] = aiohttp_session

        start_handler = CommandHandler('start', start)
        application.add_handler(start_handler)

        async with application:
            await application.start()
            await application.updater.start_polling(timeout=30)
            try:
                while True:              # THIS IS SUPPOSED TO KEEP EVENT LOOP RUNNING UNTIL CTRL-C
                    await asyncio.sleep(0) 
            except KeyboardInterrupt:
                await application.updater.stop()
                await application.stop()

if __name__ == '__main__':
    asyncio.run(main())

Operating System

Raspberry Pi OS (based on debian 11 bullseye)

Version of Python, python-telegram-bot & dependencies

python-telegram-bot 20.8
Bot API 7.0
Python 3.9.2 (default, Feb 28 2021, 17:03:44)  [GCC 10.2.1 20210110]

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions