From e84eca27377205d5717e60687cade0032cfb76f5 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 18 May 2026 20:02:46 -0400 Subject: [PATCH] fix: require Python 3.14 and handle bot shutdown race Bump pyproject minimum Python version and classifier to 3.14. In the bot, add an internal _stopping flag and a _run_threaded helper to run the event loop and suppress the "Session is closed" RuntimeError when shutdown is in progress. start_threaded now uses _run_threaded and sets _stopping=False, while shutdown sets _stopping=True before closing to avoid a race when stopping the threaded bot. --- pyproject.toml | 4 ++-- src/discord_bot/bot.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 95ff67b..a2c3c5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "support-bot" dynamic = ["version"] description = "Support bot written in python to help manage LizardByte communities" readme = "README.md" -requires-python = ">=3.13" +requires-python = ">=3.14" license = {text = "AGPL-3.0-only"} authors = [ {name = "LizardByte", email = "lizardbyte@users.noreply.github.com"} @@ -17,7 +17,7 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: GNU Affero General Public License v3.0 only (AGPL-3.0-only)", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dependencies = [ diff --git a/src/discord_bot/bot.py b/src/discord_bot/bot.py index 1f2d1b2..274f219 100644 --- a/src/discord_bot/bot.py +++ b/src/discord_bot/bot.py @@ -39,6 +39,7 @@ def __init__(self, *args, **kwargs): self.DEGRADED = False self.bot_thread = threading.Thread(target=lambda: None) + self._stopping = False self.token = os.environ['DISCORD_BOT_TOKEN'] self.db = Database(db_name='discord_bot_database') self.ephemeral_db = {} @@ -260,12 +261,21 @@ def create_thread( ), self.loop) return future.result() + def _run_threaded(self): + try: + self.loop.run_until_complete(self.start(token=self.token)) + except RuntimeError as e: + if self._stopping and str(e) == "Session is closed": + logger.debug("Discord bot startup stopped while closing") + return + raise + def start_threaded(self): try: # Login the bot in a separate thread + self._stopping = False self.bot_thread = threading.Thread( - target=self.loop.run_until_complete, - args=(self.start(token=self.token),), + target=self._run_threaded, daemon=True ) self.bot_thread.start() @@ -281,6 +291,7 @@ def stop(self, future: asyncio.Future = None): self.clean_ephemeral_cache.stop() logger.info("Attempting to close bot connection") if self.bot_thread is not None and self.bot_thread.is_alive(): + self._stopping = True asyncio.run_coroutine_threadsafe(self.close(), self.loop) self.bot_thread.join() logger.info("Closed bot")