Improve handling of fatal exceptions (as showing the GUI, even on the main thread, can fail),

making sure that the error is furthermore logged or at least written to stderr
This commit is contained in:
Dominik Jain committed 2025-04-16 12:05:02 +02:00
1 parent 36f0103fa2
commit b6f3843e9f
3 files changed
+29 -7

No files matched your search

+25 -1
View File
@@ -6,6 +6,7 @@ import inspect
import json
import os
import platform
import sys
import traceback
from abc import ABC
from collections import defaultdict
@@ -16,6 +17,7 @@ from typing import TYPE_CHECKING, Any, Self, TypeVar, Union, cast
import yaml
from sensai.util import logging
from sensai.util.logging import FallbackHandler
from sensai.util.string import ToStringMixin, dict_string
from multilspy import SyncLanguageServer
@@ -40,6 +42,27 @@ TTool = TypeVar("TTool", bound="Tool")
SUCCESS_RESULT = "OK"
def show_fatal_exception_safe(e: Exception) -> None:
"""
Shows the given exception in the GUI log viewer on the main thread and ensures that the exception is logged or at
least printed to stderr.
"""
# Make sure the error is logged (adding a fallback handler which writes to stderr in case there is no other handler)
fallback_handler = FallbackHandler(logging.StreamHandler(sys.stderr))
Logger.root.addHandler(fallback_handler)
log.error(f"Fatal exception: {e}", exc_info=e)
# attempt to show the error in the GUI
try:
# NOTE: The import can fail on macOS if Tk is not available (depends on Python interpreter installation, which uv
# used as a base); while tkinter as such is always available, its dependencies can be unavailable on macOS.
from serena.gui_log_viewer import show_fatal_exception
show_fatal_exception(e)
except:
pass
class SerenaConfigError(Exception):
pass
@@ -171,7 +194,8 @@ class SerenaAgent:
if platform.system() == "Darwin":
log.warning("GUI log window is not supported on macOS")
else:
# even importing on macOS may fail since tkinter is not available by default
# even importing on macOS may fail if tkinter dependencies are unavailable (depends on Python interpreter installation
# which uv used as a base, unfortunately)
from serena.gui_log_viewer import GuiLogViewer, GuiLogViewerHandler
log_level = self.serena_config.gui_log_window_level
+2 -3
View File
@@ -15,8 +15,7 @@ from dotenv import load_dotenv
from sensai.util.logging import LogTime
from serena import serena_root_path
from serena.agent import SerenaAgent, Tool
from serena.gui_log_viewer import show_fatal_exception
from serena.agent import SerenaAgent, Tool, show_fatal_exception_safe
log = logging.getLogger(__name__)
@@ -92,7 +91,7 @@ class SerenaAgnoAgentProvider:
try:
serena_agent = SerenaAgent(project_file)
except Exception as e:
show_fatal_exception(e)
show_fatal_exception_safe(e)
raise
# Even though we don't want to keep history between sessions,
+2 -3
View File
@@ -15,8 +15,7 @@ from mcp.server.fastmcp.utilities.func_metadata import func_metadata
from sensai.util import logging
from sensai.util.helper import mark_used
from serena.agent import SerenaAgent, Tool
from serena.gui_log_viewer import show_fatal_exception
from serena.agent import SerenaAgent, Tool, show_fatal_exception_safe
log = logging.getLogger(__name__)
LOG_FORMAT = "%(levelname)-5s %(asctime)-15s %(name)s:%(funcName)s:%(lineno)d - %(message)s"
@@ -100,7 +99,7 @@ def create_mcp_server() -> FastMCP:
# project_activation_callback=update_tools
)
except Exception as e:
show_fatal_exception(e)
show_fatal_exception_safe(e)
raise
@asynccontextmanager