From 855532fd7f41366b988d99a7e725116bc0a0a546 Mon Sep 17 00:00:00 2001 From: Katelyn Gigante Date: Thu, 28 Jul 2022 22:04:58 +1000 Subject: [PATCH 1/3] feat: Convert `client.on_error` into a proper event --- naff/api/events/internal.py | 14 ++++++++++++-- naff/client/auto_shard_client.py | 2 +- naff/client/client.py | 30 ++++++++++++++++++++---------- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/naff/api/events/internal.py b/naff/api/events/internal.py index 8b41dd85e..e5dc2d996 100644 --- a/naff/api/events/internal.py +++ b/naff/api/events/internal.py @@ -21,7 +21,7 @@ def on_guild_join(event): """ import re -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Optional from naff.client.const import MISSING from naff.models.discord.snowflake import to_snowflake @@ -33,6 +33,7 @@ def on_guild_join(event): "Component", "Connect", "Disconnect", + "Error", "ShardConnect", "ShardDisconnect", "GuildEvent", @@ -47,7 +48,7 @@ def on_guild_join(event): if TYPE_CHECKING: from naff import Client - from naff.models.naff.context import ComponentContext + from naff.models.naff.context import ComponentContext, Context from naff.models.discord.snowflake import Snowflake_Type from naff.models.discord.guild import Guild @@ -161,3 +162,12 @@ class Button(Component): @define(kw_only=False) class Select(Component): """Dispatched when a user uses a Select.""" + +@define(kw_only=False) +class Error(BaseEvent): + """Dispatched when the library encounters an error.""" + source: str + error: Exception + args: tuple[Any] = field(factory=tuple) + kwargs: dict[str, Any] = field(factory=dict) + ctx: Optional["Context"] = field(default=None) diff --git a/naff/client/auto_shard_client.py b/naff/client/auto_shard_client.py index 3841236c0..9af6d6039 100644 --- a/naff/client/auto_shard_client.py +++ b/naff/client/auto_shard_client.py @@ -149,7 +149,7 @@ async def _on_websocket_ready(self, event: events.RawGatewayEvent) -> None: try: await asyncio.gather(*self.async_startup_tasks) except Exception as e: - await self.on_error("async-extension-loader", e) + self.dispatch(events.Error("async-extension-loader", e)) # cache slash commands if not self._startup: diff --git a/naff/client/client.py b/naff/client/client.py index ec59de852..bebcd2ca4 100644 --- a/naff/client/client.py +++ b/naff/client/client.py @@ -460,7 +460,11 @@ async def _async_wrap(_coro: Listener, _event: BaseEvent, *_args, **_kwargs) -> except asyncio.CancelledError: pass except Exception as e: - await self.on_error(event, e) + if isinstance(event, events.Error): + # No infinite loops please + self.default_error_handler(repr(event), e) + else: + self.dispatch(events.Error(repr(event), e)) wrapped = _async_wrap(coro, event, *args, **kwargs) @@ -490,6 +494,10 @@ def default_error_handler(source: str, error: BaseException) -> None: "Ignoring exception in {}:{}{}".format(source, "\n" if len(out) > 1 else " ", "".join(out)), ) + @Listener.create() + async def _on_error(self, event: events.Error) -> None: + self.on_error(event.source, event.error, *event.args, **event.kwargs) + async def on_error(self, source: str, error: Exception, *args, **kwargs) -> None: """ Catches all errors dispatched by the library. @@ -510,7 +518,7 @@ async def on_command_error(self, ctx: Context, error: Exception, *args, **kwargs Override this to change error handling behavior """ - await self.on_error(f"cmd /`{ctx.invoke_target}`", error, *args, **kwargs) + self.dispatch(events.Error(f"cmd /`{ctx.invoke_target}`", error, args, kwargs, ctx)) try: if isinstance(error, errors.CommandOnCooldown): await ctx.send( @@ -537,7 +545,8 @@ async def on_command_error(self, ctx: Context, error: Exception, *args, **kwargs ) elif self.send_command_tracebacks: out = "".join(traceback.format_exception(error)) - out = out.replace(self.http.token, "[REDACTED TOKEN]") + if self.http.token is not None: + out = out.replace(self.http.token, "[REDACTED TOKEN]") await ctx.send( embeds=Embed( title=f"Error: {type(error).__name__}", @@ -575,7 +584,7 @@ async def on_component_error(self, ctx: ComponentContext, error: Exception, *arg Override this to change error handling behavior """ - return await self.on_error(f"Component Callback for {ctx.custom_id}", error, *args, **kwargs) + return self.dispatch(events.Error(f"Component Callback for {ctx.custom_id}", error, args, kwargs, ctx)) async def on_component(self, ctx: ComponentContext) -> None: """ @@ -599,12 +608,13 @@ async def on_autocomplete_error(self, ctx: AutocompleteContext, error: Exception Override this to change error handling behavior """ - return await self.on_error( + return self.dispatch(events.Error( f"Autocomplete Callback for /{ctx.invoke_target} - Option: {ctx.focussed_option}", error, - *args, - **kwargs, - ) + args, + kwargs, + ctx + )) async def on_autocomplete(self, ctx: AutocompleteContext) -> None: """ @@ -667,7 +677,7 @@ async def _on_websocket_ready(self, event: events.RawGatewayEvent) -> None: try: await asyncio.gather(*self.async_startup_tasks) except Exception as e: - await self.on_error("async-extension-loader", e) + self.dispatch(events.Error("async-extension-loader", e)) # cache slash commands if not self._startup: @@ -1081,7 +1091,7 @@ async def _init_interactions(self) -> None: else: await self._cache_interactions(warn_missing=False) except Exception as e: - await self.on_error("Interaction Syncing", e) + self.dispatch(events.Error("Interaction Syncing", e)) async def _cache_interactions(self, warn_missing: bool = False) -> None: """Get all interactions used by this bot and cache them.""" From b58e3c320e6f4635eaf31be8f5584101b614df35 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 28 Jul 2022 12:40:28 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- naff/api/events/internal.py | 2 ++ naff/client/client.py | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/naff/api/events/internal.py b/naff/api/events/internal.py index e5dc2d996..d3ce975f0 100644 --- a/naff/api/events/internal.py +++ b/naff/api/events/internal.py @@ -163,9 +163,11 @@ class Button(Component): class Select(Component): """Dispatched when a user uses a Select.""" + @define(kw_only=False) class Error(BaseEvent): """Dispatched when the library encounters an error.""" + source: str error: Exception args: tuple[Any] = field(factory=tuple) diff --git a/naff/client/client.py b/naff/client/client.py index bebcd2ca4..876556bac 100644 --- a/naff/client/client.py +++ b/naff/client/client.py @@ -460,7 +460,7 @@ async def _async_wrap(_coro: Listener, _event: BaseEvent, *_args, **_kwargs) -> except asyncio.CancelledError: pass except Exception as e: - if isinstance(event, events.Error): + if isinstance(event, events.Error): # No infinite loops please self.default_error_handler(repr(event), e) else: @@ -608,13 +608,15 @@ async def on_autocomplete_error(self, ctx: AutocompleteContext, error: Exception Override this to change error handling behavior """ - return self.dispatch(events.Error( - f"Autocomplete Callback for /{ctx.invoke_target} - Option: {ctx.focussed_option}", - error, - args, - kwargs, - ctx - )) + return self.dispatch( + events.Error( + f"Autocomplete Callback for /{ctx.invoke_target} - Option: {ctx.focussed_option}", + error, + args, + kwargs, + ctx, + ) + ) async def on_autocomplete(self, ctx: AutocompleteContext) -> None: """ From 3c7cd1f90c17d0bed36f0747e6b10b69f3d7f5ae Mon Sep 17 00:00:00 2001 From: Katelyn Gigante Date: Sun, 31 Jul 2022 16:25:36 +1000 Subject: [PATCH 3/3] Document fields --- naff/api/events/internal.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/naff/api/events/internal.py b/naff/api/events/internal.py index d3ce975f0..4c16aa5e3 100644 --- a/naff/api/events/internal.py +++ b/naff/api/events/internal.py @@ -168,8 +168,8 @@ class Select(Component): class Error(BaseEvent): """Dispatched when the library encounters an error.""" - source: str - error: Exception + source: str = field(metadata=docs("The source of the error")) + error: Exception = field(metadata=docs("The error that was encountered")) args: tuple[Any] = field(factory=tuple) kwargs: dict[str, Any] = field(factory=dict) - ctx: Optional["Context"] = field(default=None) + ctx: Optional["Context"] = field(default=None, metadata=docs("The Context, if one was active"))