summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Behnel <stefan_ml@behnel.de>2020-05-09 11:05:44 +0200
committerStefan Behnel <stefan_ml@behnel.de>2020-05-09 11:07:35 +0200
commit2fb70171d6a6b247ac4695f6690a8e836f0267d2 (patch)
treef286cbc67c5af074a1ec130abe5b9200f8a7da4c
parentac694af11d5ae1b2cb13372c33bd535416818f3a (diff)
downloadcython-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.rst4
-rw-r--r--Cython/Utility/ModuleSetupCode.c26
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