Index: ChangeLog =================================================================== RCS file: /cvs/gnome/gnome-python/pygtk/ChangeLog,v retrieving revision 1.1182 diff -u -p -r1.1182 ChangeLog --- ChangeLog 2 May 2005 23:38:48 -0000 1.1182 +++ ChangeLog 9 May 2005 17:06:14 -0000 @@ -0,0 +1,14 @@ +2005-05-09 Mark McLoughlin + + Fix for bug #303573 - "exceptions raised in a child watch + handler are silently swallowed" + + * gobject/gobjectmodule.c: + (child_watch_func), + (_pyg_spawn_async_callback): if PyObject_CallFunction() + returns NULL, print a traceback of the exception. + + * tests/test_mainloop.py: add testcase. + + * tests/Makefile.am: add test_mainloop.py + Index: gobject/gobjectmodule.c =================================================================== RCS file: /cvs/gnome/gnome-python/pygtk/gobject/gobjectmodule.c,v retrieving revision 1.162 diff -u -p -r1.162 gobjectmodule.c --- gobject/gobjectmodule.c 22 Jan 2005 17:45:46 -0000 1.162 +++ gobject/gobjectmodule.c 9 May 2005 17:06:15 -0000 @@ -1832,7 +1832,12 @@ child_watch_func(GPid pid, gint status, child_data->data); else retval = PyObject_CallFunction(child_data->func, "ii", pid, status); - Py_XDECREF(retval); + + if (retval) + Py_DECREF(retval); + else + PyErr_Print(); + pyg_gil_state_release(gil); } @@ -1894,7 +1899,10 @@ _pyg_spawn_async_callback(gpointer user_ retval = PyObject_CallFunction(data->func, "O", data->data); else retval = PyObject_CallFunction(data->func, NULL); - Py_XDECREF(retval); + if (retval) + Py_DECREF(retval); + else + PyErr_Print(); Py_DECREF(data->func); Py_XDECREF(data->data); g_free(data); Index: tests/Makefile.am =================================================================== RCS file: /cvs/gnome/gnome-python/pygtk/tests/Makefile.am,v retrieving revision 1.16 diff -u -p -r1.16 Makefile.am --- tests/Makefile.am 2 May 2005 23:38:49 -0000 1.16 +++ tests/Makefile.am 9 May 2005 17:06:15 -0000 @@ -28,7 +28,8 @@ tests = \ test_subprocess.py \ test_subtype.py \ test_gdkevent.py \ - test_unknown.py + test_unknown.py \ + test_mainloop.py # This is a hack to make sure a shared library is built testhelper.la: $(testhelper_la_OBJECTS) $(testhelper_la_DEPENDENCIES) Index: tests/test_mainloop.py =================================================================== RCS file: tests/test_mainloop.py diff -N tests/test_mainloop.py --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/test_mainloop.py 9 May 2005 17:06:15 -0000 @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +import unittest + +from common import gobject + +class TestMainLoop (unittest.TestCase): + def testExceptionHandling (self): + import os + import sys + import select + import exceptions + import gobject + + (pipe_r, pipe_w) = os.pipe () + + pid = os.fork () + if pid == 0: + os.close (pipe_w) + select.select ([pipe_r], [], []) + os.close (pipe_r) + os._exit (1) + + def child_died (pid, status, loop): + loop.quit () + raise Exception ("foo") + + loop = gobject.MainLoop () + gobject.child_watch_add (pid, child_died, loop) + + os.close (pipe_r) + os.write (pipe_w, "Y") + os.close (pipe_w) + + def excepthook (type, value, traceback): + assert type is exceptions.Exception + assert value.args[0] == "foo" + sys.excepthook = excepthook + + got_exception = False + try: + loop.run () + except: + got_exception = True + + # + # The exception should be handled (by printing it) + # immediately on return from child_died() rather + # than here. See bug #303573 + # + sys.excepthook = sys.__excepthook__ + assert not got_exception