diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2020-05-09 11:05:44 +0200 |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2020-05-09 11:07:35 +0200 |
commit | 2fb70171d6a6b247ac4695f6690a8e836f0267d2 (patch) | |
tree | f286cbc67c5af074a1ec130abe5b9200f8a7da4c | |
parent | ac694af11d5ae1b2cb13372c33bd535416818f3a (diff) | |
download | cython-gh3578_refleak.tar.gz |
Reimplement __Pyx_PyDict_GetItemStrWithError() as a hacky version in Py2 to get the semantics right of returning a borrowed reference with non-KeyError exceptions left in place.gh3578_refleak
Closes https://github.com/cython/cython/issues/3578
-rw-r--r-- | CHANGES.rst | 4 | ||||
-rw-r--r-- | Cython/Utility/ModuleSetupCode.c | 26 |
2 files changed, 26 insertions, 4 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 3bb62b351..15f87287e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -25,6 +25,10 @@ Bugs fixed could fail to acquire the GIL in some cases on function exit. (Github issue #3590 etc.) +* A reference leak when processing keyword arguments in Py2 was resolved, + that appeared in 3.0a1. + (Github issue #3578) + * The outdated getbuffer/releasebuffer implementations in the NumPy declarations were removed so that buffers declared as ``ndarray`` now use the normal implementation in NumPy. diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index 89289725c..b1ae78d22 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -676,10 +676,28 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStr(PyObject *dict, PyObject #define __Pyx_PyDict_GetItemStr PyDict_GetItem #else static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, PyObject *name) { - PyObject *res = PyObject_GetItem(dict, name); - if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) - PyErr_Clear(); - return res; + // This is tricky - we should return a borrowed reference but not swallow non-KeyError exceptions. 8-| + // But: this function is only used in Py2 and older PyPys, + // and currently only for argument parsing and other non-correctness-critical lookups + // and we know that 'name' is an interned 'str' with pre-calculated hash value (only comparisons can fail), + // thus, performance matters more than correctness here, especially in the "not found" case. +#if CYTHON_COMPILING_IN_PYPY + // So we ignore any exceptions in old PyPys ... + return PyDict_GetItem(dict, name); +#else + // and hack together a stripped-down and modified PyDict_GetItem() in CPython 2. + PyDictEntry *ep; + PyDictObject *mp = (PyDictObject*) dict; + long hash = ((PyStringObject *) name)->ob_shash; + assert(hash != -1); /* hash values of interned strings are always initialised */ + ep = (mp->ma_lookup)(mp, name, hash); + if (ep == NULL) { + // error occurred + return NULL; + } + // found or not found + return ep->me_value; +#endif } #define __Pyx_PyDict_GetItemStr PyDict_GetItem #endif |