From fd2ace30259fa286b5da8c265cebb2e21fc5bb66 Mon Sep 17 00:00:00 2001 From: Valerie Date: Fri, 23 May 2025 06:21:30 -0400 Subject: [PATCH] Refactor ModrinthTracker API request handling to improve session management and retry logic. Enhance error handling for network issues and provide clearer user feedback in the add command. Add cog load and unload documentation. --- modrinthtracker/modrinthtracker.py | 59 ++++++++++++++++-------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/modrinthtracker/modrinthtracker.py b/modrinthtracker/modrinthtracker.py index ff66083..3e2267b 100644 --- a/modrinthtracker/modrinthtracker.py +++ b/modrinthtracker/modrinthtracker.py @@ -24,14 +24,17 @@ class ModrinthTracker(commands.Cog): self.request_lock = asyncio.Lock() async def cog_load(self): + """Initialize the cog.""" self.session = aiohttp.ClientSession() self.bg_task = self.bot.loop.create_task(self.update_checker()) async def cog_unload(self): - if self.session: - await self.session.close() + """Clean up when cog shuts down.""" if self.bg_task: self.bg_task.cancel() + if self.session: + await self.session.close() + self.session = None async def _rate_limit(self): """Implements rate limiting for API requests""" @@ -54,32 +57,34 @@ class ModrinthTracker(commands.Cog): self.request_timestamps.append(current_time) - async def _make_request(self, url, params=None): + async def _make_request(self, url, params=None, retries=3): """Make a rate-limited request to the Modrinth API""" await self._rate_limit() - # Ensure session is available - if self.session is None or self.session.closed: - self.session = aiohttp.ClientSession() - - try: - async with self.session.get(url, params=params) as response: - if response.status == 429: # Too Many Requests - retry_after = int(response.headers.get('Retry-After', 60)) - await asyncio.sleep(retry_after) - return await self._make_request(url, params) - return response - except aiohttp.ClientConnectorError: - # Connection error, try to recreate session and retry once - if self.session and not self.session.closed: - await self.session.close() - self.session = aiohttp.ClientSession() - async with self.session.get(url, params=params) as response: - return response - except Exception as e: - # Log the error for debugging - self.bot.logger.error(f"Error in _make_request: {str(e)}", exc_info=True) - raise + for attempt in range(retries): + try: + # Ensure session is available + if self.session is None or self.session.closed: + self.session = aiohttp.ClientSession() + + async with self.session.get(url, params=params) as response: + if response.status == 429: # Too Many Requests + retry_after = int(response.headers.get('Retry-After', 60)) + await asyncio.sleep(retry_after) + continue + return response + + except (aiohttp.ClientConnectorError, aiohttp.ClientError) as e: + if attempt == retries - 1: # Last attempt + raise + # Close and recreate session + if self.session and not self.session.closed: + await self.session.close() + self.session = aiohttp.ClientSession() + await asyncio.sleep(1) # Wait a bit before retrying + except Exception as e: + self.bot.logger.error(f"Error in _make_request: {str(e)}", exc_info=True) + raise @commands.group() @checks.admin() @@ -170,12 +175,12 @@ class ModrinthTracker(commands.Cog): except aiohttp.ClientConnectorError: await ctx.send("Error: Failed to connect to Modrinth API. Please try again in a few moments.") except aiohttp.ClientError as e: - await ctx.send(f"Error: Network error occurred while contacting Modrinth API: {str(e)}") + await ctx.send(f"Error: Network error occurred while contacting Modrinth API. Please try again in a few moments.") except asyncio.TimeoutError: await ctx.send("Error: Request to Modrinth API timed out. Please try again.") except Exception as e: self.bot.logger.error(f"Error in modrinth add command: {str(e)}", exc_info=True) - await ctx.send(f"An unexpected error occurred. Please try again later or contact the bot owner if the issue persists.") + await ctx.send("An unexpected error occurred. Please try again later or contact the bot owner if the issue persists.") @modrinth.command() async def remove(self, ctx, project_id: str):