Author: Wim Lavrijsen <[email protected]>
Branch: cppyy-packaging
Changeset: r94431:dd967ce1da92
Date: 2018-04-23 15:52 -0700
http://bitbucket.org/pypy/pypy/changeset/dd967ce1da92/
Log: initial support for exception handling from wrapped functions
diff --git a/pypy/module/_cppyy/include/capi.h
b/pypy/module/_cppyy/include/capi.h
--- a/pypy/module/_cppyy/include/capi.h
+++ b/pypy/module/_cppyy/include/capi.h
@@ -16,6 +16,8 @@
typedef long cppyy_index_t;
typedef void* cppyy_funcaddr_t;
+ typedef unsigned long cppyy_exctype_t;
+
/* name to opaque C++ scope representation
-------------------------------- */
RPY_EXTERN
char* cppyy_resolve_name(const char* cppitem_name);
diff --git a/pypy/module/_cppyy/interp_cppyy.py
b/pypy/module/_cppyy/interp_cppyy.py
--- a/pypy/module/_cppyy/interp_cppyy.py
+++ b/pypy/module/_cppyy/interp_cppyy.py
@@ -19,6 +19,9 @@
INSTANCE_FLAGS_IS_REF = 0x0002
INSTANCE_FLAGS_IS_R_VALUE = 0x0004
+OVERLOAD_FLAGS_USE_FFI = 0x0001
+
+
class FastCallNotPossible(Exception):
pass
@@ -186,7 +189,7 @@
return rffi.cast(rffi.VOIDP, loc_idx)
@jit.unroll_safe
- def call(self, cppthis, args_w):
+ def call(self, cppthis, args_w, useffi):
jit.promote(self)
assert lltype.typeOf(cppthis) == capi.C_OBJECT
@@ -218,16 +221,25 @@
try:
# attempt to call directly through ffi chain
- if self._funcaddr:
+ if useffi and self._funcaddr:
try:
return self.do_fast_call(cppthis, args_w, call_local)
except FastCallNotPossible:
pass # can happen if converters or executor does not
implement ffi
# ffi chain must have failed; using stub functions instead
- args = self.prepare_arguments(args_w, call_local)
+ args, stat = self.prepare_arguments(args_w, call_local)
try:
- return self.executor.execute(self.space, self.cppmethod,
cppthis, len(args_w), args)
+ result = self.executor.execute(
+ self.space, self.cppmethod, cppthis, len(args_w), args)
+ if stat[0] != rffi.cast(rffi.ULONG, 0):
+ what = rffi.cast(rffi.CCHARP, stat[1])
+ pywhat = rffi.charp2str(what)
+ capi.c_free(self.space, rffi.cast(rffi.VOIDP, what))
+ if hasattr(self.space, "fake"):
+ raise OperationError(self.space.w_Exception,
self.space.newtext("C++ exception"))
+ raise oefmt(self.space.w_Exception, pywhat)
+ return result
finally:
self.finalize_call(args, args_w, call_local)
finally:
@@ -373,7 +385,10 @@
conv.free_argument(self.space, rffi.cast(capi.C_OBJECT,
arg_j), loc_j)
capi.c_deallocate_function_args(self.space, args)
raise
- return args
+ stat = rffi.cast(rffi.ULONGP,
+ lltype.direct_ptradd(rffi.cast(rffi.CCHARP, args),
int(len(args_w))*stride))
+ stat[0] = rffi.cast(rffi.ULONG, 0)
+ return args, stat
@jit.unroll_safe
def finalize_call(self, args, args_w, call_local):
@@ -435,7 +450,7 @@
# TODO: might have to specialize for CPPTemplatedCall on
CPPMethod/CPPFunction here
CPPMethod.__init__(self, space, declaring_scope, method_index,
arg_defs, args_required)
- def call(self, cppthis, args_w):
+ def call(self, cppthis, args_w, useffi):
assert lltype.typeOf(cppthis) == capi.C_OBJECT
for i in range(len(args_w)):
try:
@@ -447,10 +462,10 @@
raise oefmt(self.space.w_TypeError,
"non-matching template (got %s where %s expected)",
s, self.templ_args[i])
- return W_CPPBoundMethod(cppthis, self)
+ return W_CPPBoundMethod(cppthis, self, useffi)
- def bound_call(self, cppthis, args_w):
- return CPPMethod.call(self, cppthis, args_w)
+ def bound_call(self, cppthis, args_w, useffi):
+ return CPPMethod.call(self, cppthis, args_w, useffi)
def __repr__(self):
return "CPPTemplatedCall: %s" % self.prototype()
@@ -468,11 +483,11 @@
def unpack_cppthis(space, w_cppinstance, declaring_scope):
return rffi.cast(capi.C_OBJECT, declaring_scope.handle)
- def call(self, cppthis, args_w):
+ def call(self, cppthis, args_w, useffi):
# Note: this does not return a wrapped instance, just a pointer to the
# new instance; the overload must still wrap it before returning. Also,
# cppthis is declaring_scope.handle (as per unpack_cppthis(), above).
- return CPPMethod.call(self, cppthis, args_w)
+ return CPPMethod.call(self, cppthis, args_w, useffi)
def __repr__(self):
return "CPPConstructor: %s" % self.prototype()
@@ -485,7 +500,7 @@
_immutable_ = True
- def call(self, cppthis, args_w):
+ def call(self, cppthis, args_w, useffi):
end = len(args_w)-1
if 0 <= end:
w_item = args_w[end]
@@ -493,7 +508,7 @@
if self.converters is None:
self._setup(cppthis)
self.executor.set_item(self.space, w_item) # TODO: what about
threads?
- CPPMethod.call(self, cppthis, args_w)
+ CPPMethod.call(self, cppthis, args_w, useffi)
class W_CPPOverload(W_Root):
@@ -501,7 +516,7 @@
collection of (possibly) overloaded methods or functions. It calls these
in order and deals with error handling and reporting."""
- _attrs_ = ['space', 'scope', 'functions']
+ _attrs_ = ['space', 'scope', 'functions', 'flags']
_immutable_fields_ = ['scope', 'functions[*]']
def __init__(self, space, declaring_scope, functions):
@@ -510,6 +525,19 @@
assert len(functions)
from rpython.rlib import debug
self.functions = debug.make_sure_not_resized(functions)
+ self.flags = 0
+ self.flags |= OVERLOAD_FLAGS_USE_FFI
+
+ # allow user to determine ffi use rules per overload
+ def fget_useffi(self, space):
+ return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI))
+
+ @unwrap_spec(value=bool)
+ def fset_useffi(self, space, value):
+ if space.is_true(value):
+ self.flags |= OVERLOAD_FLAGS_USE_FFI
+ else:
+ self.flags &= ~OVERLOAD_FLAGS_USE_FFI
@jit.elidable_promote()
def is_static(self):
@@ -540,7 +568,7 @@
for i in range(len(self.functions)):
cppyyfunc = self.functions[i]
try:
- return cppyyfunc.call(cppthis, args_w)
+ return cppyyfunc.call(cppthis, args_w, self.flags &
OVERLOAD_FLAGS_USE_FFI)
except Exception:
pass
@@ -553,7 +581,7 @@
for i in range(len(self.functions)):
cppyyfunc = self.functions[i]
try:
- return cppyyfunc.call(cppthis, args_w)
+ return cppyyfunc.call(cppthis, args_w, self.flags &
OVERLOAD_FLAGS_USE_FFI)
except OperationError as e:
# special case if there's just one function, to prevent
clogging the error message
if len(self.functions) == 1:
@@ -588,6 +616,7 @@
'CPPOverload',
is_static = interp2app(W_CPPOverload.is_static),
call = interp2app(W_CPPOverload.call),
+ __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi,
W_CPPOverload.fset_useffi),
prototype = interp2app(W_CPPOverload.prototype),
)
@@ -642,17 +671,18 @@
class W_CPPBoundMethod(W_Root):
- _attrs_ = ['cppthis', 'method']
+ _attrs_ = ['cppthis', 'method', 'useffi']
- def __init__(self, cppthis, method):
+ def __init__(self, cppthis, method, useffi):
self.cppthis = cppthis
self.method = method
+ self.useffi = useffi
def __call__(self, args_w):
- return self.method.bound_call(self.cppthis, args_w)
+ return self.method.bound_call(self.cppthis, args_w, self.useffi)
def __repr__(self):
- return "W_CPPBoundMethod(%s)" % [f.prototype() for f in self.functions]
+ return "W_CPPBoundMethod(%s)" % self.method.prototype()
W_CPPBoundMethod.typedef = TypeDef(
'CPPBoundMethod',
diff --git a/pypy/module/_cppyy/test/advancedcpp.cxx
b/pypy/module/_cppyy/test/advancedcpp.cxx
--- a/pypy/module/_cppyy/test/advancedcpp.cxx
+++ b/pypy/module/_cppyy/test/advancedcpp.cxx
@@ -1,5 +1,7 @@
#include "advancedcpp.h"
+#include <stdexcept>
+
// for testing of default arguments
#define IMPLEMENT_DEFAULTER_CLASS(type, tname) \
@@ -112,3 +114,13 @@
std::string overload_the_other_way::gime() { return "aap"; }
int overload_the_other_way::gime() const { return 1; }
+
+
+// exception handling testing
+void Thrower::throw_anything() {
+ throw 1;
+}
+
+void Thrower::throw_exception() {
+ throw std::runtime_error("C++ function failed");
+}
diff --git a/pypy/module/_cppyy/test/advancedcpp.h
b/pypy/module/_cppyy/test/advancedcpp.h
--- a/pypy/module/_cppyy/test/advancedcpp.h
+++ b/pypy/module/_cppyy/test/advancedcpp.h
@@ -400,3 +400,11 @@
std::string gime();
int gime() const;
};
+
+
+//===========================================================================
+class Thrower { // exception handling testing
+public:
+ void throw_anything();
+ void throw_exception();
+};
diff --git a/pypy/module/_cppyy/test/advancedcpp.xml
b/pypy/module/_cppyy/test/advancedcpp.xml
--- a/pypy/module/_cppyy/test/advancedcpp.xml
+++ b/pypy/module/_cppyy/test/advancedcpp.xml
@@ -57,4 +57,6 @@
<class name="overload_one_way" />
<class name="overload_the_other_way" />
+ <class name="Thrower" />
+
</lcgdict>
diff --git a/pypy/module/_cppyy/test/test_advancedcpp.py
b/pypy/module/_cppyy/test/test_advancedcpp.py
--- a/pypy/module/_cppyy/test/test_advancedcpp.py
+++ b/pypy/module/_cppyy/test/test_advancedcpp.py
@@ -656,3 +656,22 @@
# TODO: currently fails b/c double** not understood as &double*
#assert cppyy.gbl.my_global_ptr[0] == 1234.
+ def test22_exceptions(self):
+ """Catching of C++ exceptions"""
+
+ import _cppyy as cppyy
+ Thrower = cppyy.gbl.Thrower
+
+ # TODO: clean up this interface:
+ Thrower.__cppdecl__.get_overload('throw_anything').__useffi__ = False
+ Thrower.__cppdecl__.get_overload('throw_exception').__useffi__ = False
+
+ t = Thrower()
+
+ assert raises(Exception, t.throw_anything)
+ assert raises(Exception, t.throw_exception)
+
+ try:
+ t.throw_exception()
+ except Exception, e:
+ "C++ function failed" in str(e)
diff --git a/pypy/module/_cppyy/test/test_overloads.py
b/pypy/module/_cppyy/test/test_overloads.py
--- a/pypy/module/_cppyy/test/test_overloads.py
+++ b/pypy/module/_cppyy/test/test_overloads.py
@@ -15,7 +15,6 @@
spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools'])
def setup_class(cls):
- env = os.environ
cls.w_test_dct = cls.space.newtext(test_dct)
cls.w_overloads = cls.space.appexec([], """():
import ctypes
diff --git a/pypy/module/_cppyy/test/test_zjit.py
b/pypy/module/_cppyy/test/test_zjit.py
--- a/pypy/module/_cppyy/test/test_zjit.py
+++ b/pypy/module/_cppyy/test/test_zjit.py
@@ -137,6 +137,7 @@
executor.get_executor(self, 'int').__class__.c_stubcall =
staticmethod(c_call_i)
self.w_AttributeError = FakeException(self, "AttributeError")
+ self.w_Exception = FakeException(self, "Exception")
self.w_KeyError = FakeException(self, "KeyError")
self.w_NotImplementedError = FakeException(self, "NotImplementedError")
self.w_ReferenceError = FakeException(self, "ReferenceError")
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit