summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/__init__.py58
-rw-r--r--numpy/_globals.py62
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c14
-rw-r--r--numpy/distutils/extension.py63
-rw-r--r--numpy/lib/tests/test_type_check.py35
-rw-r--r--numpy/lib/type_check.py12
-rw-r--r--numpy/tests/test_reloading.py14
7 files changed, 165 insertions, 93 deletions
diff --git a/numpy/__init__.py b/numpy/__init__.py
index 4be750c19..5384d61ab 100644
--- a/numpy/__init__.py
+++ b/numpy/__init__.py
@@ -109,56 +109,8 @@ from __future__ import division, absolute_import, print_function
import sys
import warnings
-# Disallow reloading numpy. Doing that does nothing to change previously
-# loaded modules, which would need to be reloaded separately, but it does
-# change the identity of the warnings and sentinal classes defined below
-# with dire consequences when checking for identity.
-if '_is_loaded' in globals():
- raise RuntimeError('Reloading numpy is not supported')
-_is_loaded = True
-
-
-# Define some global warnings and the _NoValue sentinal. Defining them here
-# means that their identity will change if numpy is reloaded, hence if that is
-# to be allowed they should be moved into their own, non-reloadable module.
-# Note that these should be defined (or imported) before the other imports.
-class ModuleDeprecationWarning(DeprecationWarning):
- """Module deprecation warning.
-
- The nose tester turns ordinary Deprecation warnings into test failures.
- That makes it hard to deprecate whole modules, because they get
- imported by default. So this is a special Deprecation warning that the
- nose tester will let pass without making tests fail.
-
- """
- pass
-
-
-class VisibleDeprecationWarning(UserWarning):
- """Visible deprecation warning.
-
- By default, python will not show deprecation warnings, so this class
- can be used when a very visible warning is helpful, for example because
- the usage is most likely a user bug.
-
- """
- pass
-
-
-class _NoValue:
- """Special keyword value.
-
- This class may be used as the default value assigned to a deprecated
- keyword in order to check if it has been given a user defined value.
- """
- pass
-
-
-# oldnumeric and numarray were removed in 1.9. In case some packages import
-# but do not use them, we define them here for backward compatibility.
-oldnumeric = 'removed'
-numarray = 'removed'
-
+from ._globals import ModuleDeprecationWarning, VisibleDeprecationWarning
+from ._globals import _NoValue
# We first need to detect if we're being called as part of the numpy setup
# procedure itself in a reliable manner.
@@ -177,6 +129,7 @@ else:
its source directory; please exit the numpy source tree, and relaunch
your python interpreter from there."""
raise ImportError(msg)
+
from .version import git_revision as __git_revision__
from .version import version as __version__
@@ -239,3 +192,8 @@ else:
warnings.filterwarnings("ignore", message="numpy.dtype size changed")
warnings.filterwarnings("ignore", message="numpy.ufunc size changed")
warnings.filterwarnings("ignore", message="numpy.ndarray size changed")
+
+ # oldnumeric and numarray were removed in 1.9. In case some packages import
+ # but do not use them, we define them here for backward compatibility.
+ oldnumeric = 'removed'
+ numarray = 'removed'
diff --git a/numpy/_globals.py b/numpy/_globals.py
new file mode 100644
index 000000000..64a84da96
--- /dev/null
+++ b/numpy/_globals.py
@@ -0,0 +1,62 @@
+"""
+Module defining global singleton classes.
+
+This module raises a RuntimeError if an attempt to reload it is made. In that
+way the identities of the classes defined here are fixed and will remain so
+even if numpy itself is reloaded. In particular, a function like the following
+will still work correctly after numpy is reloaded::
+
+ def foo(arg=np._NoValue):
+ if arg is np._NoValue:
+ ...
+
+That was not the case when the singleton classes were defined in the numpy
+``__init__.py`` file. See gh-7844 for a discussion of the reload problem that
+motivated this module.
+
+"""
+from __future__ import division, absolute_import, print_function
+
+
+__ALL__ = [
+ 'ModuleDeprecationWarning', 'VisibleDeprecationWarning', '_NoValue'
+ ]
+
+
+# Disallow reloading this module so as to preserve the identities of the
+# classes defined here.
+if '_is_loaded' in globals():
+ raise RuntimeError('Reloading numpy._globals is not allowed')
+_is_loaded = True
+
+
+class ModuleDeprecationWarning(DeprecationWarning):
+ """Module deprecation warning.
+
+ The nose tester turns ordinary Deprecation warnings into test failures.
+ That makes it hard to deprecate whole modules, because they get
+ imported by default. So this is a special Deprecation warning that the
+ nose tester will let pass without making tests fail.
+
+ """
+ pass
+
+
+class VisibleDeprecationWarning(UserWarning):
+ """Visible deprecation warning.
+
+ By default, python will not show deprecation warnings, so this class
+ can be used when a very visible warning is helpful, for example because
+ the usage is most likely a user bug.
+
+ """
+ pass
+
+
+class _NoValue:
+ """Special keyword value.
+
+ This class may be used as the default value assigned to a deprecated
+ keyword in order to check if it has been given a user defined value.
+ """
+ pass
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index d90debc6f..39acf87fa 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -3635,7 +3635,6 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type,
PyArrayMultiIterObject* in_iter = NULL;
PyArrayObject* result = NULL;
PyArrayIterObject* out_iter = NULL;
- PyObject* args_tuple = NULL;
Py_ssize_t i, n, nargs;
nargs = PySequence_Size(args) + 1;
@@ -3672,18 +3671,18 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type,
goto err;
}
- args_tuple = PyTuple_New(n);
- if (args_tuple == NULL) {
- goto err;
- }
-
while (PyArray_MultiIter_NOTDONE(in_iter)) {
PyObject* item_result;
+ PyObject* args_tuple = PyTuple_New(n);
+ if (args_tuple == NULL) {
+ goto err;
+ }
for (i = 0; i < n; i++) {
PyArrayIterObject* it = in_iter->iters[i];
PyObject* arg = PyArray_ToScalar(PyArray_ITER_DATA(it), it->ao);
if (arg == NULL) {
+ Py_DECREF(args_tuple);
goto err;
}
/* Steals ref to arg */
@@ -3691,6 +3690,7 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type,
}
item_result = PyObject_CallObject(method, args_tuple);
+ Py_DECREF(args_tuple);
if (item_result == NULL) {
goto err;
}
@@ -3709,14 +3709,12 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type,
Py_DECREF(in_iter);
Py_DECREF(out_iter);
- Py_DECREF(args_tuple);
return (PyObject*)result;
err:
Py_XDECREF(in_iter);
Py_XDECREF(out_iter);
- Py_XDECREF(args_tuple);
Py_XDECREF(result);
return 0;
diff --git a/numpy/distutils/extension.py b/numpy/distutils/extension.py
index 344c66da0..ebb263bd1 100644
--- a/numpy/distutils/extension.py
+++ b/numpy/distutils/extension.py
@@ -20,36 +20,39 @@ cxx_ext_re = re.compile(r'.*[.](cpp|cxx|cc)\Z', re.I).match
fortran_pyf_ext_re = re.compile(r'.*[.](f90|f95|f77|for|ftn|f|pyf)\Z', re.I).match
class Extension(old_Extension):
- def __init__ (self, name, sources,
- include_dirs=None,
- define_macros=None,
- undef_macros=None,
- library_dirs=None,
- libraries=None,
- runtime_library_dirs=None,
- extra_objects=None,
- extra_compile_args=None,
- extra_link_args=None,
- export_symbols=None,
- swig_opts=None,
- depends=None,
- language=None,
- f2py_options=None,
- module_dirs=None,
- extra_f77_compile_args=None,
- extra_f90_compile_args=None,
- ):
- old_Extension.__init__(self, name, [],
- include_dirs,
- define_macros,
- undef_macros,
- library_dirs,
- libraries,
- runtime_library_dirs,
- extra_objects,
- extra_compile_args,
- extra_link_args,
- export_symbols)
+ def __init__ (
+ self, name, sources,
+ include_dirs=None,
+ define_macros=None,
+ undef_macros=None,
+ library_dirs=None,
+ libraries=None,
+ runtime_library_dirs=None,
+ extra_objects=None,
+ extra_compile_args=None,
+ extra_link_args=None,
+ export_symbols=None,
+ swig_opts=None,
+ depends=None,
+ language=None,
+ f2py_options=None,
+ module_dirs=None,
+ extra_f77_compile_args=None,
+ extra_f90_compile_args=None,):
+
+ old_Extension.__init__(
+ self, name, [],
+ include_dirs=include_dirs,
+ define_macros=define_macros,
+ undef_macros=undef_macros,
+ library_dirs=library_dirs,
+ libraries=libraries,
+ runtime_library_dirs=runtime_library_dirs,
+ extra_objects=extra_objects,
+ extra_compile_args=extra_compile_args,
+ extra_link_args=extra_link_args,
+ export_symbols=export_symbols)
+
# Avoid assert statements checking that sources contains strings:
self.sources = sources
diff --git a/numpy/lib/tests/test_type_check.py b/numpy/lib/tests/test_type_check.py
index f7430c27d..93a4da97a 100644
--- a/numpy/lib/tests/test_type_check.py
+++ b/numpy/lib/tests/test_type_check.py
@@ -148,6 +148,41 @@ class TestIscomplexobj(TestCase):
z = np.array([-1j, 0, -1])
assert_(iscomplexobj(z))
+ def test_scalar(self):
+ assert_(not iscomplexobj(1.0))
+ assert_(iscomplexobj(1+0j))
+
+ def test_list(self):
+ assert_(iscomplexobj([3, 1+0j, True]))
+ assert_(not iscomplexobj([3, 1, True]))
+
+ def test_duck(self):
+ class DummyComplexArray:
+ @property
+ def dtype(self):
+ return np.dtype(complex)
+ dummy = DummyComplexArray()
+ assert_(iscomplexobj(dummy))
+
+ def test_pandas_duck(self):
+ # This tests a custom np.dtype duck-typed class, such as used by pandas
+ # (pandas.core.dtypes)
+ class PdComplex(np.complex128):
+ pass
+ class PdDtype(object):
+ name = 'category'
+ names = None
+ type = PdComplex
+ kind = 'c'
+ str = '<c16'
+ base = np.dtype('complex128')
+ class DummyPd:
+ @property
+ def dtype(self):
+ return PdDtype
+ dummy = DummyPd()
+ assert_(iscomplexobj(dummy))
+
class TestIsrealobj(TestCase):
def test_basic(self):
diff --git a/numpy/lib/type_check.py b/numpy/lib/type_check.py
index d6e0704ad..f620d49d5 100644
--- a/numpy/lib/type_check.py
+++ b/numpy/lib/type_check.py
@@ -266,7 +266,15 @@ def iscomplexobj(x):
True
"""
- return issubclass(asarray(x).dtype.type, _nx.complexfloating)
+ try:
+ dtype = x.dtype
+ except AttributeError:
+ dtype = asarray(x).dtype
+ try:
+ return issubclass(dtype.type, _nx.complexfloating)
+ except AttributeError:
+ return False
+
def isrealobj(x):
"""
@@ -300,7 +308,7 @@ def isrealobj(x):
False
"""
- return not issubclass(asarray(x).dtype.type, _nx.complexfloating)
+ return not iscomplexobj(x)
#-----------------------------------------------------------------------------
diff --git a/numpy/tests/test_reloading.py b/numpy/tests/test_reloading.py
index 141e11f6c..ca651c874 100644
--- a/numpy/tests/test_reloading.py
+++ b/numpy/tests/test_reloading.py
@@ -2,7 +2,6 @@ from __future__ import division, absolute_import, print_function
import sys
-import numpy as np
from numpy.testing import assert_raises, assert_, run_module_suite
if sys.version_info[:2] >= (3, 4):
@@ -10,13 +9,22 @@ if sys.version_info[:2] >= (3, 4):
else:
from imp import reload
-def test_reloading_exception():
+def test_numpy_reloading():
# gh-7844. Also check that relevant globals retain their identity.
+ import numpy as np
+ import numpy._globals
+
_NoValue = np._NoValue
VisibleDeprecationWarning = np.VisibleDeprecationWarning
ModuleDeprecationWarning = np.ModuleDeprecationWarning
- assert_raises(RuntimeError, reload, np)
+ reload(np)
+ assert_(_NoValue is np._NoValue)
+ assert_(ModuleDeprecationWarning is np.ModuleDeprecationWarning)
+ assert_(VisibleDeprecationWarning is np.VisibleDeprecationWarning)
+
+ assert_raises(RuntimeError, reload, numpy._globals)
+ reload(np)
assert_(_NoValue is np._NoValue)
assert_(ModuleDeprecationWarning is np.ModuleDeprecationWarning)
assert_(VisibleDeprecationWarning is np.VisibleDeprecationWarning)