""" Module for handling errors and presenting them as dialogs. """ import __builtin__ import sys import traceback from ErrorFormatter import ErrorFormatter import dialog class UserError(StandardError): """ Exception for user-visible errors. """ def __init__(self, error, description, code = None, lineno = -1, show_details = True): self.error = error self.description = description if (code): self.code = code if (lineno != -1): self.lineno = lineno self.show_details = show_details StandardError.__init__(self) class _Error: def __init__(self): # table for mapping IDs to the .display files self.__ident_map = {"unknown": ""} # table for mapping filenames to code pieces self.__code_map = {} def register(self, ident, path): """ Registers the given ID with the given path. This is needed so that the dialog can point to the broken desklet. """ self.__ident_map[ident] = path def forget(self, ident): """ Forgets about the given ID. """ del self.__ident_map[ident] dialog.forget(ident) def register_code(self, filename, code): """ Registers a piece of code with a "filename". This is needed for code which is not accessible as a file. The filename is a pseudo-filename, not a real one. """ self.__code_map[filename] = code def __dig_down(self, tb): """ Digs through the traceback looking for the place which is interesting for the exception, which is not necessarily where it was thrown. Returns a tuple (filename, lineno). """ tbs = traceback.extract_tb(tb) filename = None lineno = -1 found = False while (True and tbs): trace = tbs.pop() filename = trace[0] lineno = trace[1] if (filename.startswith("<")): found = True break del tbs if (found): return (filename, lineno) else: return (None, -1) def handle(self, ident): """ Handles the recently caught error and associates it with the given display ID. """ exc_type, exc_value, tb = sys.exc_info() dspfile = self.__ident_map[ident] filename = None lineno = -1 code = None # get some information from the exception if (hasattr(exc_value, "lineno")): lineno = exc_value.lineno if (hasattr(exc_value, "filename")): filename = exc_value.filename if (hasattr(exc_value, "code")): code = exc_value.code if (hasattr(exc_value, "show_details")): show_details = exc_value.show_details else: show_details = True # find the interesting piece of code if (not filename and lineno == -1): filename, lineno = self.__dig_down(tb) del tb if (not code): code = self.__code_map.get(filename) if (filename): if (filename.startswith("<")): index1 = filename.find("'") index2 = filename.rfind("_") filename = filename[index1 + 1:index2] # hilight bad code if (show_details): details = ErrorFormatter().format(sys.exc_info(), code, lineno, filename = filename) else: details = "" # present exception to the user if (hasattr(exc_value, "error") and hasattr(exc_value, "description")): log("=== " + exc_value.error + "\n" + details) dialog.user_error(ident, exc_value.error + "\n%s\n\n" % dspfile, exc_value.description, details) else: log("=== Runtime Error\n" + details) dialog.user_error(ident, _("Runtime Error") + "\n%s\n\n" % dspfile, _("An error occurred while executing a desklet."), details) # make UserError globally available __builtin__.UserError = UserError _singleton = _Error() def Error(): return _singleton