diff options
author | Sebastian Berg <sebastian@sipsolutions.net> | 2020-05-19 11:03:29 -0500 |
---|---|---|
committer | Sebastian Berg <sebastian@sipsolutions.net> | 2020-05-19 13:25:19 -0500 |
commit | ba14823f1bf846807985417a69cb05a864683c09 (patch) | |
tree | 00925f8b38cf61f466d738eaab3a8606b2d163e2 /numpy | |
parent | d329a66dbb9710aefd03cce6a8b0f46da51490ca (diff) | |
download | numpy-ba14823f1bf846807985417a69cb05a864683c09.tar.gz |
BUG: Allow attaching documentation twice in add_docstring
This is technically not a bug, but some IDEs and IPython have
autoreload magic which can mean that NumPy gets reloaded a second
time. This is not safe, but when it happens ignoring that an
identical docstring is already attached fixes the issue.
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/multiarray/compiled_base.c | 11 | ||||
-rw-r--r-- | numpy/tests/test_reloading.py | 26 |
2 files changed, 34 insertions, 3 deletions
diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index 308e72009..5cf18049d 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -1425,7 +1425,7 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) #else char *docstr; #endif - static char *msg = "already has a docstring"; + static char *msg = "already has a different docstring"; PyObject *tp_dict = PyArrayDescr_Type.tp_dict; PyObject *myobj; static PyTypeObject *PyMemberDescr_TypePtr = NULL; @@ -1482,7 +1482,7 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) if (!(doc)) { \ doc = docstr; \ } \ - else { \ + else if (strcmp(doc, docstr) != 0) { \ PyErr_Format(PyExc_RuntimeError, "%s method %s", name, msg); \ return NULL; \ } \ @@ -1507,7 +1507,12 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) PyObject *doc_attr; doc_attr = PyObject_GetAttrString(obj, "__doc__"); - if (doc_attr != NULL && doc_attr != Py_None) { + if (doc_attr != NULL && doc_attr != Py_None && + (PyUnicode_Compare(doc_attr, str) != 0)) { + if (PyErr_Occurred()) { + /* error during PyUnicode_Compare */ + return NULL; + } PyErr_Format(PyExc_RuntimeError, "object %s", msg); return NULL; } diff --git a/numpy/tests/test_reloading.py b/numpy/tests/test_reloading.py index 860832be8..61ae91b00 100644 --- a/numpy/tests/test_reloading.py +++ b/numpy/tests/test_reloading.py @@ -1,8 +1,12 @@ from numpy.testing import assert_raises, assert_, assert_equal from numpy.compat import pickle +import sys +import subprocess +import textwrap from importlib import reload + def test_numpy_reloading(): # gh-7844. Also check that relevant globals retain their identity. import numpy as np @@ -29,3 +33,25 @@ def test_novalue(): assert_equal(repr(np._NoValue), '<no value>') assert_(pickle.loads(pickle.dumps(np._NoValue, protocol=proto)) is np._NoValue) + + +def test_full_reimport(): + """At the time of writing this, it is *not* truly supported, but + apparently enough users rely on it, for it to be an annoying change + when it started failing previously. + """ + # Test within a new process, to ensure that we do not mess with the + # global state during the test run (could lead to cryptic test failures). + # This is generally unsafe, especially, since we also reload the C-modules. + code = textwrap.dedent(r""" + import sys + import numpy as np + + for k in list(sys.modules.keys()): + if "numpy" in k: + del sys.modules[k] + + import numpy as np + """) + p = subprocess.run([sys.executable, '-c', code]) + assert p.returncode == 0 |