summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2020-05-19 11:03:29 -0500
committerSebastian Berg <sebastian@sipsolutions.net>2020-05-19 13:25:19 -0500
commitba14823f1bf846807985417a69cb05a864683c09 (patch)
tree00925f8b38cf61f466d738eaab3a8606b2d163e2 /numpy
parentd329a66dbb9710aefd03cce6a8b0f46da51490ca (diff)
downloadnumpy-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.c11
-rw-r--r--numpy/tests/test_reloading.py26
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