From 262c88f846cf9d1ce2f0b9a8f60fd5bb44e47bc9 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 23 Aug 2009 19:16:42 +0100 Subject: [PATCH] catch exceptions in method calls too --- pybindgen/cppmethod.py | 28 +++++++++++++++++++++++++++- tests/foo.cc | 15 ++++++++++++++- tests/foo.h | 5 +++++ tests/foomodulegen.py | 4 ++++ tests/footest.py | 6 ++++++ 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/pybindgen/cppmethod.py b/pybindgen/cppmethod.py index 386bbae..21ef6b4 100644 --- a/pybindgen/cppmethod.py +++ b/pybindgen/cppmethod.py @@ -13,6 +13,8 @@ from typehandlers import codesink import overloading import settings import utils +from cppexception import CppException + class CppMethod(ForwardWrapperBase): """ @@ -23,7 +25,7 @@ class CppMethod(ForwardWrapperBase): template_parameters=(), is_virtual=None, is_const=False, unblock_threads=None, is_pure_virtual=False, custom_template_method_name=None, visibility='public', - custom_name=None, deprecated=False, docstring=None): + custom_name=None, deprecated=False, docstring=None, throw=()): """ Create an object the generates code to wrap a C++ class method. @@ -66,6 +68,9 @@ class CppMethod(ForwardWrapperBase): - False: Not deprecated - True: Deprecated - "message": Deprecated, and deprecation warning contains the given message + + @param throw: list of C++ exceptions that the function may throw + @type throw: list of L{CppException} """ self.stack_where_defined = traceback.extract_stack() @@ -114,6 +119,10 @@ class CppMethod(ForwardWrapperBase): unblock_threads=unblock_threads) self.deprecated = deprecated + for t in throw: + assert isinstance(t, CppException) + self.throw = list(throw) + def set_helper_class(self, helper_class): "Set the C++ helper class, which is used for overriding virtual methods" self._helper_class = helper_class @@ -189,6 +198,12 @@ class CppMethod(ForwardWrapperBase): method = '%s::%s%s' % (class_.full_name, self.method_name, template_params) else: method = 'self->obj->%s%s' % (self.method_name, template_params) + + + if self.throw: + self.before_call.write_code('try\n{') + self.before_call.indent() + if self.return_value.ctype == 'void': self.before_call.write_code( '%s(%s);' % @@ -203,6 +218,17 @@ class CppMethod(ForwardWrapperBase): 'retval = %s(%s);' % (method, ", ".join(self.call_params))) + if self.throw: + for exc in self.throw: + self.before_call.unindent() + self.before_call.write_code('} catch (%s const &exc) {' % exc.full_name) + self.before_call.indent() + self.before_call.write_cleanup() + self.before_call.write_code('PyErr_SetNone((PyObject *) %s);\nreturn NULL;' % exc.pytypestruct) + self.before_call.unindent() + self.before_call.write_code('}') + + def _before_return_hook(self): """hook that post-processes parameters and check for custodian= CppClass parameters""" diff --git a/tests/foo.cc b/tests/foo.cc index 765f3e0..5168853 100644 --- a/tests/foo.cc +++ b/tests/foo.cc @@ -434,7 +434,20 @@ namespace xpto } } -double my_inverse_func (double x) throw (DomainError) +double +my_inverse_func (double x) throw (DomainError) +{ + if (x == 0) + { + DomainError ex; + ex.message = "value must be != 0"; + throw ex; + } + return 1/x; +} + +double +ClassThatThrows::my_inverse_method (double x) throw (DomainError) { if (x == 0) { diff --git a/tests/foo.h b/tests/foo.h index 32d13f5..43d9853 100644 --- a/tests/foo.h +++ b/tests/foo.h @@ -1046,6 +1046,11 @@ struct DomainError : public Error // returns 1/x, raises DomainError if x == 0 double my_inverse_func (double x) throw (DomainError); +struct ClassThatThrows +{ + // returns 1/x, raises DomainError if x == 0 + double my_inverse_method (double x) throw (DomainError); +}; #endif /* !FOO_H_ */ diff --git a/tests/foomodulegen.py b/tests/foomodulegen.py index 586bf28..b2b6f44 100755 --- a/tests/foomodulegen.py +++ b/tests/foomodulegen.py @@ -500,6 +500,10 @@ int %s::custom_method_added_by_a_hook(int x) mod.add_function('my_inverse_func', 'double', [Parameter.new('double', 'x')], throw=[DomainError]) + ClassThatThrows = mod.add_struct('ClassThatThrows') + ClassThatThrows.add_method('my_inverse_method', 'double', [Parameter.new('double', 'x')], + throw=[DomainError]) + #### --- error handler --- class MyErrorHandler(pybindgen.settings.ErrorHandler): diff --git a/tests/footest.py b/tests/footest.py index 330dd46..60a4b33 100644 --- a/tests/footest.py +++ b/tests/footest.py @@ -1166,5 +1166,11 @@ class TestFoo(unittest.TestCase): self.assertEqual(y, 0.5) self.assertRaises(foo.DomainError, foo.my_inverse_func, 0) + def test_method_exception(self): + c = foo.ClassThatThrows() + y = c.my_inverse_method(2) + self.assertEqual(y, 0.5) + self.assertRaises(foo.DomainError, c.my_inverse_method, 0) + if __name__ == '__main__': unittest.main()