Skip to main content
Glama
error_events.py8.22 kB
import logging import sys import time import traceback from typing import Any, Dict, List, Optional try: from .base import EventProvider from .import_utils import safe_import_freecad_util freecad_safe_emit = safe_import_freecad_util('freecad_safe_emit') except ImportError: # Fallback for when module is loaded by FreeCAD import os addon_dir = os.path.dirname(os.path.dirname(__file__)) if addon_dir not in sys.path: sys.path.insert(0, addon_dir) from events.base import EventProvider from events.import_utils import safe_import_freecad_util freecad_safe_emit = safe_import_freecad_util('freecad_safe_emit') logger = logging.getLogger(__name__) class ErrorEventProvider(EventProvider): """Event provider for FreeCAD error events.""" def __init__(self, freecad_app=None, event_router=None, max_history_size=50, install_global_handler=False): """ Initialize the error event provider. Args: freecad_app: Optional FreeCAD application instance. If None, will try to import FreeCAD. event_router: Router for broadcasting events max_history_size: Maximum number of errors to keep in history install_global_handler: Whether to install global exception handler (dangerous, default False) """ super().__init__(max_history_size) self.app = freecad_app self.event_router = event_router self.error_history: List[Dict[str, Any]] = [] self.original_excepthook = None self._global_handler_installed = False if self.app is None: try: import FreeCAD self.app = FreeCAD logger.info("Connected to FreeCAD") except ImportError: logger.warning( "Could not import FreeCAD. Make sure it's installed and in your Python path." ) self.app = None # Only install global exception handler if explicitly requested if install_global_handler: self._install_global_exception_handler() self._setup_signal_handlers() def _install_global_exception_handler(self): """Install global exception handler if not already installed.""" if not self._global_handler_installed and hasattr(sys, 'excepthook'): self.original_excepthook = sys.excepthook sys.excepthook = self._global_exception_handler self._global_handler_installed = True logger.info("Installed global exception handler for error tracking") def _uninstall_global_exception_handler(self): """Restore original exception handler.""" if self._global_handler_installed and self.original_excepthook: sys.excepthook = self.original_excepthook self._global_handler_installed = False logger.info("Restored original exception handler") def _setup_signal_handlers(self): """Set up signal handlers for FreeCAD error events.""" if self.app is None: logger.warning("FreeCAD not available, cannot set up signal handlers") return try: # Connect to error signals if available if hasattr(self.app, "signalError"): self.app.signalError.connect(self._on_error) logger.info("Connected to FreeCAD error signal") # Try to connect to the Console observer for Python errors if hasattr(self.app, "Gui") and hasattr(self.app.Gui, "getMainWindow"): try: # Some FreeCAD versions provide a console viewer with error signals console = self.app.Gui.getMainWindow().findChild( "QTextEdit", "Report view" ) if console and hasattr(console, "signalError"): console.signalError.connect(self._on_error) logger.info("Connected to FreeCAD console error signal") except Exception as console_error: logger.debug(f"Could not connect to console error signal: {console_error}") except Exception as e: logger.error(f"Error setting up FreeCAD error signal handlers: {e}") def _on_error(self, error_msg): """Handle error event from FreeCAD signals.""" logger.error(f"FreeCAD error: {error_msg}") # Create event data event_data = { "type": "error", "message": error_msg, "timestamp": time.time(), "source": "freecad_signal", } # Record the error self._record_error(event_data) def _global_exception_handler(self, exc_type, exc_value, exc_traceback): """Handle unhandled exceptions globally.""" # Still call the original handler self.original_excepthook(exc_type, exc_value, exc_traceback) # Format the exception tb_str = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback)) # Create event data event_data = { "type": "error", "message": str(exc_value), "traceback": tb_str, "timestamp": time.time(), "source": "unhandled_exception", "exception_type": exc_type.__name__, } # Record the error self._record_error(event_data) def _record_error(self, event_data): """Record an error and emit an event.""" # Store in history (limit to configured max size) self.error_history.append(event_data) if len(self.error_history) > self.max_history_size: self.error_history.pop(0) # Emit event safely if freecad_safe_emit: freecad_safe_emit(self.emit_event, "error", event_data, "error_recorded") def get_error_history(self, limit: Optional[int] = None) -> List[Dict[str, Any]]: """ Get the error history. Args: limit: Optional limit on the number of history items to return Returns: List of error events """ if limit and 0 < limit < len(self.error_history): return self.error_history[-limit:] return self.error_history def report_error( self, error_message: str, error_type: str = "manual", details: Dict[str, Any] = None, ): """ Manually report an error. Args: error_message: Error message error_type: Type of error details: Additional error details """ error_data = { "type": "error", "message": error_message, "error_type": error_type, "timestamp": time.time(), "source": "manual_report", } if details: error_data.update(details) self._record_error(error_data) def get_status(self) -> Dict[str, Any]: """Get the current status of the error event provider.""" return { "freecad_available": self.app is not None, "global_handler_installed": self._global_handler_installed, "signals_connected": len(self._signal_connections), "error_history_size": len(self.error_history), "max_history_size": self.max_history_size, "is_shutdown": self._is_shutdown } async def shutdown(self) -> None: """Clean up resources when the provider is shutdown.""" # Restore original exception handler first self._uninstall_global_exception_handler() # Then call parent shutdown await super().shutdown() async def emit_event(self, event_type: str, event_data: Dict[str, Any]) -> None: """ Emit an event to all listeners. Args: event_type: The type of event event_data: The event data """ if self.event_router is not None: await self.event_router.broadcast_event(event_type, event_data) else: logger.warning(f"No event router available to broadcast {event_type} event")

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jango-blockchained/mcp-freecad'

If you have feedback or need assistance with the MCP directory API, please join our Discord server