Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r54115:623bcea85df3 Date: 2012-04-01 12:30 +0200 http://bitbucket.org/pypy/pypy/changeset/623bcea85df3/
Log: Extension to cpyext proposed by Stefan Behnel: PyErr_{Get,Set}ExcInfo(). diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py --- a/pypy/interpreter/executioncontext.py +++ b/pypy/interpreter/executioncontext.py @@ -167,6 +167,11 @@ frame = self.getnextframe_nohidden(frame) return None + def set_sys_exc_info(self, operror): + frame = self.gettopframe_nohidden() + if frame: # else, the exception goes nowhere and is lost + frame.last_exception = operror + def settrace(self, w_func): """Set the global trace function.""" if self.space.is_w(w_func, self.space.w_None): diff --git a/pypy/module/cpyext/pyerrors.py b/pypy/module/cpyext/pyerrors.py --- a/pypy/module/cpyext/pyerrors.py +++ b/pypy/module/cpyext/pyerrors.py @@ -315,3 +315,63 @@ It may be called without holding the interpreter lock.""" space.check_signal_action.set_interrupt() +@cpython_api([PyObjectP, PyObjectP, PyObjectP], lltype.Void) +def PyErr_GetExcInfo(space, ptype, pvalue, ptraceback): + """---Cython extension--- + + Retrieve the exception info, as known from ``sys.exc_info()``. This + refers to an exception that was already caught, not to an exception + that was freshly raised. Returns new references for the three + objects, any of which may be *NULL*. Does not modify the exception + info state. + + .. note:: + + This function is not normally used by code that wants to handle + exceptions. Rather, it can be used when code needs to save and + restore the exception state temporarily. Use + :c:func:`PyErr_SetExcInfo` to restore or clear the exception + state. + """ + ec = space.getexecutioncontext() + operror = ec.sys_exc_info() + if operror: + ptype[0] = make_ref(space, operror.w_type) + pvalue[0] = make_ref(space, operror.get_w_value(space)) + ptraceback[0] = make_ref(space, space.wrap(operror.get_traceback())) + else: + ptype[0] = lltype.nullptr(PyObject.TO) + pvalue[0] = lltype.nullptr(PyObject.TO) + ptraceback[0] = lltype.nullptr(PyObject.TO) + +@cpython_api([PyObject, PyObject, PyObject], lltype.Void) +def PyErr_SetExcInfo(space, w_type, w_value, w_traceback): + """---Cython extension--- + + Set the exception info, as known from ``sys.exc_info()``. This refers + to an exception that was already caught, not to an exception that was + freshly raised. This function steals the references of the arguments. + To clear the exception state, pass *NULL* for all three arguments. + For general rules about the three arguments, see :c:func:`PyErr_Restore`. + + .. note:: + + This function is not normally used by code that wants to handle + exceptions. Rather, it can be used when code needs to save and + restore the exception state temporarily. Use + :c:func:`PyErr_GetExcInfo` to read the exception state. + """ + if w_value is None or space.is_w(w_value, space.w_None): + operror = None + else: + if w_traceback is None or space.is_w(w_traceback, space.w_None): + tb = None + else: + tb = w_traceback + operror = OperationError(w_type, w_value, tb) + # + ec = space.getexecutioncontext() + ec.set_sys_exc_info(operror) + Py_DecRef(space, w_type) + Py_DecRef(space, w_value) + Py_DecRef(space, w_traceback) diff --git a/pypy/module/cpyext/test/test_pyerrors.py b/pypy/module/cpyext/test/test_pyerrors.py --- a/pypy/module/cpyext/test/test_pyerrors.py +++ b/pypy/module/cpyext/test/test_pyerrors.py @@ -218,3 +218,51 @@ assert e.filename == "blyf" assert e.errno == errno.EBADF assert e.strerror == os.strerror(errno.EBADF) + + def test_GetSetExcInfo(self): + import sys + module = self.import_extension('foo', [ + ("getset_exc_info", "METH_VARARGS", + r''' + PyObject *type, *val, *tb; + PyObject *new_type, *new_val, *new_tb; + PyObject *result; + + if (!PyArg_ParseTuple(args, "OOO", &new_type, &new_val, &new_tb)) + return NULL; + + PyErr_GetExcInfo(&type, &val, &tb); + + Py_INCREF(new_type); + Py_INCREF(new_val); + Py_INCREF(new_tb); + PyErr_SetExcInfo(new_type, new_val, new_tb); + + result = Py_BuildValue("OOO", + type ? type : Py_None, + val ? val : Py_None, + tb ? tb : Py_None); + Py_XDECREF(type); + Py_XDECREF(val); + Py_XDECREF(tb); + return result; + ''' + ), + ]) + try: + raise ValueError(5) + except ValueError, old_exc: + new_exc = TypeError("TEST") + orig_sys_exc_info = sys.exc_info() + orig_exc_info = module.getset_exc_info(new_exc.__class__, + new_exc, None) + new_sys_exc_info = sys.exc_info() + new_exc_info = module.getset_exc_info(*orig_exc_info) + reset_sys_exc_info = sys.exc_info() + + assert orig_exc_info[0] is old_exc.__class__ + assert orig_exc_info[1] is old_exc + assert orig_exc_info == orig_sys_exc_info + assert orig_exc_info == reset_sys_exc_info + assert new_exc_info == (new_exc.__class__, new_exc, None) + assert new_exc_info == new_sys_exc_info _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit