diff options
-rw-r--r-- | slack/commands.py | 17 | ||||
-rw-r--r-- | slack/error.py | 41 | ||||
-rw-r--r-- | slack/python_compatibility.py | 4 | ||||
-rw-r--r-- | slack/slack_workspace.py | 35 |
4 files changed, 73 insertions, 24 deletions
diff --git a/slack/commands.py b/slack/commands.py index 5e6e40c..69b5952 100644 --- a/slack/commands.py +++ b/slack/commands.py @@ -1,5 +1,6 @@ from __future__ import annotations +import json import pprint import re from dataclasses import dataclass @@ -8,7 +9,7 @@ from typing import Callable, Dict, List, Optional, Tuple import weechat -from slack.error import UncaughtError +from slack.error import SlackRtmError, UncaughtError from slack.log import print_error from slack.python_compatibility import format_exception, removeprefix, removesuffix from slack.shared import shared @@ -262,11 +263,19 @@ def command_slack_workspace_del( ) -def print_uncaught_error(error: UncaughtError, detailed: bool = False): +def print_uncaught_error( + error: UncaughtError, detailed: bool, options: Dict[str, Optional[str]] +): weechat.prnt("", f" {error.id} ({error.time}): {error.exception}") if detailed: for line in format_exception(error.exception): weechat.prnt("", f" {line}") + data = options.get("data", False) is None + if data: + if isinstance(error.exception, SlackRtmError): + weechat.prnt("", f" data: {json.dumps(error.exception.message_json)}") + else: + print_error("This error does not have any data") @weechat_command("tasks|buffer|errors|error", split_all_args=True) @@ -287,7 +296,7 @@ def command_slack_debug( num = min(num_arg, len(shared.uncaught_errors)) weechat.prnt("", f"Last {num} errors:") for error in shared.uncaught_errors[-num:]: - print_uncaught_error(error) + print_uncaught_error(error, False, options) elif args[0] == "error": if len(args) > 1: if args[1].isdecimal() and args[1] != "0": @@ -311,7 +320,7 @@ def command_slack_debug( else: error = shared.uncaught_errors[-1] weechat.prnt("", "Last error:") - print_uncaught_error(error, True) + print_uncaught_error(error, True, options) def completion_slack_workspaces_cb( diff --git a/slack/error.py b/slack/error.py index 8817759..c978584 100644 --- a/slack/error.py +++ b/slack/error.py @@ -5,10 +5,12 @@ from datetime import datetime from typing import TYPE_CHECKING, Dict, Mapping, Sequence, Union from uuid import uuid4 +from slack.python_compatibility import format_exception_only from slack.shared import shared if TYPE_CHECKING: from slack_api.slack_common import SlackErrorResponse + from slack_rtm.slack_rtm_message import SlackRtmMessage from slack.slack_workspace import SlackWorkspace @@ -51,6 +53,22 @@ class SlackApiError(Exception): self.response = response +class SlackRtmError(Exception): + def __init__( + self, + workspace: SlackWorkspace, + exception: BaseException, + message_json: SlackRtmMessage, + ): + super().__init__( + f"{self.__class__.__name__}: workspace={workspace}, exception=`{format_exception_only_str(exception)}`" + ) + super().with_traceback(exception.__traceback__) + self.workspace = workspace + self.exception = exception + self.message_json = message_json + + class SlackError(Exception): def __init__(self, workspace: SlackWorkspace, error: str): super().__init__( @@ -70,22 +88,35 @@ class UncaughtError: self.time = datetime.now() +def format_exception_only_str(exc: BaseException) -> str: + return format_exception_only(exc)[-1].strip() + + def store_and_format_exception(e: BaseException): uncaught_error = UncaughtError(e) shared.uncaught_errors.append(uncaught_error) - stack_msg = f"(run /slack debug error {uncaught_error.id} for stack trace)" + stack_msg_command = f"/slack debug error {uncaught_error.id}" + stack_msg = f"run `{stack_msg_command}` for the stack trace" if isinstance(e, HttpError): return ( f"Error calling URL {e.url}: return code: {e.return_code}, " - f"http status code: {e.http_status_code}, error: {e.error} {stack_msg}" + f"http status code: {e.http_status_code}, error: {e.error} ({stack_msg})" ) elif isinstance(e, SlackApiError): return ( f"Error from Slack API method {e.method} with params {e.params} for workspace " - f"{e.workspace.name}: {e.response} {stack_msg}" + f"{e.workspace.name}: {e.response} ({stack_msg})" + ) + elif isinstance(e, SlackRtmError): + return ( + f"Error while handling Slack event of type '{e.message_json['type']}' for workspace " + f"{e.workspace.name}: {format_exception_only_str(e.exception)} ({stack_msg}, " + f"run `{stack_msg_command} -data` for the event data)" ) elif isinstance(e, SlackError): - return f"Error occurred in workspace {e.workspace.name}: {e.error} {stack_msg}" + return ( + f"Error occurred in workspace {e.workspace.name}: {e.error} ({stack_msg})" + ) else: - return f"Unknown error occurred: {e.__class__.__name__}: {e} {stack_msg}" + return f"Unknown error occurred: {format_exception_only_str(e)} ({stack_msg})" diff --git a/slack/python_compatibility.py b/slack/python_compatibility.py index e96b8af..8aa8a84 100644 --- a/slack/python_compatibility.py +++ b/slack/python_compatibility.py @@ -18,5 +18,9 @@ def removesuffix(self: str, suffix: str) -> str: return self[:] +def format_exception_only(exc: BaseException) -> List[str]: + return traceback.format_exception_only(type(exc), exc) + + def format_exception(exc: BaseException) -> List[str]: return traceback.format_exception(type(exc), exc, exc.__traceback__) diff --git a/slack/slack_workspace.py b/slack/slack_workspace.py index 638a17b..51e4a89 100644 --- a/slack/slack_workspace.py +++ b/slack/slack_workspace.py @@ -15,7 +15,8 @@ from websocket import ( create_connection, ) -from slack.error import SlackError +from slack.error import SlackError, SlackRtmError, store_and_format_exception +from slack.log import print_error from slack.proxy import Proxy from slack.shared import shared from slack.slack_api import SlackApi @@ -262,21 +263,25 @@ class SlackWorkspace: run_async(self._ws_recv(json.loads(recv_data.decode()))) async def _ws_recv(self, data: SlackRtmMessage): - if data["type"] == "message": - if "subtype" in data and data["subtype"] == "message_changed": - pass - elif "subtype" in data and data["subtype"] == "message_deleted": - pass - elif "subtype" in data and data["subtype"] == "message_replied": - pass + try: + if data["type"] == "message": + if "subtype" in data and data["subtype"] == "message_changed": + pass + elif "subtype" in data and data["subtype"] == "message_deleted": + pass + elif "subtype" in data and data["subtype"] == "message_replied": + pass + else: + channel_id = data["channel"] + if channel_id in self.open_conversations: + channel = self.open_conversations[channel_id] + message = SlackMessage(channel, data) + await channel.add_message(message) else: - channel_id = data["channel"] - if channel_id in self.open_conversations: - channel = self.open_conversations[channel_id] - message = SlackMessage(channel, data) - await channel.add_message(message) - else: - weechat.prnt("", f"\t{self.name} received: {json.dumps(data)}") + weechat.prnt("", f"\t{self.name} received: {json.dumps(data)}") + except Exception as e: + slack_error = SlackRtmError(self, e, data) + print_error(store_and_format_exception(slack_error)) def ping(self): if not self.is_connected: |