[pypy-issue] Issue #3120: PyErr_Restore does not restore traceback (pypy/pypy)
Kirill Smelkov
issues-reply at bitbucket.org
Wed Nov 20 10:21:11 EST 2019
New issue 3120: PyErr_Restore does not restore traceback
https://bitbucket.org/pypy/pypy/issues/3120/pyerr_restore-does-not-restore-traceback
Kirill Smelkov:
Hello up there. I again hit PyPy vs CPython incomatibility while moving parts of Pygolang to Cython. Please consider the following minimal example:
\(mymod.pyx\)
```python
# cython: language_level=2
cdef extern from "Python.h":
ctypedef struct PyObject
PyObject *PyObject_CallFunction(PyObject *f, const char *fmt, ...)
void PyErr_Fetch(PyObject **pexc_type, PyObject **pexc_value, PyObject **pexc_tb)
void PyErr_Restore(PyObject *exc_type, PyObject *exc_value, PyObject *exc_tb)
void Py_XINCREF(PyObject*)
# PyExc wraps information about Python exception
cdef class PyExc:
# retrieved by PyErr_Fetch - keep 1 reference to each
cdef PyObject *exc_type
cdef PyObject *exc_value
cdef PyObject *exc_tb
# call_pyfunc calls f and return PyExc describing exception state after f call.
# f must raise an exception.
def call_pyfunc(f):
cdef PyExc pyexc = PyExc()
cdef PyObject *ret
ret = PyObject_CallFunction(<PyObject*>f, NULL)
if ret != NULL:
raise AssertionError('f must raise an exception')
PyErr_Fetch(&pyexc.exc_type, &pyexc.exc_value, &pyexc.exc_tb)
return pyexc
# reraise_pyexc reraises pyexc including its original traceback.
def reraise_pyexc(PyExc pyexc not None):
_pyexc_reraise(pyexc)
cdef void _pyexc_reraise(PyExc pyexc) except *:
# PyErr_Restore takes 1 reference to restored objects.
# We want to keep pyerr itself alive and valid.
Py_XINCREF(pyexc.exc_type);
Py_XINCREF(pyexc.exc_value);
Py_XINCREF(pyexc.exc_tb);
PyErr_Restore(pyexc.exc_type, pyexc.exc_value, pyexc.exc_tb)
```
\(mytest.py\)
```python
#!/usr/bin/env python
import mymod
def f(): g()
def g(): h()
def h(): 1/0
def main():
e = mymod.call_pyfunc(f)
#print(e)
i(e)
def i(e): j(e)
def j(e): k(e)
def k(e): mymod.reraise_pyexc(e)
if __name__ == '__main__':
main()
```
When running with CPython `mytest.py` prints traceback that includes _both_ i-j-k _and_ f-g-h:
```
(neo) (z-dev) (g.env) kirr at deco:~/src/tools/go/pygolang/x$ python mytest.py
Traceback (most recent call last):
File "mytest.py", line 19, in <module>
main()
File "mytest.py", line 12, in main
i(e)
File "mytest.py", line 14, in i
def i(e): j(e)
File "mytest.py", line 15, in j
def j(e): k(e)
File "mytest.py", line 16, in k
def k(e): mymod.reraise_pyexc(e)
File "mymod.pyx", line 33, in mymod.reraise_pyexc
_pyexc_reraise(pyexc)
File "mytest.py", line 5, in f
def f(): g()
File "mytest.py", line 6, in g
def g(): h()
File "mytest.py", line 7, in h
def h(): 1/0
ZeroDivisionError: integer division or modulo by zero
```
However when run under PyPy \(I verified up till today’s nightly\) the traceback _does_ _not_ include f-g-h:
```
(pypy.venv) test1 at deco:~/pypy/pygolang/x$ pypy mytest.py
Traceback (most recent call last):
File "mytest.py", line 19, in <module>
main()
File "mytest.py", line 12, in main
i(e)
File "mytest.py", line 14, in i
def i(e): j(e)
File "mytest.py", line 15, in j
def j(e): k(e)
File "mytest.py", line 16, in k
def k(e): mymod.reraise_pyexc(e)
File "mymod.pyx", line 33, in mymod.reraise_pyexc
_pyexc_reraise(pyexc)
ZeroDivisionError: integer division by zero
```
This minimal example models what happens in C/Pyx version of `sync.WorkGroup` in Pygolang and preserving the inner traceback is important there.
```
$ python
Python 2.7.15+ (default, Feb 3 2019, 13:13:16)
[GCC 8.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
```
```
(pypy.venv) test1 at deco:~/pypy/pygolang/x$ pypy
Python 2.7.13 (dcc4efe7355e, Nov 19 2019, 23:00:15)
[PyPy 7.3.0-alpha0 with GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``pypy is like sausages''
```
Thanks beforehand,
Kirill
/cc @{557058:7cb88866-fb18-487e-a2dc-b19de69f5f0b}
More information about the pypy-issue
mailing list