Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r78436:2e6be5bbe1dc Date: 2015-07-04 23:38 +0200 http://bitbucket.org/pypy/pypy/changeset/2e6be5bbe1dc/
Log: hg merge cffi-callback-onerror update to cffi 'onerror' argument on callbacks (2b30856ad741, bdbb14a1b774) diff --git a/lib_pypy/cffi/api.py b/lib_pypy/cffi/api.py --- a/lib_pypy/cffi/api.py +++ b/lib_pypy/cffi/api.py @@ -286,7 +286,7 @@ """ return self._backend.from_buffer(self.BCharA, python_buffer) - def callback(self, cdecl, python_callable=None, error=None): + def callback(self, cdecl, python_callable=None, error=None, onerror=None): """Return a callback object or a decorator making such a callback object. 'cdecl' must name a C function pointer type. The callback invokes the specified 'python_callable' (which may @@ -298,7 +298,8 @@ if not callable(python_callable): raise TypeError("the 'python_callable' argument " "is not callable") - return self._backend.callback(cdecl, python_callable, error) + return self._backend.callback(cdecl, python_callable, error, + onerror) if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl, consider_function_as_funcptr=True) if python_callable is None: diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -252,7 +252,8 @@ w_t, w_v, w_tb], """(where, objrepr, extra_line, t, v, tb): import sys, traceback - sys.stderr.write('From %s%s:\\n' % (where, objrepr)) + if where or objrepr: + sys.stderr.write('From %s%s:\\n' % (where, objrepr)) if extra_line: sys.stderr.write(extra_line) traceback.print_exception(t, v, tb) diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py --- a/pypy/module/_cffi_backend/ccallback.py +++ b/pypy/module/_cffi_backend/ccallback.py @@ -22,8 +22,9 @@ class W_CDataCallback(W_CData): #_immutable_fields_ = ... ll_error = lltype.nullptr(rffi.CCHARP.TO) + w_onerror = None - def __init__(self, space, ctype, w_callable, w_error): + def __init__(self, space, ctype, w_callable, w_error, w_onerror): raw_closure = rffi.cast(rffi.CCHARP, clibffi.closureHeap.alloc()) W_CData.__init__(self, space, raw_closure, ctype) # @@ -31,6 +32,12 @@ raise oefmt(space.w_TypeError, "expected a callable object, not %T", w_callable) self.w_callable = w_callable + if not space.is_none(w_onerror): + if not space.is_true(space.callable(w_onerror)): + raise oefmt(space.w_TypeError, + "expected a callable object for 'onerror', not %T", + w_onerror) + self.w_onerror = w_onerror # fresult = self.getfunctype().ctitem size = fresult.size @@ -161,6 +168,29 @@ STDERR = 2 +@jit.dont_look_inside +def _handle_applevel_exception(space, callback, e, ll_res, extra_line): + callback.write_error_return_value(ll_res) + if callback.w_onerror is None: + callback.print_error(e, extra_line) + else: + try: + e.normalize_exception(space) + w_t = e.w_type + w_v = e.get_w_value(space) + w_tb = space.wrap(e.get_traceback()) + w_res = space.call_function(callback.w_onerror, + w_t, w_v, w_tb) + if not space.is_none(w_res): + callback.convert_result(ll_res, w_res) + except OperationError, e2: + # double exception! print a double-traceback... + callback.print_error(e, extra_line) # original traceback + e2.write_unraisable(space, '', with_traceback=True, + extra_line="\nDuring the call to 'onerror', " + "another exception occurred:\n\n") + + @jit.jit_callback("CFFI") def _invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata): """ Callback specification. @@ -178,7 +208,7 @@ try: os.write(STDERR, "SystemError: invoking a callback " "that was already freed\n") - except OSError: + except: pass # In this case, we don't even know how big ll_res is. Let's assume # it is just a 'ffi_arg', and store 0 there. @@ -195,9 +225,7 @@ extra_line = "Trying to convert the result back to C:\n" callback.convert_result(ll_res, w_res) except OperationError, e: - # got an app-level exception - callback.print_error(e, extra_line) - callback.write_error_return_value(ll_res) + _handle_applevel_exception(space, callback, e, ll_res, extra_line) # except Exception, e: # oups! last-level attempt to recover. @@ -205,7 +233,7 @@ os.write(STDERR, "SystemError: callback raised ") os.write(STDERR, str(e)) os.write(STDERR, "\n") - except OSError: + except: pass callback.write_error_return_value(ll_res) if must_leave: diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py --- a/pypy/module/_cffi_backend/ffi_obj.py +++ b/pypy/module/_cffi_backend/ffi_obj.py @@ -276,8 +276,9 @@ @unwrap_spec(w_python_callable=WrappedDefault(None), - w_error=WrappedDefault(None)) - def descr_callback(self, w_cdecl, w_python_callable, w_error): + w_error=WrappedDefault(None), + w_onerror=WrappedDefault(None)) + def descr_callback(self, w_cdecl, w_python_callable, w_error, w_onerror): """\ Return a callback object or a decorator making such a callback object. 'cdecl' must name a C function pointer type. The callback invokes the @@ -290,14 +291,16 @@ space = self.space if not space.is_none(w_python_callable): return ccallback.W_CDataCallback(space, w_ctype, - w_python_callable, w_error) + w_python_callable, w_error, + w_onerror) else: # decorator mode: returns a single-argument function - return space.appexec([w_ctype, w_error], - """(ctype, error): + return space.appexec([w_ctype, w_error, w_onerror], + """(ctype, error, onerror): import _cffi_backend return lambda python_callable: ( - _cffi_backend.callback(ctype, python_callable, error))""") + _cffi_backend.callback(ctype, python_callable, + error, onerror))""") def descr_cast(self, w_arg, w_ob): diff --git a/pypy/module/_cffi_backend/func.py b/pypy/module/_cffi_backend/func.py --- a/pypy/module/_cffi_backend/func.py +++ b/pypy/module/_cffi_backend/func.py @@ -18,9 +18,9 @@ # ____________________________________________________________ @unwrap_spec(w_ctype=ctypeobj.W_CType) -def callback(space, w_ctype, w_callable, w_error=None): +def callback(space, w_ctype, w_callable, w_error=None, w_onerror=None): from pypy.module._cffi_backend.ccallback import W_CDataCallback - return W_CDataCallback(space, w_ctype, w_callable, w_error) + return W_CDataCallback(space, w_ctype, w_callable, w_error, w_onerror) # ____________________________________________________________ diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -1170,6 +1170,14 @@ BShort = new_primitive_type("short") BFunc = new_function_type((BShort,), BShort, False) f = callback(BFunc, Zcb1, -42) + # + seen = [] + oops_result = None + def oops(*args): + seen.append(args) + return oops_result + ff = callback(BFunc, Zcb1, -42, oops) + # orig_stderr = sys.stderr orig_getline = linecache.getline try: @@ -1195,6 +1203,59 @@ Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' """) + sys.stderr = cStringIO.StringIO() + bigvalue = 20000 + assert len(seen) == 0 + assert ff(bigvalue) == -42 + assert sys.stderr.getvalue() == "" + assert len(seen) == 1 + exc, val, tb = seen[0] + assert exc is OverflowError + assert str(val) == "integer 60000 does not fit 'short'" + # + sys.stderr = cStringIO.StringIO() + bigvalue = 20000 + del seen[:] + oops_result = 81 + assert ff(bigvalue) == 81 + oops_result = None + assert sys.stderr.getvalue() == "" + assert len(seen) == 1 + exc, val, tb = seen[0] + assert exc is OverflowError + assert str(val) == "integer 60000 does not fit 'short'" + # + sys.stderr = cStringIO.StringIO() + bigvalue = 20000 + del seen[:] + oops_result = "xy" # not None and not an int! + assert ff(bigvalue) == -42 + oops_result = None + assert matches(sys.stderr.getvalue(), """\ +From cffi callback <function$Zcb1 at 0x$>: +Trying to convert the result back to C: +OverflowError: integer 60000 does not fit 'short' + +During the call to 'onerror', another exception occurred: + +TypeError: $integer$ +""") + # + sys.stderr = cStringIO.StringIO() + seen = "not a list" # this makes the oops() function crash + assert ff(bigvalue) == -42 + assert matches(sys.stderr.getvalue(), """\ +From cffi callback <function$Zcb1 at 0x$>: +Trying to convert the result back to C: +OverflowError: integer 60000 does not fit 'short' + +During the call to 'onerror', another exception occurred: + +Traceback (most recent call last): + File "$", line $, in oops + $ +AttributeError: 'str' object has no attribute 'append' +""") finally: sys.stderr = orig_stderr linecache.getline = orig_getline diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py b/pypy/module/_cffi_backend/test/test_ffi_obj.py --- a/pypy/module/_cffi_backend/test/test_ffi_obj.py +++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py @@ -116,6 +116,18 @@ assert ffi.callback("int(int)", lambda x: x + "", -66)(10) == -66 assert ffi.callback("int(int)", lambda x: x + "", error=-66)(10) == -66 + def test_ffi_callback_onerror(self): + import _cffi_backend as _cffi1_backend + ffi = _cffi1_backend.FFI() + seen = [] + def myerror(exc, val, tb): + seen.append(exc) + cb = ffi.callback("int(int)", lambda x: x + "", onerror=myerror) + assert cb(10) == 0 + cb = ffi.callback("int(int)", lambda x:int(1E100), -66, onerror=myerror) + assert cb(10) == -66 + assert seen == [TypeError, OverflowError] + def test_ffi_callback_decorator(self): import _cffi_backend as _cffi1_backend ffi = _cffi1_backend.FFI() @@ -124,6 +136,37 @@ assert deco(lambda x: x + "")(10) == -66 assert deco(lambda x: x + 42)(10) == 52 + def test_ffi_callback_onerror(self): + import _cffi_backend as _cffi1_backend + ffi = _cffi1_backend.FFI() + seen = [] + def oops(*args): + seen.append(args) + + @ffi.callback("int(int)", onerror=oops) + def fn1(x): + return x + "" + assert fn1(10) == 0 + + @ffi.callback("int(int)", onerror=oops, error=-66) + def fn2(x): + return x + "" + assert fn2(10) == -66 + + assert len(seen) == 2 + exc, val, tb = seen[0] + assert exc is TypeError + assert isinstance(val, TypeError) + assert tb.tb_frame.f_code.co_name == "fn1" + exc, val, tb = seen[1] + assert exc is TypeError + assert isinstance(val, TypeError) + assert tb.tb_frame.f_code.co_name == "fn2" + del seen[:] + # + raises(TypeError, ffi.callback, "int(int)", + lambda x: x, onerror=42) # <- not callable + def test_ffi_getctype(self): import _cffi_backend as _cffi1_backend ffi = _cffi1_backend.FFI() _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit