Skip to content

Commit

Permalink
feat(hot_reload.py): add hot reloading functionality for bot extensions
Browse files Browse the repository at this point in the history
This commit introduces a new feature that allows for hot reloading of bot extensions. This is achieved by continuously checking for changes in extension files and reloading them if any modifications are detected. This feature improves the development workflow by eliminating the need to manually restart the bot whenever an extension is updated.
  • Loading branch information
kzndotsh committed Sep 14, 2024
1 parent 0b494a4 commit 8b91863
Showing 1 changed file with 75 additions and 0 deletions.
75 changes: 75 additions & 0 deletions tux/handlers/hot_reload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import os
from pathlib import Path

from discord.ext import commands, tasks
from loguru import logger

from tux.bot import Tux


def path_from_extension(extension: str) -> Path:
"""Convert an extension notation to a file path."""
base_dir = Path(__file__).parent.parent
relative_path = extension.replace(".", os.sep) + ".py"
return (base_dir / relative_path).resolve()


class HotReload(commands.Cog):
def __init__(self, bot: Tux) -> None:
self.bot = bot
self.last_modified_time: dict[str, float] = {}
self.hot_reload_loop.start()

async def cog_unload(self) -> None:
self.hot_reload_loop.stop()

@tasks.loop(seconds=3)
async def hot_reload_loop(self) -> None:
"""Loop to check for changes in extension files and reload them if modified."""
for extension in list(self.bot.extensions.keys()):
if extension == "jishaku":
continue

path: Path = path_from_extension(extension)

try:
modification_time: float = path.stat().st_mtime
except FileNotFoundError:
logger.error(f"File not found for extension {extension} at {path}")
continue

if self.last_modified_time.get(extension) == modification_time:
continue

# Reload the extension if it has been modified
self.last_modified_time[extension] = modification_time

try:
await self.bot.reload_extension(extension)
except commands.ExtensionNotLoaded:
pass
except commands.ExtensionError as e:
logger.error(f"Failed to reload extension {extension}: {e}")
else:
logger.info(f"Reloaded {extension}")

@hot_reload_loop.before_loop
async def cache_last_modified_time(self) -> None:
"""Cache the last modified time of all extensions before the loop starts."""
for extension in self.bot.extensions:
if extension == "jishaku":
continue

path: Path = path_from_extension(extension)

try:
modification_time: float = path.stat().st_mtime
except FileNotFoundError:
logger.error(f"File not found for extension {extension} at {path}")
continue

self.last_modified_time[extension] = modification_time


async def setup(bot: Tux) -> None:
await bot.add_cog(HotReload(bot))

0 comments on commit 8b91863

Please sign in to comment.