Commit f61160ed authored by Robert Bradshaw's avatar Robert Bradshaw

Catch Python exceptions for object-returning except+ functions.

Fixes Github issue #2603.
parent 76a8e08f
...@@ -197,10 +197,12 @@ def get_exception_handler(exception_value): ...@@ -197,10 +197,12 @@ def get_exception_handler(exception_value):
else: else:
return '%s(); if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError , "Error converting c++ exception.");' % exception_value.entry.cname return '%s(); if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError , "Error converting c++ exception.");' % exception_value.entry.cname
def translate_cpp_exception(code, pos, inside, exception_value, nogil): def translate_cpp_exception(code, pos, inside, py_result, exception_value, nogil):
raise_py_exception = get_exception_handler(exception_value) raise_py_exception = get_exception_handler(exception_value)
code.putln("try {") code.putln("try {")
code.putln("%s" % inside) code.putln("%s" % inside)
if py_result:
code.putln(code.error_goto_if_null(py_result, pos))
code.putln("} catch(...) {") code.putln("} catch(...) {")
if nogil: if nogil:
code.put_ensure_gil(declare_gilstate=True) code.put_ensure_gil(declare_gilstate=True)
...@@ -2306,7 +2308,11 @@ class NameNode(AtomicExprNode): ...@@ -2306,7 +2308,11 @@ class NameNode(AtomicExprNode):
if overloaded_assignment: if overloaded_assignment:
result = rhs.result() result = rhs.result()
if exception_check == '+': if exception_check == '+':
translate_cpp_exception(code, self.pos, '%s = %s;' % (self.result(), result), exception_value, self.in_nogil_context) translate_cpp_exception(
code, self.pos,
'%s = %s;' % (self.result(), result),
self.result() if self.type.is_pyobject else None,
exception_value, self.in_nogil_context)
else: else:
code.putln('%s = %s;' % (self.result(), result)) code.putln('%s = %s;' % (self.result(), result))
else: else:
...@@ -3978,6 +3984,7 @@ class IndexNode(_IndexingBaseNode): ...@@ -3978,6 +3984,7 @@ class IndexNode(_IndexingBaseNode):
translate_cpp_exception(code, self.pos, translate_cpp_exception(code, self.pos,
"%s = %s[%s];" % (self.result(), self.base.result(), "%s = %s[%s];" % (self.result(), self.base.result(),
self.index.result()), self.index.result()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context) self.exception_value, self.in_nogil_context)
else: else:
error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value
...@@ -4048,6 +4055,7 @@ class IndexNode(_IndexingBaseNode): ...@@ -4048,6 +4055,7 @@ class IndexNode(_IndexingBaseNode):
# both exception handlers are the same. # both exception handlers are the same.
translate_cpp_exception(code, self.pos, translate_cpp_exception(code, self.pos,
"%s = %s;" % (self.result(), rhs.result()), "%s = %s;" % (self.result(), rhs.result()),
self.result() if self.lhs.is_pyobject else None,
self.exception_value, self.in_nogil_context) self.exception_value, self.in_nogil_context)
else: else:
code.putln( code.putln(
...@@ -5861,6 +5869,7 @@ class SimpleCallNode(CallNode): ...@@ -5861,6 +5869,7 @@ class SimpleCallNode(CallNode):
lhs = "" lhs = ""
if func_type.exception_check == '+': if func_type.exception_check == '+':
translate_cpp_exception(code, self.pos, '%s%s;' % (lhs, rhs), translate_cpp_exception(code, self.pos, '%s%s;' % (lhs, rhs),
self.result() if self.type.is_pyobject else None,
func_type.exception_value, self.nogil) func_type.exception_value, self.nogil)
else: else:
if (self.overflowcheck if (self.overflowcheck
...@@ -10038,6 +10047,7 @@ class UnopNode(ExprNode): ...@@ -10038,6 +10047,7 @@ class UnopNode(ExprNode):
if self.is_cpp_operation() and self.exception_check == '+': if self.is_cpp_operation() and self.exception_check == '+':
translate_cpp_exception(code, self.pos, translate_cpp_exception(code, self.pos,
"%s = %s %s;" % (self.result(), self.operator, self.operand.result()), "%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context) self.exception_value, self.in_nogil_context)
else: else:
code.putln("%s = %s %s;" % (self.result(), self.operator, self.operand.result())) code.putln("%s = %s %s;" % (self.result(), self.operator, self.operand.result()))
...@@ -10277,6 +10287,7 @@ class AmpersandNode(CUnopNode): ...@@ -10277,6 +10287,7 @@ class AmpersandNode(CUnopNode):
if (self.operand.type.is_cpp_class and self.exception_check == '+'): if (self.operand.type.is_cpp_class and self.exception_check == '+'):
translate_cpp_exception(code, self.pos, translate_cpp_exception(code, self.pos,
"%s = %s %s;" % (self.result(), self.operator, self.operand.result()), "%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context) self.exception_value, self.in_nogil_context)
...@@ -10821,7 +10832,7 @@ class TypeidNode(ExprNode): ...@@ -10821,7 +10832,7 @@ class TypeidNode(ExprNode):
arg_code = self.arg_type.result() arg_code = self.arg_type.result()
translate_cpp_exception(code, self.pos, translate_cpp_exception(code, self.pos,
"%s = typeid(%s);" % (self.temp_code, arg_code), "%s = typeid(%s);" % (self.temp_code, arg_code),
None, self.in_nogil_context) None, None, self.in_nogil_context)
class TypeofNode(ExprNode): class TypeofNode(ExprNode):
# Compile-time type of an expression, as a string. # Compile-time type of an expression, as a string.
...@@ -11080,6 +11091,7 @@ class BinopNode(ExprNode): ...@@ -11080,6 +11091,7 @@ class BinopNode(ExprNode):
if self.is_cpp_operation() and self.exception_check == '+': if self.is_cpp_operation() and self.exception_check == '+':
translate_cpp_exception(code, self.pos, translate_cpp_exception(code, self.pos,
"%s = %s;" % (self.result(), self.calculate_result_code()), "%s = %s;" % (self.result(), self.calculate_result_code()),
self.result() if self.type.is_pyobject else None,
self.exception_value, self.in_nogil_context) self.exception_value, self.in_nogil_context)
else: else:
code.putln("%s = %s;" % (self.result(), self.calculate_result_code())) code.putln("%s = %s;" % (self.result(), self.calculate_result_code()))
...@@ -12393,7 +12405,13 @@ class CmpNode(object): ...@@ -12393,7 +12405,13 @@ class CmpNode(object):
self.c_operator(op), self.c_operator(op),
code2) code2)
if self.is_cpp_comparison() and self.exception_check == '+': if self.is_cpp_comparison() and self.exception_check == '+':
translate_cpp_exception(code, self.pos, statement, self.exception_value, self.in_nogil_context) translate_cpp_exception(
code,
self.pos,
statement,
result_code if self.type.is_pyobject else None,
self.exception_value,
self.in_nogil_context)
code.putln(statement) code.putln(statement)
def c_operator(self, op): def c_operator(self, op):
......
...@@ -21,6 +21,8 @@ cdef extern from "cpp_exceptions_helper.h": ...@@ -21,6 +21,8 @@ cdef extern from "cpp_exceptions_helper.h":
cdef void raise_typeerror() except + cdef void raise_typeerror() except +
cdef void raise_underflow() except + cdef void raise_underflow() except +
cdef object raise_py(bint fire_py) except +
cdef cppclass Foo: cdef cppclass Foo:
int bar_raw "bar"(bint fire) except + int bar_raw "bar"(bint fire) except +
int bar_value "bar"(bint fire) except +ValueError int bar_value "bar"(bint fire) except +ValueError
...@@ -199,3 +201,17 @@ def test_cppclass_method_custom(bint fire): ...@@ -199,3 +201,17 @@ def test_cppclass_method_custom(bint fire):
foo.bar_custom(fire) foo.bar_custom(fire)
finally: finally:
del foo del foo
def test_py(bint py_fire):
"""
>>> test_py(True)
Traceback (most recent call last):
...
RuntimeError: py error
>>> test_py(False)
Traceback (most recent call last):
...
IndexError: c++ error
"""
raise_py(py_fire)
...@@ -61,3 +61,12 @@ void raise_typeerror() { ...@@ -61,3 +61,12 @@ void raise_typeerror() {
void raise_underflow() { void raise_underflow() {
throw std::underflow_error("underflow_error"); throw std::underflow_error("underflow_error");
} }
PyObject* raise_py(int fire_py) {
if (fire_py) {
PyErr_SetString(PyExc_RuntimeError, "py error");
return NULL;
} else {
throw std::out_of_range("c++ error");
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment