Hi, the gobject.option implementation of GOption currently doesn't handle errors when for example an option gets the wrong argument (for example: "--number=text").
To support exception in gobject.Option i implemented: /** * Checks if a GError exception occurred in python and set the GError. * If the exception is not a GError or the GError exception has no * domain, code and message attributes the exception is printed. * * Ensures the GIL state. * * Returns: True if a python-exception was set and this exception was a * correct GError-Exception. */ gboolean pyg_set_gerror_from_exception (GError **error_dest) To use this function, the python-code must create a gobject.GError() object and add domain, code and message attributes manually, for example: err = gobject.GError(message) err.domain = gobject.OPTION_ERROR err.code = gobject.OPTION_ERROR_BAD_VALUE err.message = "Wrong option ..." raise err Or should i add a full GError-class? I didn't look, but are there other places in pygtk where a GError-Exception isn't translated into GError? The attached patch adds this function and the exception handling in GOption. best regards, Johannes Hölzl PS: Are two patches, one with only pyg_set_gerror_from_exception implementation and another with exception support in GOption preferred?
Index: ChangeLog =================================================================== RCS file: /cvs/gnome/gnome-python/pygobject/ChangeLog,v retrieving revision 1.43 diff -u -r1.43 ChangeLog --- ChangeLog 20 May 2006 13:45:35 -0000 1.43 +++ ChangeLog 25 May 2006 18:25:52 -0000 @@ -1,3 +1,16 @@ +2006-05-24 Johannes Hölzl <[EMAIL PROTECTED]> + + * gobject/gobjectmodule.c (pyg_exception_to_gerror): + Added function to convert gobject.GError-exception into GError. + + * gobject/gobjectmodule.c (init_gobjectmodule): + * gobject/pygoptiongroup.c (arg_func): + * gobject/option.py (OptionParser._parse_args, + OptionGroup._to_goptiongroup): + * tests/test_option.py: + Added conversion of Python exceptions + into GErrors and vice versa. + 2006-05-20 Yevgen Muntyan <[EMAIL PROTECTED]> reviewed by: Gustavo Carneiro Index: gobject/gobjectmodule.c =================================================================== RCS file: /cvs/gnome/gnome-python/pygobject/gobject/gobjectmodule.c,v retrieving revision 1.219 diff -u -r1.219 gobjectmodule.c --- gobject/gobjectmodule.c 7 May 2006 21:32:21 -0000 1.219 +++ gobject/gobjectmodule.c 25 May 2006 18:25:57 -0000 @@ -2799,6 +2799,66 @@ return FALSE; } +/** + * Checks if a GError exception occurred in python and set the GError. + * If the exception is not a GError or the GError exception has no + * domain, code and message attributes the exception is printed. + * + * Ensures the GIL state. + * + * Returns: True if a python-exception was set and this exception was a + * correct GError-Exception. + */ +gboolean +pyg_set_gerror_from_exception (GError **error_dest) +{ + PyObject *exception_type, *exception_value, *exception_tb; + GQuark domain; + gint code; + PyObject *value = NULL; + PyGILState_STATE state = pyg_gil_state_ensure(); + + g_assert(error_dest != NULL); + g_assert(*error_dest == NULL); + + PyErr_Fetch(&exception_type, &exception_value, &exception_tb); + if (!exception_type) return FALSE; + if (!PyErr_GivenExceptionMatches(exception_type, gerror_exc)) goto failed; + if (!exception_value) goto failed; + + if (!PyObject_HasAttrString(exception_value, "domain")) goto failed; + value = PyObject_GetAttrString(exception_value, "domain"); + if (!PyString_Check(value)) goto failed; + domain = g_quark_from_string(PyString_AsString(value)); + Py_CLEAR(value); + + if (!PyObject_HasAttrString(exception_value, "code")) goto failed; + value = PyObject_GetAttrString(exception_value, "code"); + if (!PyInt_Check(value)) goto failed; + code = PyInt_AsLong(value); + Py_CLEAR(value); + + if (!PyObject_HasAttrString(exception_value, "message")) goto failed; + value = PyObject_GetAttrString(exception_value, "message"); + if (!PyString_Check(value)) goto failed; + *error_dest = g_error_new_literal(domain, code, PyString_AsString(value)); + Py_CLEAR(value); + + Py_XDECREF(exception_tb); + Py_XDECREF(exception_value); + Py_DECREF(exception_type); + + pyg_gil_state_release(state); + return TRUE; + +failed: + Py_XDECREF(value); + + PyErr_Restore(exception_type, exception_value, exception_tb); + PyErr_Print(); + pyg_gil_state_release(state); + return FALSE; +} static PyObject * _pyg_strv_from_gvalue(const GValue *value) @@ -3106,6 +3166,7 @@ pyg_constant_strip_prefix, pyg_error_check, + pyg_set_gerror_from_exception, pyg_set_thread_block_funcs, (PyGThreadBlockFunc)0, /* block_threads */ @@ -3322,6 +3383,8 @@ #undef addint PyModule_AddStringConstant(m, "OPTION_REMAINING", G_OPTION_REMAINING); + + PyModule_AddStringConstant(m, "OPTION_ERROR", (char*) g_quark_to_string(G_OPTION_ERROR)); PyModule_AddIntConstant(m, "SPAWN_LEAVE_DESCRIPTORS_OPEN", G_SPAWN_LEAVE_DESCRIPTORS_OPEN); Index: gobject/option.py =================================================================== RCS file: /cvs/gnome/gnome-python/pygobject/gobject/option.py,v retrieving revision 1.1 diff -u -r1.1 option.py --- gobject/option.py 29 Apr 2006 18:58:20 -0000 1.1 +++ gobject/option.py 25 May 2006 18:25:57 -0000 @@ -31,11 +31,16 @@ import sys import optparse -from optparse import OptionError +from optparse import OptParseError, OptionError, OptionValueError, \ + BadOptionError, OptionConflictError import _gobject as gobject __all__ = [ + "OptParseError", "OptionError", + "OptionValueError", + "BadOptionError", + "OptionConflictError" "Option", "OptionGroup", "OptionParser", @@ -166,11 +171,18 @@ def _to_goptiongroup(self, parser): def callback(option_name, option_value, group): - if option_name.startswith('--'): - opt = self._long_opt[option_name] - else: - opt = self._short_opt[option_name] - opt.process(option_name, option_value, self.values, parser) + if option_name.startswith('--'): + opt = self._long_opt[option_name] + else: + opt = self._short_opt[option_name] + try: + opt.process(option_name, option_value, self.values, parser) + except OptionValueError, error: + gerror = gobject.GError(str(error)) + gerror.domain = gobject.OPTION_ERROR + gerror.code = gobject.OPTION_ERROR_BAD_VALUE + gerror.message = str(error) + raise gerror group = gobject.OptionGroup(self.name, self.description, self.help_description, callback) @@ -294,7 +306,21 @@ def _process_args(self, largs, rargs, values): context = self._to_goptioncontext(values) - largs.extend(context.parse([sys.argv[0]] + rargs)) + largs.extend(context.parse([sys.argv[0]] + rargs)) + def parse_args(self, args=None, values=None): + try: + return optparse.OptionParser.parse_args(self, args, values) + except gobject.GError, error: + if error.domain != gobject.OPTION_ERROR: + raise + if error.code == gobject.OPTION_ERROR_BAD_VALUE: + raise OptionValueError(error.message) + elif error.code == gobject.OPTION_ERROR_UNKNOWN_OPTION: + raise BadOptionError(error.message) + elif error.code == gobject.OPTION_ERROR_FAILED: + raise OptParseError(error.message) + else: + raise make_option = Option Index: gobject/pygobject-private.h =================================================================== RCS file: /cvs/gnome/gnome-python/pygobject/gobject/pygobject-private.h,v retrieving revision 1.47 diff -u -r1.47 pygobject-private.h --- gobject/pygobject-private.h 29 Apr 2006 18:58:20 -0000 1.47 +++ gobject/pygobject-private.h 25 May 2006 18:25:57 -0000 @@ -56,6 +56,7 @@ void pyg_destroy_notify (gpointer user_data); gboolean pyg_handler_marshal (gpointer user_data); gboolean pyg_error_check (GError **error); +gboolean pyg_set_gerror_from_exception (GError **error_dest); int pygobject_constructv (PyGObject *self, guint n_parameters, GParameter *parameters); Index: gobject/pygobject.h =================================================================== RCS file: /cvs/gnome/gnome-python/pygobject/gobject/pygobject.h,v retrieving revision 1.59 diff -u -r1.59 pygobject.h --- gobject/pygobject.h 7 May 2006 21:32:21 -0000 1.59 +++ gobject/pygobject.h 25 May 2006 18:25:58 -0000 @@ -118,6 +118,7 @@ const gchar *strip_prefix); gboolean (* error_check)(GError **error); + gboolean (* pyg_set_gerror_from_exception)(GError **error_dest); /* hooks to register handlers for getting GDK threads to cooperate * with python threading */ @@ -215,6 +216,7 @@ #define pyg_flags_add_constants (_PyGObject_API->flags_add_constants) #define pyg_constant_strip_prefix (_PyGObject_API->constant_strip_prefix) #define pyg_error_check (_PyGObject_API->error_check) +#define pyg_set_gerror_from_exception (_PyGObject_API->pyg_set_gerror_from_exception) #define pyg_set_thread_block_funcs (_PyGObject_API->set_thread_block_funcs) #define PyGParamSpec_Type (*_PyGObject_API->paramspec_type) #define pyg_param_spec_new (_PyGObject_API->paramspec_new) Index: gobject/pygoptiongroup.c =================================================================== RCS file: /cvs/gnome/gnome-python/pygobject/gobject/pygoptiongroup.c,v retrieving revision 1.1 diff -u -r1.1 pygoptiongroup.c --- gobject/pygoptiongroup.c 29 Apr 2006 18:58:20 -0000 1.1 +++ gobject/pygoptiongroup.c 25 May 2006 18:25:58 -0000 @@ -38,8 +38,8 @@ return FALSE; } - -static void destroy_g_group(PyGOptionGroup *self) +static void +destroy_g_group(PyGOptionGroup *self) { PyGILState_STATE state; state = pyg_gil_state_ensure(); @@ -100,7 +100,7 @@ arg_func(const gchar *option_name, const gchar *value, PyGOptionGroup *self, - GError *error) + GError **error) { PyObject *ret; PyGILState_STATE state; @@ -114,15 +114,16 @@ ret = PyObject_CallFunction(self->callback, "ssO", option_name, value, self); - if (ret == NULL) - PyErr_Print(); - - pyg_gil_state_release(state); if (ret == NULL) - return FALSE; + { + pyg_gil_state_release(state); + return !pyg_set_gerror_from_exception(error); + } Py_DECREF(ret); + pyg_gil_state_release(state); + return TRUE; } Index: tests/test_option.py =================================================================== RCS file: /cvs/gnome/gnome-python/pygobject/tests/test_option.py,v retrieving revision 1.1 diff -u -r1.1 test_option.py --- tests/test_option.py 29 Apr 2006 18:58:20 -0000 1.1 +++ tests/test_option.py 25 May 2006 18:25:58 -0000 @@ -1,13 +1,19 @@ #!/usr/bin/env python import unittest +import sys +from StringIO import StringIO from common import gobject from gobject import option class TestOption(unittest.TestCase): + EXCEPTION_MESSAGE = "This callback fails" def setup_group(self): + def option_callback(option, opt, value, parser): + raise StandardError(self.EXCEPTION_MESSAGE) + group = option.OptionGroup( "unittest", "Unit test options", "Show all unittest options", option_list = [ @@ -15,6 +21,15 @@ type="filename", dest="unit_file", help="Unit test option"), + option.make_option("--test-integer", + type="int", + dest="test_integer", + help="Unit integer option"), + option.make_option("--callback-failure-test", + action="callback", + callback=option_callback, + dest="test_integer", + help="Unit integer option"), ]) group.add_option("-t", "--test", action="store_false", @@ -24,7 +39,8 @@ return group def setup_parser(self): - parser = option.OptionParser("NAMES...", description="Option unit test") + parser = option.OptionParser("NAMES...", + description="Option unit test") parser.add_option("-t", "--test", help="Unit test option", action="store_false", dest="test", default=True) return parser @@ -38,3 +54,28 @@ assert group.values.test assert not parser.values.test assert group.values.unit_file == "test" + + try: + parser.parse_args(["test_option.py", "--test-integer=text"]) + except option.OptionValueError: + pass + else: + assert False + + sio = StringIO() + old_stderr = sys.stderr + sys.stderr = sio + try: + parser.parse_args(["test_option.py", "--callback-failure-test"]) + finally: + sys.stderr = old_stderr + assert (sio.getvalue().split('\n')[-2] == + "StandardError: " + self.EXCEPTION_MESSAGE) + + try: + parser.parse_args(["test_option.py", "--unknwon-option"]) + except option.BadOptionError: + pass + else: + assert False +
_______________________________________________ pygtk mailing list pygtk@daa.com.au http://www.daa.com.au/mailman/listinfo/pygtk Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/