diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2020-04-25 09:34:59 +0200 |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2020-04-25 09:36:28 +0200 |
commit | fb41108bca2f5874957b29f1a2e0db6bba438255 (patch) | |
tree | edc92d2da3e7157d65a77212b24b7511fe5ccd19 | |
parent | 973510e1f4cb5503d09e794f6b1c07e94b7e3165 (diff) | |
download | cython-fb41108bca2f5874957b29f1a2e0db6bba438255.tar.gz |
Work around error that "__reduce_cython__ cannot be found" when re-importing an extension module that was already initialised before.
Backport to 0.29.x.
Closes #3545.
-rw-r--r-- | Cython/Utility/ExtensionTypes.c | 25 | ||||
-rw-r--r-- | Cython/Utility/ObjectHandling.c | 35 | ||||
-rwxr-xr-x | runtests.py | 1 | ||||
-rw-r--r-- | tests/pypy_bugs.txt | 2 | ||||
-rw-r--r-- | tests/run/reimport_failure.srctree | 38 |
5 files changed, 95 insertions, 6 deletions
diff --git a/Cython/Utility/ExtensionTypes.c b/Cython/Utility/ExtensionTypes.c index 4107b9143..5c700cf93 100644 --- a/Cython/Utility/ExtensionTypes.c +++ b/Cython/Utility/ExtensionTypes.c @@ -132,6 +132,7 @@ static void __Pyx_call_next_tp_clear(PyObject* obj, inquiry current_tp_clear) { static int __Pyx_setup_reduce(PyObject* type_obj); /////////////// SetupReduce /////////////// +//@requires: ObjectHandling.c::PyObjectGetAttrStrNoError //@requires: ObjectHandling.c::PyObjectGetAttrStr //@substitute: naming @@ -188,16 +189,28 @@ static int __Pyx_setup_reduce(PyObject* type_obj) { reduce = __Pyx_PyObject_GetAttrStr(type_obj, PYIDENT("__reduce__")); if (unlikely(!reduce)) goto __PYX_BAD; if (reduce == object_reduce || __Pyx_setup_reduce_is_named(reduce, PYIDENT("__reduce_cython__"))) { - reduce_cython = __Pyx_PyObject_GetAttrStr(type_obj, PYIDENT("__reduce_cython__")); if (unlikely(!reduce_cython)) goto __PYX_BAD; - ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce__"), reduce_cython); if (unlikely(ret < 0)) goto __PYX_BAD; - ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD; + reduce_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__reduce_cython__")); + if (likely(reduce_cython)) { + ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce__"), reduce_cython); if (unlikely(ret < 0)) goto __PYX_BAD; + ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD; + } else if (reduce == object_reduce || PyErr_Occurred()) { + // Ignore if we're done, i.e. if 'reduce' already has the right name and the original is gone. + // Otherwise: error. + goto __PYX_BAD; + } setstate = __Pyx_PyObject_GetAttrStr(type_obj, PYIDENT("__setstate__")); if (!setstate) PyErr_Clear(); if (!setstate || __Pyx_setup_reduce_is_named(setstate, PYIDENT("__setstate_cython__"))) { - setstate_cython = __Pyx_PyObject_GetAttrStr(type_obj, PYIDENT("__setstate_cython__")); if (unlikely(!setstate_cython)) goto __PYX_BAD; - ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate__"), setstate_cython); if (unlikely(ret < 0)) goto __PYX_BAD; - ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD; + setstate_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__setstate_cython__")); + if (likely(setstate_cython)) { + ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate__"), setstate_cython); if (unlikely(ret < 0)) goto __PYX_BAD; + ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD; + } else if (!setstate || PyErr_Occurred()) { + // Ignore if we're done, i.e. if 'setstate' already has the right name and the original is gone. + // Otherwise: error. + goto __PYX_BAD; + } } PyType_Modified((PyTypeObject*)type_obj); } diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index bf6f95322..fbb22bea4 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -1361,6 +1361,41 @@ static PyObject* __Pyx_PyObject_GenericGetAttr(PyObject* obj, PyObject* attr_nam #endif +/////////////// PyObjectGetAttrStrNoError.proto /////////////// + +static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name);/*proto*/ + +/////////////// PyObjectGetAttrStrNoError /////////////// +//@requires: PyObjectGetAttrStr +//@requires: Exceptions.c::PyThreadStateGet +//@requires: Exceptions.c::PyErrFetchRestore +//@requires: Exceptions.c::PyErrExceptionMatches + +static void __Pyx_PyObject_GetAttrStr_ClearAttributeError(void) { + __Pyx_PyThreadState_declare + __Pyx_PyThreadState_assign + if (likely(__Pyx_PyErr_ExceptionMatches(PyExc_AttributeError))) + __Pyx_PyErr_Clear(); +} + +static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name) { + PyObject *result; +#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_TYPE_SLOTS && PY_VERSION_HEX >= 0x030700B1 + // _PyObject_GenericGetAttrWithDict() in CPython 3.7+ can avoid raising the AttributeError. + // See https://bugs.python.org/issue32544 + PyTypeObject* tp = Py_TYPE(obj); + if (likely(tp->tp_getattro == PyObject_GenericGetAttr)) { + return _PyObject_GenericGetAttrWithDict(obj, attr_name, NULL, 1); + } +#endif + result = __Pyx_PyObject_GetAttrStr(obj, attr_name); + if (unlikely(!result)) { + __Pyx_PyObject_GetAttrStr_ClearAttributeError(); + } + return result; +} + + /////////////// PyObjectGetAttrStr.proto /////////////// #if CYTHON_USE_TYPE_SLOTS diff --git a/runtests.py b/runtests.py index 5d3e30eba..40843d7e7 100755 --- a/runtests.py +++ b/runtests.py @@ -424,6 +424,7 @@ VER_DEP_MODULES = { # to be unsafe... (2,999): (operator.lt, lambda x: x in ['run.special_methods_T561_py3', 'run.test_raisefrom', + 'run.reimport_failure', # reimports don't do anything in Py2 ]), (3,): (operator.ge, lambda x: x in ['run.non_future_division', 'compile.extsetslice', diff --git a/tests/pypy_bugs.txt b/tests/pypy_bugs.txt index 4152fd63a..77b814cf1 100644 --- a/tests/pypy_bugs.txt +++ b/tests/pypy_bugs.txt @@ -11,6 +11,8 @@ sequential_parallel yield_from_pep380 memoryview_inplace_division +run.reimport_failure + # gc issue? memoryview_in_subclasses external_ref_reassignment diff --git a/tests/run/reimport_failure.srctree b/tests/run/reimport_failure.srctree new file mode 100644 index 000000000..498c9bd36 --- /dev/null +++ b/tests/run/reimport_failure.srctree @@ -0,0 +1,38 @@ +# mode: run +# tag: pep489 + +""" +PYTHON setup.py build_ext -i +PYTHON tester.py +""" + +######## setup.py ######## + +from Cython.Build.Dependencies import cythonize +from distutils.core import setup + +setup( + ext_modules = cythonize("*.pyx"), +) + + +######## failure.pyx ######## + +if globals(): # runtime True to confuse dead code removal + raise ImportError + +cdef class C: + cdef int a + + +######## tester.py ######## + +try: + try: + import failure # 1 + except ImportError: + import failure # 2 +except ImportError: + pass +else: + raise RuntimeError("ImportError was not raised on second import!") |