summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Moskovcak <jmoskovc@redhat.com>2009-03-02 22:22:53 +0100
committerJiri Moskovcak <jmoskovc@redhat.com>2009-03-02 22:22:53 +0100
commit71ca2b43a199e347551eb8a9bb205bb644bc9171 (patch)
treed88fe2df4cfb94cc43974eceed23a4f81ee0d4c1
parent802635f2e07084f058cdde3057ba31a20345e94d (diff)
downloadabrt-71ca2b43a199e347551eb8a9bb205bb644bc9171.tar.gz
abrt-71ca2b43a199e347551eb8a9bb205bb644bc9171.tar.xz
abrt-71ca2b43a199e347551eb8a9bb205bb644bc9171.zip
async dbus method calls, added exception handler
-rw-r--r--src/Gui/CCDBusBackend.py10
-rw-r--r--src/Gui/CCMainWindow.py33
-rw-r--r--src/Gui/CCReporterDialog.py2
-rw-r--r--src/Gui/Makefile.am6
-rw-r--r--src/Gui/exception.py432
5 files changed, 469 insertions, 14 deletions
diff --git a/src/Gui/CCDBusBackend.py b/src/Gui/CCDBusBackend.py
index 0408df95..5199f4ac 100644
--- a/src/Gui/CCDBusBackend.py
+++ b/src/Gui/CCDBusBackend.py
@@ -26,7 +26,7 @@ class DBusManager(gobject.GObject):
# new crash notify
self.proxy.connect_to_signal("Crash",self.crash_cb,dbus_interface=CC_IFACE)
# BT extracting complete
- self.proxy.connect_to_signal("AnalyzeComplete",self.analyze_complete_cb,dbus_interface=CC_IFACE)
+ #self.proxy.connect_to_signal("AnalyzeComplete",self.analyze_complete_cb,dbus_interface=CC_IFACE)
else:
raise Exception("Proxy object doesn't exist!")
@@ -34,6 +34,10 @@ class DBusManager(gobject.GObject):
def disconnected(*args):
print "disconnect"
+ def error_handler(self,*args):
+ for arg in args:
+ print "error %s" % arg
+
def crash_cb(self,*args):
#FIXME "got another crash, gui should reload!"
#for arg in args:
@@ -60,7 +64,9 @@ class DBusManager(gobject.GObject):
def getReport(self, UUID):
try:
- return self.cc.CreateReport(UUID)
+ #return self.cc.CreateReport(UUID)
+ # let's try it async
+ self.cc.CreateReport(UUID, reply_handler=self.analyze_complete_cb, error_handler=self.error_handler)
except dbus.exceptions.DBusException, e:
raise Exception(e.message)
diff --git a/src/Gui/CCMainWindow.py b/src/Gui/CCMainWindow.py
index 4c35afa1..ef373edb 100644
--- a/src/Gui/CCMainWindow.py
+++ b/src/Gui/CCMainWindow.py
@@ -9,11 +9,14 @@ from CC_gui_functions import *
from CCDumpList import getDumpList, DumpList
from CCReporterDialog import ReporterDialog
from CCReport import Report
+from exception import installExceptionHandler, handleMyException
try:
import rpm
except:
rpm = None
+installExceptionHandler("cc-gui", "0.0.1")
+
class MainWindow():
def __init__(self):
self.theme = theme = gtk.icon_theme_get_default()
@@ -80,9 +83,15 @@ class MainWindow():
def hydrate(self):
self.dumpsListStore.clear()
self.rows = self.ccdaemon.getDumps()
- dumplist = getDumpList(self.ccdaemon, refresh=True)
+ try:
+ dumplist = getDumpList(self.ccdaemon, refresh=True)
+ except Exception, e:
+ gui_error_message("Error while loading the dumplist, please check if crash-catcher daemon is running\n %s" % e.message)
for entry in dumplist:
- icon = get_icon_for_package(self.theme,entry.getPackageName())
+ try:
+ icon = get_icon_for_package(self.theme,entry.getPackageName())
+ except:
+ icon = None
self.dumpsListStore.append([icon, entry.getPackage(),entry.getExecutable(), entry.getTime("%Y.%m.%d %H:%M:%S"),entry.getCount(), entry])
def filter_dumps(self, model, miter, data):
@@ -128,12 +137,20 @@ class MainWindow():
pass
#print "got another crash, refresh gui?"
- def on_analyze_complete_cb(self, daemon, UUID):
+ def on_analyze_complete_cb(self, daemon, report):
dumplist = getDumpList(self.ccdaemon)
- entry = dumplist.ddict[UUID]
- print "GUI: Analyze for package %s crash with UUID %s is complete" % (entry.Package, UUID)
+ #entry = dumplist.ddict[UUID]
+ # tady asi nedostanem UUID, ale vysledek nasi volane metody
+ #print report
+ #print "GUI: Analyze for package %s crash with UUID %s is complete" % (entry.Package, UUID)
print "We should refresh the UI ..."
-
+ if not report:
+ gui_error_message("Unable to get report! Debuginfo missing?")
+ return
+ report_dialog = ReporterDialog(report)
+ result = report_dialog.run()
+ if result:
+ self.ccdaemon.Report(result)
#ret = gui_question_dialog("GUI: Analyze for package %s crash with UUID %s is complete" % (entry.Package, UUID),self.window)
#if ret == gtk.RESPONSE_YES:
# self.hydrate()
@@ -156,13 +173,13 @@ class MainWindow():
# do this async and wait for yum to end with debuginfoinstal
gui_error_message("Error getting the report: %s" % e.message)
return
-
+ return
if not report:
gui_error_message("Unable to get report! Debuginfo missing?")
return
report_dialog = ReporterDialog(report)
result = report_dialog.run()
- if result == gtk.RESPONSE_APPLY:
+ if result:
self.ccdaemon.Report(result)
diff --git a/src/Gui/CCReporterDialog.py b/src/Gui/CCReporterDialog.py
index 91f6961e..cd55daf8 100644
--- a/src/Gui/CCReporterDialog.py
+++ b/src/Gui/CCReporterDialog.py
@@ -75,7 +75,7 @@ class ReporterDialog():
result = self.window.run()
if result == gtk.RESPONSE_CANCEL:
self.window.destroy()
- return result
+ return None
else:
self.window.destroy()
return self.report
diff --git a/src/Gui/Makefile.am b/src/Gui/Makefile.am
index eea12e0c..4162dcfc 100644
--- a/src/Gui/Makefile.am
+++ b/src/Gui/Makefile.am
@@ -2,11 +2,11 @@
bin_SCRIPTS = cc-gui
-PYTHON_FILES = CCDBusBackend.py CCDumpList.py CCDump.py CC_gui_functions.py ccgui.glade report.glade CCReporterDialog.py CCReport.py CCMainWindow.py
+PYTHON_FILES = CCDBusBackend.py CCDumpList.py CCDump.py CC_gui_functions.py ccgui.glade report.glade CCReporterDialog.py CCReport.py CCMainWindow.py exception.py
-GLADE_FILES = ccgui.glade
+GLADE_FILES = ccgui.glade report.glade
-EXTRA_DIST = $(PYTHON_FILES) $(GLADE_FILES) cc-gui
+EXTRA_DIST = $(PYTHON_FILES) $(GLADE_FILES) cc-gui crash-catcher.desktop
#ccguidir = $(pkgdatadir)
pkgdata_PYTHON = $(PYTHON_FILES)
diff --git a/src/Gui/exception.py b/src/Gui/exception.py
new file mode 100644
index 00000000..fa9a3048
--- /dev/null
+++ b/src/Gui/exception.py
@@ -0,0 +1,432 @@
+# -*- coding: utf-8 -*-
+## Copyright (C) 2001-2005 Red Hat, Inc.
+## Copyright (C) 2001-2005 Harald Hoyer <harald@redhat.com>
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+"""
+Module for a userfriendly exception handling
+
+Example code:
+
+import sys
+
+from exception import action, error, exitcode, installExceptionHandler
+
+installExceptionHandler("test", "1.0", gui=0, debug=0)
+
+def exception_function():
+ action("Trying to divide by zero")
+
+ try:
+ local_var_1 = 1
+ local_var_2 = 0
+ # test exception raised to show the effect
+ local_var_3 = local_var_1 / local_var_2
+ except:
+ error("Does not seem to work!? :-)")
+ exitcode(15)
+ raise
+
+"""
+import sys
+
+from rhpl.translate import _
+
+
+__DUMPHASH = {}
+# FIXME: do length limits on obj dumps.
+def __dump_class(instance, fd, level=0):
+ "dumps all classes"
+ import types
+ # protect from loops
+ if not __DUMPHASH.has_key(instance):
+ __DUMPHASH[instance] = True
+ else:
+ fd.write("Already dumped\n")
+ return
+ if (instance.__class__.__dict__.has_key("__str__") or
+ instance.__class__.__dict__.has_key("__repr__")):
+ fd.write("%s\n" % (instance,))
+ return
+ fd.write("%s instance, containing members:\n" %
+ (instance.__class__.__name__))
+ pad = ' ' * ((level) * 2)
+ for key, value in instance.__dict__.items():
+ if type(value) == types.ListType:
+ fd.write("%s%s: [" % (pad, key))
+ first = 1
+ for item in value:
+ if not first:
+ fd.write(", ")
+ else:
+ first = 0
+ if type(item) == types.InstanceType:
+ __dump_class(item, fd, level + 1)
+ else:
+ fd.write("%s" % (item,))
+ fd.write("]\n")
+ elif type(value) == types.DictType:
+ fd.write("%s%s: {" % (pad, key))
+ first = 1
+ for k, v in value.items():
+ if not first:
+ fd.write(", ")
+ else:
+ first = 0
+ if type(k) == types.StringType:
+ fd.write("'%s': " % (k,))
+ else:
+ fd.write("%s: " % (k,))
+ if type(v) == types.InstanceType:
+ __dump_class(v, fd, level + 1)
+ else:
+ fd.write("%s" % (v,))
+ fd.write("}\n")
+ elif type(value) == types.InstanceType:
+ fd.write("%s%s: " % (pad, key))
+ __dump_class(value, fd, level + 1)
+ else:
+ fd.write("%s%s: %s\n" % (pad, key, value))
+
+def __dump_exception(out, text, tracebk):
+ 'write a traceback to "out"'
+ #from cPickle import Pickler
+ #p = Pickler(out)
+
+ out.write(text)
+
+ trace = tracebk
+ while trace.tb_next:
+ trace = trace.tb_next
+ frame = trace.tb_frame
+ out.write ("\nLocal variables in innermost frame:\n")
+ try:
+ for (key, value) in frame.f_locals.items():
+ out.write ("%s: %s\n" % (key, value))
+ except:
+ pass
+
+
+def __exception_window(title, text, component_name):
+ "Creates a dialog and displays the exception"
+ import gtk
+ win = gtk.Dialog(title, None, gtk.DIALOG_MODAL)
+ win.add_button(_("_Debug"), 1)
+ win.add_button(_("_Save to file"), 2)
+ win.add_button(gtk.STOCK_QUIT, 0)
+ win.set_border_width(6)
+ mbuffer = gtk.TextBuffer(None)
+ mbuffer.set_text(text)
+ textbox = gtk.TextView()
+ textbox.set_buffer(mbuffer)
+ textbox.set_property("editable", False)
+ textbox.set_property("cursor_visible", False)
+ scw = gtk.ScrolledWindow ()
+ scw.set_shadow_type(gtk.SHADOW_IN)
+ scw.add (textbox)
+ scw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ hbox = gtk.HBox (False)
+ hbox.set_border_width(6)
+ txt = _("An unhandled exception has occurred. This "
+ "is most likely a bug. Please save the crash "
+ "dump and file a detailed bug "
+ "report against %s at "
+ "https://bugzilla.redhat.com/bugzilla") % \
+ component_name
+ info = gtk.Label(txt)
+ info.set_line_wrap(True)
+ hbox.pack_start (scw, True)
+ # pylint: disable-msg=E1101
+ win.vbox.pack_start (info, False)
+ win.vbox.pack_start (hbox, True)
+ win.vbox.set_border_width(12)
+ win.vbox.set_spacing(12)
+ win.set_size_request (500, 300)
+ win.set_position (gtk.WIN_POS_CENTER)
+ #contents = win.get_children()[0]
+ win.show_all ()
+ rc = win.run ()
+ win.destroy()
+ return rc
+
+
+def _generic_error_dialog (title, message, parent_dialog,
+ message_type=None,
+ widget=None, page=0, broken_widget=None):
+ import gtk
+
+ if message_type == None:
+ message_type = gtk.MESSAGE_ERROR
+
+ dialog = gtk.MessageDialog(parent_dialog,
+ gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+ message_type, gtk.BUTTONS_OK,
+ message)
+ dialog.set_title(title)
+
+ if widget != None:
+ if isinstance (widget, gtk.CList):
+ widget.select_row (page, 0)
+ elif isinstance (widget, gtk.Notebook):
+ widget.set_current_page (page)
+ if broken_widget != None:
+ broken_widget.grab_focus ()
+ if isinstance (broken_widget, gtk.Entry):
+ broken_widget.select_region (0, -1)
+
+ if parent_dialog:
+ dialog.set_position (gtk.WIN_POS_CENTER_ON_PARENT)
+ dialog.set_transient_for(parent_dialog)
+ else:
+ dialog.set_position (gtk.WIN_POS_CENTER)
+
+ ret = dialog.run ()
+ dialog.destroy()
+ return ret
+
+__ACTION_STR = ""
+def action(what):
+ """Describe what you want to do actually.
+ what - string
+ """
+ global __ACTION_STR # pylint: disable-msg=W0603
+ __ACTION_STR = what
+
+__ERROR_STR = ""
+def error(what):
+ """Describe what went wrong with a userfriendly text.
+ what - string
+ """
+ global __ERROR_STR # pylint: disable-msg=W0603
+ __ERROR_STR = what
+
+__EXITCODE = 10
+def exitcode(num):
+ """The exitcode, with which the exception handling routine should call
+ sys.exit().
+ num - int(exitcode)
+ """
+ global __EXITCODE # pylint: disable-msg=W0603
+ __EXITCODE = int(num)
+
+#
+# handleMyException function
+#
+def handleMyException((etype, value, tb), progname, version,
+ gui = 1, debug = 1):
+ """
+ The exception handling function.
+
+ progname - the name of the application
+ version - the version of the application
+ gui - display a gtk dialog (0, 1) to show the error message
+ debug - show the full traceback (with "Save to file" in GUI)
+ """
+ if not debug:
+ if not gui:
+ print _("Error: %s: %s") % (__ACTION_STR, __ERROR_STR)
+ else:
+ import gtk
+ text = _("%s\n\n%s:\n%s") % (progname, __ACTION_STR, __ERROR_STR)
+ _generic_error_dialog(progname, text, None)
+
+ sys.exit(__EXITCODE)
+
+ # restore original exception handler
+ sys.excepthook = sys.__excepthook__ # pylint: disable-msg=E1101
+
+ import os.path
+ import md5
+ import traceback
+
+ elist = traceback.format_exception (etype, value, tb)
+ tblast = traceback.extract_tb(tb, limit=None)
+ if len(tblast):
+ tblast = tblast[len(tblast)-1]
+ extxt = traceback.format_exception_only(etype, value)
+ if progname:
+ text = "Component: %s\n" % progname
+ if version:
+ text = text + "Version: %s\n" % version
+ text = text + "Summary: TB"
+ if tblast and len(tblast) > 3:
+ ll = []
+ ll.extend(tblast[:3])
+ ll[0] = os.path.basename(tblast[0])
+ tblast = ll
+
+ m = md5.new()
+ ntext = ""
+ for t in tblast:
+ ntext += str(t) + ":"
+ m.update(str(t))
+
+
+ text += str(m.hexdigest())[:8] + " " + ntext
+
+ text += extxt[0]
+ text += "\n"
+ text += "".join(elist)
+
+ trace = tb
+ while trace.tb_next:
+ trace = trace.tb_next
+ frame = trace.tb_frame
+ text += ("\nLocal variables in innermost frame:\n")
+ try:
+ for (key, value) in frame.f_locals.items():
+ text += "%s: %s\n" % (key, value)
+ except:
+ pass
+
+ if not gui:
+ print text
+ sys.exit(__EXITCODE)
+
+ import gtk # pylint: disable-msg=W0404
+ if not debug:
+ _generic_error_dialog(progname, text, None)
+ sys.exit(__EXITCODE)
+
+ while 1:
+ rc = __exception_window (_("%(progname)s - Exception Occurred") \
+ % {'progname' : progname},
+ text, progname)
+ print text
+
+ if rc == 1 and tb:
+ import pdb
+ import signal
+ pdb.post_mortem (tb)
+ os.kill(os.getpid(), signal.SIGKILL)
+ elif not rc:
+ sys.exit(__EXITCODE)
+ else:
+ d = gtk.FileChooserDialog(_("Specify a file to save the dump"),
+ None, gtk.FILE_CHOOSER_ACTION_SAVE,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+ d.set_default_response(gtk.RESPONSE_OK)
+ rc = d.run()
+ if rc == gtk.RESPONSE_OK:
+ tfile = d.get_filename()
+ d.destroy()
+
+ if not tfile:
+ tfile = "/tmp/dump"
+
+ try:
+ out = open(tfile, "w")
+ out.write(text)
+ out.close()
+
+ except IOError:
+ _generic_error_dialog(progname,
+ _("Failed to write to file %s.") \
+ % (tfile), None)
+ else:
+ _generic_error_dialog(progname,
+ _("The application's state has been successfully\n"
+ "written to the file '%s'.") % (tfile), None,
+ message_type = "info")
+ sys.exit(__EXITCODE)
+ else:
+ d.destroy()
+ continue
+
+ sys.exit(__EXITCODE)
+
+def installExceptionHandler(progname, version, gui = 1, debug = 1):
+ """
+ Install the exception handling function.
+
+ progname - the name of the application
+ version - the version of the application
+ gui - display a gtk dialog (0, 1) to show the error message
+ debug - show the full traceback (with "Save to file" in GUI)
+ """
+ sys.excepthook = lambda etype, value, tb: \
+ handleMyException((etype, value, tb),
+ progname, version, gui, debug)
+
+if __name__ == '__main__':
+ def _exception_function():
+ action("Trying to divide by zero")
+
+ try:
+ local_var_1 = 1
+ local_var_2 = 0
+ # test exception raised to show the effect
+ local_var_3 = local_var_1 / local_var_2 # pylint: disable-msg=W0612
+ except:
+ error("Does not seem to work!? :-)")
+ exitcode(15)
+ raise
+
+ def _usage():
+ print """%s [-dgh] [--debug] [--gui] [--help]
+ -d, --debug
+ Show the whole backtrace
+
+ -g, --gui
+ Display a gtk error dialog
+
+ -h, --help
+ Display this message""" % (sys.argv[0])
+
+ import getopt
+ __debug = 1
+ __gui = 0
+
+ installExceptionHandler("test", "1.0", __gui, __debug)
+
+ __debug = 0
+
+ class BadUsage(Exception):
+ "exception for a bad command line usage"
+
+ try:
+ __opts, __args = getopt.getopt(sys.argv[1:], "dgh",
+ [
+ "debug",
+ "help",
+ "gui",
+ ])
+
+ for __opt, __val in __opts:
+ if __opt == '-d' or __opt == '--debug':
+ __debug = 1
+ continue
+
+ if __opt == '-g' or __opt == '--gui':
+ __gui = 1
+ continue
+
+ if __opt == '-h' or __opt == '--help':
+ _usage()
+ sys.exit(0)
+
+ except (getopt.error, BadUsage):
+ _usage()
+ sys.exit(1)
+
+ installExceptionHandler("test", "1.0", __gui, __debug)
+
+ _exception_function()
+ sys.exit(0)
+
+
+__author__ = "Harald Hoyer <harald@redhat.com>"