-
Notifications
You must be signed in to change notification settings - Fork 112
feat: replace empty pass statements with error handling and logging #214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: replace empty pass statements with error handling and logging #214
Conversation
📝 WalkthroughWalkthroughAdded robust logging and duplicate-registration checks to the event bus; hardened Discord view handlers with explicit logging for exceptions; and enhanced the DuckDuckGo search tool with configurable timeout/retries, optional caching, asyncio timeouts, exponential backoff with jitter, and detailed per-attempt logging and caching. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant DuckDuckGoSearchTool as Tool
participant ExternalDDG as DDG_API
participant Cache
Note over Tool: Configured (timeout, retries, caching, backoff)
Client->>Tool: search(query)
alt Cache enabled
Tool->>Cache: compute & lookup key
Cache-->>Tool: cached results (hit) or miss
alt hit
Tool-->>Client: return cached results
else miss
Tool->>DDG_API: request (with timeout)
alt success
DDG_API-->>Tool: results
Tool->>Cache: store results
Tool-->>Client: return results
else timeout/conn error
Tool->>Tool: backoff + retry (up to max_retries)
Tool->>DDG_API: retry...
Tool-->>Client: [] after retries exhausted
end
end
else Cache disabled
Tool->>DDG_API: request (with timeout)
alt success
DDG_API-->>Tool: results
Tool-->>Client: return results
else retries/backoff similar to above
Tool-->>Client: [] after retries exhausted
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/app/core/events/event_bus.py (1)
18-29: Platform filter parameter is accepted but never enforced during dispatch.The
platformparameter inregister_handler()(line 18) is only logged at line 29 and never stored. The handler is added to the registry via_add_handler()without the platform constraint, and during dispatch (lines 54-68), handlers are retrieved and invoked without any filtering by platform. If platform filtering is intended, handlers must be stored with their platform constraint and filtered during dispatch.
🧹 Nitpick comments (4)
backend/integrations/discord/views.py (1)
36-41: Uselogger.exceptionto preserve stack traces.For the generic
Exceptionhandler,logger.exceptionautomatically includes the traceback, improving debuggability. The same applies to lines 111-114 and 131-134.Proposed fix
except discord.HTTPException as e: logger.error(f"Discord API error sending DM to user {user.id}: {e.status} - {e.text}") except Exception as e: - logger.error(f"Unexpected error sending DM to user {user.id}: {type(e).__name__} - {str(e)}") + logger.exception(f"Unexpected error sending DM to user {user.id}: {type(e).__name__}")Apply the same pattern to the other exception handlers at lines 113-114 and 133-134.
backend/app/core/events/event_bus.py (1)
54-68: Consider log level and error handling for fire-and-forget tasks.Two observations:
Log level: INFO-level logging on every handler invocation (lines 59, 65) could be noisy under high event volume. Consider DEBUG level.
Unhandled exceptions:
asyncio.create_taskwithout exception handling means handler errors are silently lost (only logged to asyncio's default handler). Consider wrapping handlers or addingtask.add_done_callback()for visibility.Optional: Add exception callback for task monitoring
async def dispatch(self, event: BaseEvent): """Dispatch an event to all registered handlers""" def _log_exception(task): if task.exception(): logger.error(f"Handler failed: {task.exception()}") for handler in self.global_handlers: logger.debug(f"Calling global handler: {handler.__name__}") task = asyncio.create_task(handler(event)) task.add_done_callback(_log_exception) # ... same for event-specific handlersbackend/app/agents/devrel/tools/search_tool/ddg.py (2)
18-23: Unbounded cache can cause memory growth.The
_cachedict grows indefinitely without eviction. For long-running services, this could lead to memory issues.Consider adding a max size or TTL, or use
functools.lru_cache/cachetools.TTLCache:Proposed fix using bounded cache
+from cachetools import TTLCache + class DuckDuckGoSearchTool: - def __init__(self, timeout: int = 10, max_retries: int = 2, cache_enabled: bool = False): + def __init__(self, timeout: int = 10, max_retries: int = 2, cache_enabled: bool = False, cache_maxsize: int = 100, cache_ttl: int = 300): self.timeout = timeout self.max_retries = max_retries self.cache_enabled = cache_enabled - self._cache: dict = {} if cache_enabled else None + self._cache = TTLCache(maxsize=cache_maxsize, ttl=cache_ttl) if cache_enabled else None
91-96: Uselogger.exceptionto preserve stack traces.For generic exception handlers,
logger.exceptionautomatically includes the traceback for better debuggability.Proposed fix
except Exception as e: last_exception = e - logger.error(f"Search error (attempt {attempt + 1}/{self.max_retries + 1}): {str(e)}") + logger.exception(f"Search error (attempt {attempt + 1}/{self.max_retries + 1})") if attempt < self.max_retries: await asyncio.sleep(1) continue
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
backend/app/agents/devrel/tools/search_tool/ddg.pybackend/app/core/events/event_bus.pybackend/integrations/discord/views.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-08T13:31:11.572Z
Learnt from: smokeyScraper
Repo: AOSSIE-Org/Devr.AI PR: 72
File: backend/app/agents/devrel/nodes/handle_web_search_node.py:31-42
Timestamp: 2025-06-08T13:31:11.572Z
Learning: In backend/app/agents/devrel/tools/search_tool.py, the TavilySearchTool.search() method has partial error handling for missing API key, AttributeError, ConnectionError, and TimeoutError, but lacks a comprehensive Exception catch-all block, so calling functions may still need additional error handling for other potential exceptions.
Applied to files:
backend/app/agents/devrel/tools/search_tool/ddg.py
🧬 Code graph analysis (1)
backend/app/core/events/event_bus.py (1)
backend/app/core/events/enums.py (1)
EventType(10-32)
🪛 Ruff (0.14.10)
backend/app/agents/devrel/tools/search_tool/ddg.py
75-75: Consider moving this statement to an else block
(TRY300)
91-91: Do not catch blind exception: Exception
(BLE001)
93-93: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
93-93: Use explicit conversion flag
Replace with conversion flag
(RUF010)
backend/integrations/discord/views.py
39-39: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
40-40: Do not catch blind exception: Exception
(BLE001)
41-41: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
41-41: Use explicit conversion flag
Replace with conversion flag
(RUF010)
112-112: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
113-113: Do not catch blind exception: Exception
(BLE001)
114-114: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
114-114: Use explicit conversion flag
Replace with conversion flag
(RUF010)
132-132: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
133-133: Do not catch blind exception: Exception
(BLE001)
134-134: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
134-134: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🔇 Additional comments (7)
backend/integrations/discord/views.py (3)
1-12: LGTM!Logger initialization follows Python conventions with
__name__for proper module-level namespacing.
109-114: Good implementation of specific Discord exception handling.The cascading exception hierarchy (NotFound → HTTPException → Exception) correctly handles the Discord API errors as required by the issue objectives.
129-134: Consistent error handling pattern.Same recommendation applies here regarding
logger.exceptionfor the generic Exception handler (already noted above).backend/app/core/events/event_bus.py (2)
31-42: LGTM!Duplicate handler prevention is correctly implemented using identity check. The logging provides good visibility into the registration process.
44-52: LGTM!Consistent duplicate prevention for global handlers with appropriate logging.
backend/app/agents/devrel/tools/search_tool/ddg.py (2)
29-44: LGTM!Cache key construction and lookup logic is correct. Early return on cache hit improves performance.
98-100: LGTM!Graceful degradation by returning an empty list after exhausting retries is appropriate. The final error log captures sufficient context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
backend/app/agents/devrel/tools/search_tool/ddg.py (2)
21-29: Consider cache size limits and thread safety.The cache implementation uses an unbounded dictionary without TTL or size limits, which could lead to memory growth in production. Additionally, the dictionary is not thread-safe—concurrent searches could cause race conditions during cache reads/writes.
Consider:
- Adding a maximum cache size with LRU eviction (e.g., using
functools.lru_cacheor a bounded dict)- Implementing cache entry TTL to prevent stale results
- Using
asyncio.Lockto protect cache operations in concurrent scenarios
83-105: Exponential backoff with jitter correctly implemented!The retry logic now properly implements exponential backoff with jitter across all exception handlers (lines 87, 95, 103), correctly addressing the previous review feedback. The formula
delay = min(self.base_delay * (2 ** attempt) + random.uniform(-0.1, 0.1), self.max_delay)provides resilient retry behavior with bounded delays.Minor improvement: Line 101 uses
logging.error—consider usinglogging.exceptioninstead to automatically capture the stack trace for better debugging:- logger.error(f"Search error (attempt {attempt + 1}/{self.max_retries + 1}): {str(e)}") + logger.exception(f"Search error (attempt {attempt + 1}/{self.max_retries + 1}): {e}")Note: Catching both
asyncio.TimeoutErrorandTimeoutErrortogether (line 83) is appropriate for Python version compatibility.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/app/agents/devrel/tools/search_tool/ddg.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-08T13:31:11.572Z
Learnt from: smokeyScraper
Repo: AOSSIE-Org/Devr.AI PR: 72
File: backend/app/agents/devrel/nodes/handle_web_search_node.py:31-42
Timestamp: 2025-06-08T13:31:11.572Z
Learning: In backend/app/agents/devrel/tools/search_tool.py, the TavilySearchTool.search() method has partial error handling for missing API key, AttributeError, ConnectionError, and TimeoutError, but lacks a comprehensive Exception catch-all block, so calling functions may still need additional error handling for other potential exceptions.
Applied to files:
backend/app/agents/devrel/tools/search_tool/ddg.py
🧬 Code graph analysis (1)
backend/app/agents/devrel/tools/search_tool/ddg.py (1)
backend/app/agents/devrel/tools/search_tool/tavilly.py (1)
search(17-52)
🪛 Ruff (0.14.10)
backend/app/agents/devrel/tools/search_tool/ddg.py
81-81: Consider moving this statement to an else block
(TRY300)
87-87: Standard pseudo-random generators are not suitable for cryptographic purposes
(S311)
95-95: Standard pseudo-random generators are not suitable for cryptographic purposes
(S311)
99-99: Do not catch blind exception: Exception
(BLE001)
101-101: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
101-101: Use explicit conversion flag
Replace with conversion flag
(RUF010)
103-103: Standard pseudo-random generators are not suitable for cryptographic purposes
(S311)
Aditya30ag
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’d suggest a simpler pattern (e.g., NotFound - debug, plus a single logger.exception for unexpected errors) to keep this readable and maintainable.
| except discord.Forbidden: | ||
| logger.warning(f"Cannot send DM to user {user.id} - DMs are disabled or bot is blocked") | ||
| except discord.HTTPException as e: | ||
| logger.error(f"Discord API error sending DM to user {user.id}: {e.status} - {e.text}") | ||
| except Exception as e: | ||
| logger.error(f"Unexpected error sending DM to user {user.id}: {type(e).__name__} - {str(e)}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
except discord.Forbidden:
logger.debug(f"Cannot send DM to user {user.id}")
except Exception:
logger.exception("Failed to send handoff DM")
| except discord.NotFound: | ||
| logger.warning(f"Message not found when editing onboarding view for user {interaction.user.id}") | ||
| except discord.HTTPException as e: | ||
| logger.error(f"Failed to edit onboarding view: {e.status} - {e.text}") | ||
| except Exception as e: | ||
| logger.error(f"Unexpected error editing onboarding view: {type(e).__name__} - {str(e)}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
except discord.NotFound:
logger.debug("Interaction message no longer exists")
except Exception:
logger.exception("Failed to update interaction view")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @backend/integrations/discord/views.py:
- Around line 107-110: The exception log uses an f-string prefix without
placeholders and lacks context; replace the line logger.exception(f"Unexpected
error editing onboarding view") with a contextual log that includes the user and
interaction identifiers (e.g., interaction.user.id and interaction.id or
channel_id) and keep logger.exception to capture the traceback—for example, use
logger.exception("Unexpected error editing onboarding view for user=%s
interaction=%s", interaction.user.id, getattr(interaction, "id", None)) or an
equivalent f-string that actually interpolates interaction.user.id and
interaction.id.
- Around line 125-128: In the generic except block in the skip-view edit
handler, capture the exception (change "except Exception:" to "except Exception
as e:"), remove the unnecessary f-string prefix or include placeholders, and log
useful context; e.g. call logger.exception with a message that includes
interaction.user.id and the exception (or use exc_info=True) so the log becomes
something like "Unexpected error editing skip view for user
{interaction.user.id}: {e}" ensuring the user id and error details are present.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/integrations/discord/views.py
🧰 Additional context used
🪛 Ruff (0.14.10)
backend/integrations/discord/views.py
110-110: f-string without any placeholders
Remove extraneous f prefix
(F541)
128-128: f-string without any placeholders
Remove extraneous f prefix
(F541)
🔇 Additional comments (2)
backend/integrations/discord/views.py (2)
1-1: LGTM! Standard logging setup.The module-level logger follows Python best practices and enables structured logging throughout the module.
Also applies to: 11-12
30-40: The code follows the established exception handling pattern used consistently throughout the Discord integration module. While the PR objectives mentiondiscord.HTTPException, the codebase does not implement explicit HTTPException handling anywhere. Instead, the pattern acrosscogs.pyandviews.pyis to catch specific Discord exceptions (likediscord.Forbiddenanddiscord.NotFound) followed by a genericExceptionhandler, which implicitly captures all other exceptions includingHTTPException. The current implementation insend_final_handoff_dmis correct and consistent with this approach.
Closes #213
📝 Description
This PR completes 7 incomplete implementations across the codebase by replacing empty
passstatements with production-ready code including proper error handling, logging, and retry mechanisms.🔧 Changes Made
event_bus.py): Added duplicate handler prevention and comprehensive logging for event registrationviews.py): Replaced generic exception handling with specific Discord exceptions (Forbidden, NotFound, HTTPException) and detailed error loggingddg.py): Implemented configurable timeout, retry logic with exponential backoff, and optional result cachingAll changes are backward compatible with no breaking changes to existing APIs.
📷 Screenshots or Visual Changes (if applicable)
N/A - Backend changes only, no visual changes.
🤝 Collaboration
N/A
✅ Checklist
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.