diff options
-rw-r--r-- | numpy/__init__.py | 58 | ||||
-rw-r--r-- | numpy/_globals.py | 62 | ||||
-rw-r--r-- | numpy/core/src/multiarray/multiarraymodule.c | 14 | ||||
-rw-r--r-- | numpy/distutils/extension.py | 63 | ||||
-rw-r--r-- | numpy/lib/tests/test_type_check.py | 35 | ||||
-rw-r--r-- | numpy/lib/type_check.py | 12 | ||||
-rw-r--r-- | numpy/tests/test_reloading.py | 14 |
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) |