diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2018-09-23 08:56:16 +0200 |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2018-09-23 08:56:16 +0200 |
commit | 36f9d0b8d01b694d14b4516c880dab786728567d (patch) | |
tree | 435b352d74c87922f9c6334e7e63422d4c63904b | |
parent | 107fc454595180121f4a42eecc28a48b617d445f (diff) | |
parent | 525b8e2ec134cc478d6ab6cafe10a1f3a60df7e5 (diff) | |
download | cython-36f9d0b8d01b694d14b4516c880dab786728567d.tar.gz |
Merge branch 'master' into release
-rw-r--r-- | CHANGES.rst | 8 | ||||
-rw-r--r-- | Cython/Compiler/ExprNodes.py | 40 | ||||
-rw-r--r-- | Cython/Compiler/Nodes.py | 6 | ||||
-rw-r--r-- | Cython/Compiler/Parsing.py | 3 | ||||
-rw-r--r-- | docs/src/userguide/wrapping_CPlusPlus.rst | 17 | ||||
-rw-r--r-- | tests/run/cpp_exceptions.pyx | 14 | ||||
-rw-r--r-- | tests/run/cpp_exceptions_helper.h | 8 |
7 files changed, 80 insertions, 16 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 58d3eba5a..6e25b7712 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -21,11 +21,15 @@ Features added * Memoryviews are supported in PEP-484/526 style type declarations. (Github issue #2529) +* ``@cython.nogil`` is supported as a C-function decorator in Python code. + (Github issue #2557) + * Raising exceptions from nogil code will automatically acquire the GIL, instead of requiring an explicit ``with gil`` block. -* ``@cython.nogil`` is supported as a C-function decorator in Python code. - (Github issue #2557) +* C++ functions can now be declared as potentially raising both C++ and Python + exceptions, so that Cython can handle both correctly. + (Github issue #2615) * ``cython.inline()`` supports a direct ``language_level`` keyword argument that was previously only available via a directive. diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index b3e5b31e1..9c8cab8a3 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -187,22 +187,44 @@ def infer_sequence_item_type(env, seq_node, index_node=None, seq_type=None): return item_types.pop() return None +# Returns a block of code to translate the exception, +# plus a boolean indicating whether to check for Python exceptions. def get_exception_handler(exception_value): if exception_value is None: - return "__Pyx_CppExn2PyErr();" + return "__Pyx_CppExn2PyErr();", False + elif (exception_value.type == PyrexTypes.c_char_type + and exception_value.value == '*'): + return "__Pyx_CppExn2PyErr();", True elif exception_value.type.is_pyobject: - return 'try { throw; } catch(const std::exception& exn) { PyErr_SetString(%s, exn.what()); } catch(...) { PyErr_SetNone(%s); }' % ( - exception_value.entry.cname, - exception_value.entry.cname) + return ( + 'try { throw; } catch(const std::exception& exn) {' + 'PyErr_SetString(%s, exn.what());' + '} catch(...) { PyErr_SetNone(%s); }' % ( + exception_value.entry.cname, + exception_value.entry.cname), + False) 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), + False) + +def maybe_check_py_error(code, check_py_exception, pos, nogil): + if check_py_exception: + if nogil: + code.putln(code.error_goto_if("__Pyx_ErrOccurredWithGIL()", pos)) + else: + code.putln(code.error_goto_if("PyErr_Occurred()", pos)) def translate_cpp_exception(code, pos, inside, py_result, exception_value, nogil): - raise_py_exception = get_exception_handler(exception_value) + raise_py_exception, check_py_exception = get_exception_handler(exception_value) code.putln("try {") code.putln("%s" % inside) if py_result: code.putln(code.error_goto_if_null(py_result, pos)) + maybe_check_py_error(code, check_py_exception, pos, nogil) code.putln("} catch(...) {") if nogil: code.put_ensure_gil(declare_gilstate=True) @@ -216,12 +238,14 @@ def translate_cpp_exception(code, pos, inside, py_result, exception_value, nogil # both have an exception declaration. def translate_double_cpp_exception(code, pos, lhs_type, lhs_code, rhs_code, lhs_exc_val, assign_exc_val, nogil): - handle_lhs_exc = get_exception_handler(lhs_exc_val) - handle_assignment_exc = get_exception_handler(assign_exc_val) + handle_lhs_exc, lhc_check_py_exc = get_exception_handler(lhs_exc_val) + handle_assignment_exc, assignment_check_py_exc = get_exception_handler(assign_exc_val) code.putln("try {") code.putln(lhs_type.declaration_code("__pyx_local_lvalue = %s;" % lhs_code)) + maybe_check_py_error(code, lhc_check_py_exc, pos, nogil) code.putln("try {") code.putln("__pyx_local_lvalue = %s;" % rhs_code) + maybe_check_py_error(code, assignment_check_py_exc, pos, nogil) # Catch any exception from the overloaded assignment. code.putln("} catch(...) {") if nogil: diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index bb1ec44e4..e06bfdc9b 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -741,9 +741,11 @@ class CFuncDeclaratorNode(CDeclaratorNode): and not exc_val_type.is_pyobject and not (exc_val_type.is_cfunction and not exc_val_type.return_type.is_pyobject - and not exc_val_type.args)): + and not exc_val_type.args) + and not (exc_val_type == PyrexTypes.c_char_type + and self.exception_value.value == '*')): error(self.exception_value.pos, - "Exception value must be a Python exception or cdef function with no arguments.") + "Exception value must be a Python exception or cdef function with no arguments or *.") exc_val = self.exception_value else: self.exception_value = self.exception_value.coerce_to( diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index dde1c3b8f..4200ee494 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -2937,6 +2937,9 @@ def p_exception_value_clause(s): name = s.systring s.next() exc_val = p_name(s, name) + elif s.sy == '*': + exc_val = ExprNodes.CharNode(s.position(), value=u'*') + s.next() else: if s.sy == '?': exc_check = 1 diff --git a/docs/src/userguide/wrapping_CPlusPlus.rst b/docs/src/userguide/wrapping_CPlusPlus.rst index 94c5df5f1..e12bf38be 100644 --- a/docs/src/userguide/wrapping_CPlusPlus.rst +++ b/docs/src/userguide/wrapping_CPlusPlus.rst @@ -111,7 +111,12 @@ of the :func:`cythonize` function. Declare a var with the wrapped C++ class ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Now, we use cdef to declare a var of the class with the C++ ``new`` statement: +We'll create a ``.pyx`` file named ``rect.pyx`` to build our wrapper. We're +using a name other than ``Rectangle``, but if you prefer giving the same name +to the wrapper as the C++ class, see the section on +:ref:`resolving naming conflicts <resolve-conflicts>`. + +Within, we use cdef to declare a var of the class with the C++ ``new`` statement: .. literalinclude:: ../../examples/userguide/wrapping_CPlusPlus/cython_usage.pyx @@ -164,9 +169,6 @@ instance. .. literalinclude:: ../../examples/userguide/wrapping_CPlusPlus/rect_ptr.pyx -If you prefer giving the same name to the wrapper as the C++ class, see the -section on :ref:`resolving naming conflicts <resolve-conflicts>`. - Compilation and Importing ========================= @@ -440,6 +442,13 @@ called, which allows one to do custom C++ to Python error "translations." If raise_py_error does not actually raise an exception a RuntimeError will be raised. +There is also the special form:: + + cdef int raise_py_or_cpp() except +* + +for those functions that may raise either a Python or a C++ exception. + + Static member method -------------------- diff --git a/tests/run/cpp_exceptions.pyx b/tests/run/cpp_exceptions.pyx index 419eb7bd5..38f27ba54 100644 --- a/tests/run/cpp_exceptions.pyx +++ b/tests/run/cpp_exceptions.pyx @@ -22,6 +22,7 @@ cdef extern from "cpp_exceptions_helper.h": cdef void raise_underflow() except + cdef raise_or_throw(bint py) except + + cdef int raise_or_throw_int(bint py) except +* cdef cppclass Foo: int bar_raw "bar"(bint fire) except + @@ -113,6 +114,19 @@ def test_func_that_can_raise_or_throw(bint py): """ raise_or_throw(py) +def test_func_that_can_raise_or_throw_c_return(bint py): + """ + >>> test_func_that_can_raise_or_throw_c_return(0) + Traceback (most recent call last): + ... + RuntimeError: oopsie + >>> test_func_that_can_raise_or_throw_c_return(1) + Traceback (most recent call last): + ... + ValueError: oopsie + """ + raise_or_throw_int(py) + def test_int_raw(bint fire): """ >>> test_int_raw(False) diff --git a/tests/run/cpp_exceptions_helper.h b/tests/run/cpp_exceptions_helper.h index d1cbffb6f..a5147fe31 100644 --- a/tests/run/cpp_exceptions_helper.h +++ b/tests/run/cpp_exceptions_helper.h @@ -70,3 +70,11 @@ PyObject *raise_or_throw(int py) { PyErr_SetString(PyExc_ValueError, "oopsie"); return NULL; } + +int raise_or_throw_int(int py) { + if (!py) { + throw std::runtime_error("oopsie"); + } + PyErr_SetString(PyExc_ValueError, "oopsie"); + return -1; +} |