diff options
author | Charles Harris <charlesr.harris@gmail.com> | 2017-08-06 11:36:11 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-06 11:36:11 -0500 |
commit | f307cec3962926558b6387ebb4ab8d3f4ea3aa34 (patch) | |
tree | b902a845cb2b7a64f78aae7a10776c3daeb16e50 | |
parent | d6539868b353e211b4aaa3a683101e094b3ee1df (diff) | |
parent | 091b8c3b16d39d18f6921f9a17d06c1652b40dca (diff) | |
download | numpy-f307cec3962926558b6387ebb4ab8d3f4ea3aa34.tar.gz |
Merge pull request #9505 from eric-wieser/fix-issubdtype
BUG: issubdtype is inconsistent on types and dtypes
-rw-r--r-- | doc/release/1.14.0-notes.rst | 11 | ||||
-rw-r--r-- | numpy/core/function_base.py | 2 | ||||
-rw-r--r-- | numpy/core/numeric.py | 3 | ||||
-rw-r--r-- | numpy/core/numerictypes.py | 45 | ||||
-rw-r--r-- | numpy/core/tests/test_numerictypes.py | 32 | ||||
-rw-r--r-- | numpy/doc/basics.py | 4 | ||||
-rw-r--r-- | numpy/lib/_iotools.py | 2 | ||||
-rw-r--r-- | numpy/lib/tests/test_function_base.py | 8 | ||||
-rw-r--r-- | numpy/lib/tests/test_index_tricks.py | 2 | ||||
-rw-r--r-- | numpy/lib/tests/test_type_check.py | 2 | ||||
-rw-r--r-- | numpy/matrixlib/defmatrix.py | 2 | ||||
-rw-r--r-- | numpy/matrixlib/tests/test_defmatrix.py | 6 |
12 files changed, 97 insertions, 22 deletions
diff --git a/doc/release/1.14.0-notes.rst b/doc/release/1.14.0-notes.rst index 5e12b9f4a..7a4087dd9 100644 --- a/doc/release/1.14.0-notes.rst +++ b/doc/release/1.14.0-notes.rst @@ -25,6 +25,17 @@ Deprecations Future Changes ============== +``np.issubdtype`` will stop downcasting dtype-like arguments +------------------------------------------------------------ +It would be expected that ``issubdtype(np.float32, 'float64')`` and +``issubdtype(np.float32, np.float64)`` mean the same thing - however, there +was an undocumented special case that translated the former into +``issubdtype(np.float32, np.floating)``, giving the surprising result of True. + +This translation now gives a warning explaining what translation is occuring. +In future, the translation will be disabled, and the first example will be made +equivalent to the second. + Build System Changes ==================== diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py index 7098b8b91..0415e16ac 100644 --- a/numpy/core/function_base.py +++ b/numpy/core/function_base.py @@ -339,7 +339,7 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None): # complex and another is negative and log would produce NaN otherwise start = start + (stop - stop) stop = stop + (start - start) - if _nx.issubdtype(dtype, complex): + if _nx.issubdtype(dtype, _nx.complexfloating): start = start + 0j stop = stop + 0j diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 13a99505c..7d53f2b68 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -436,8 +436,7 @@ def count_nonzero(a, axis=None): if issubdtype(a.dtype, np.number): return (a != 0).sum(axis=axis, dtype=np.intp) - if (issubdtype(a.dtype, np.string_) or - issubdtype(a.dtype, np.unicode_)): + if issubdtype(a.dtype, np.character): nullstr = a.dtype.type('') return (a != nullstr).sum(axis=axis, dtype=np.intp) diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py index 5a8d9abe0..2cb66aab4 100644 --- a/numpy/core/numerictypes.py +++ b/numpy/core/numerictypes.py @@ -85,6 +85,7 @@ from __future__ import division, absolute_import, print_function import types as _types import sys import numbers +import warnings from numpy.compat import bytes, long from numpy.core.multiarray import ( @@ -739,20 +740,46 @@ def issubdtype(arg1, arg2): Examples -------- - >>> np.issubdtype('S1', str) + >>> np.issubdtype('S1', np.string_) True >>> np.issubdtype(np.float64, np.float32) False """ - if issubclass_(arg2, generic): - return issubclass(dtype(arg1).type, arg2) - mro = dtype(arg2).type.mro() - if len(mro) > 1: - val = mro[1] - else: - val = mro[0] - return issubclass(dtype(arg1).type, val) + if not issubclass_(arg1, generic): + arg1 = dtype(arg1).type + if not issubclass_(arg2, generic): + arg2_orig = arg2 + arg2 = dtype(arg2).type + if not isinstance(arg2_orig, dtype): + # weird deprecated behaviour, that tried to infer np.floating from + # float, and similar less obvious things, such as np.generic from + # basestring + mro = arg2.mro() + arg2 = mro[1] if len(mro) > 1 else mro[0] + + def type_repr(x): + """ Helper to produce clear error messages """ + if not isinstance(x, type): + return repr(x) + elif issubclass(x, generic): + return "np.{}".format(x.__name__) + else: + return x.__name__ + + # 1.14, 2017-08-01 + warnings.warn( + "Conversion of the second argument of issubdtype from `{raw}` " + "to `{abstract}` is deprecated. In future, it will be treated " + "as `{concrete} == np.dtype({raw}).type`.".format( + raw=type_repr(arg2_orig), + abstract=type_repr(arg2), + concrete=type_repr(dtype(arg2_orig).type) + ), + FutureWarning, stacklevel=2 + ) + + return issubclass(arg1, arg2) # This dictionary allows look up based on any alias for an array data-type diff --git a/numpy/core/tests/test_numerictypes.py b/numpy/core/tests/test_numerictypes.py index f912e3944..8831cd1bb 100644 --- a/numpy/core/tests/test_numerictypes.py +++ b/numpy/core/tests/test_numerictypes.py @@ -1,6 +1,7 @@ from __future__ import division, absolute_import, print_function import sys +import itertools import numpy as np from numpy.testing import ( @@ -377,5 +378,36 @@ class TestMultipleFields(object): res = self.ary[['f0', 'f2']].tolist() assert_(res == [(1, 3), (5, 7)]) + +class TestIsSubDType(object): + # scalar types can be promoted into dtypes + wrappers = [np.dtype, lambda x: x] + + def test_both_abstract(self): + assert_(np.issubdtype(np.floating, np.inexact)) + assert_(not np.issubdtype(np.inexact, np.floating)) + + def test_same(self): + for cls in (np.float32, np.int32): + for w1, w2 in itertools.product(self.wrappers, repeat=2): + assert_(np.issubdtype(w1(cls), w2(cls))) + + def test_subclass(self): + # note we cannot promote floating to a dtype, as it would turn into a + # concrete type + for w in self.wrappers: + assert_(np.issubdtype(w(np.float32), np.floating)) + assert_(np.issubdtype(w(np.float64), np.floating)) + + def test_subclass_backwards(self): + for w in self.wrappers: + assert_(not np.issubdtype(np.floating, w(np.float32))) + assert_(not np.issubdtype(np.floating, w(np.float64))) + + def test_sibling_class(self): + for w1, w2 in itertools.product(self.wrappers, repeat=2): + assert_(not np.issubdtype(w1(np.float32), w2(np.float64))) + assert_(not np.issubdtype(w1(np.float64), w2(np.float32))) + if __name__ == "__main__": run_module_suite() diff --git a/numpy/doc/basics.py b/numpy/doc/basics.py index 083d55a84..10d7248c1 100644 --- a/numpy/doc/basics.py +++ b/numpy/doc/basics.py @@ -114,10 +114,10 @@ properties of the type, such as whether it is an integer:: >>> d dtype('int32') - >>> np.issubdtype(d, int) + >>> np.issubdtype(d, np.integer) True - >>> np.issubdtype(d, float) + >>> np.issubdtype(d, np.floating) False diff --git a/numpy/lib/_iotools.py b/numpy/lib/_iotools.py index 304bba3d3..1874c2e97 100644 --- a/numpy/lib/_iotools.py +++ b/numpy/lib/_iotools.py @@ -527,7 +527,7 @@ class StringConverter(object): _mapper.append((nx.int64, int, -1)) _mapper.extend([(nx.floating, float, nx.nan), - (complex, _bytes_to_complex, nx.nan + 0j), + (nx.complexfloating, _bytes_to_complex, nx.nan + 0j), (nx.longdouble, nx.longdouble, nx.nan), (nx.string_, bytes, b'???')]) diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index ad840f8ef..151c20a4b 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -1711,16 +1711,16 @@ class TestHistogram(object): # Check the type of the returned histogram a = np.arange(10) + .5 h, b = histogram(a) - assert_(np.issubdtype(h.dtype, int)) + assert_(np.issubdtype(h.dtype, np.integer)) h, b = histogram(a, normed=True) - assert_(np.issubdtype(h.dtype, float)) + assert_(np.issubdtype(h.dtype, np.floating)) h, b = histogram(a, weights=np.ones(10, int)) - assert_(np.issubdtype(h.dtype, int)) + assert_(np.issubdtype(h.dtype, np.integer)) h, b = histogram(a, weights=np.ones(10, float)) - assert_(np.issubdtype(h.dtype, float)) + assert_(np.issubdtype(h.dtype, np.floating)) def test_f32_rounding(self): # gh-4799, check that the rounding of the edges works with float32 diff --git a/numpy/lib/tests/test_index_tricks.py b/numpy/lib/tests/test_index_tricks.py index f06406c9e..452b3d6a2 100644 --- a/numpy/lib/tests/test_index_tricks.py +++ b/numpy/lib/tests/test_index_tricks.py @@ -243,7 +243,7 @@ class TestIx_(object): for k, (a, sz) in enumerate(zip(arrays, sizes)): assert_equal(a.shape[k], sz) assert_(all(sh == 1 for j, sh in enumerate(a.shape) if j != k)) - assert_(np.issubdtype(a.dtype, int)) + assert_(np.issubdtype(a.dtype, np.integer)) def test_bool(self): bool_a = [True, False, True, True] diff --git a/numpy/lib/tests/test_type_check.py b/numpy/lib/tests/test_type_check.py index d863e5924..e05079981 100644 --- a/numpy/lib/tests/test_type_check.py +++ b/numpy/lib/tests/test_type_check.py @@ -420,7 +420,7 @@ class TestArrayConversion(object): def test_asfarray(self): a = asfarray(np.array([1, 2, 3])) assert_equal(a.__class__, np.ndarray) - assert_(np.issubdtype(a.dtype, float)) + assert_(np.issubdtype(a.dtype, np.floating)) if __name__ == "__main__": run_module_suite() diff --git a/numpy/matrixlib/defmatrix.py b/numpy/matrixlib/defmatrix.py index 2aed3ebde..e016b5f4c 100644 --- a/numpy/matrixlib/defmatrix.py +++ b/numpy/matrixlib/defmatrix.py @@ -137,7 +137,7 @@ def matrix_power(M, n): M = asanyarray(M) if M.ndim != 2 or M.shape[0] != M.shape[1]: raise ValueError("input must be a square array") - if not issubdtype(type(n), int): + if not issubdtype(type(n), N.integer): raise TypeError("exponent must be an integer") from numpy.linalg import inv diff --git a/numpy/matrixlib/tests/test_defmatrix.py b/numpy/matrixlib/tests/test_defmatrix.py index e4c3c49fb..77f262031 100644 --- a/numpy/matrixlib/tests/test_defmatrix.py +++ b/numpy/matrixlib/tests/test_defmatrix.py @@ -249,6 +249,12 @@ class TestAlgebra(object): assert_array_almost_equal(m4, np.dot(m2, m2)) assert_array_almost_equal(np.dot(mi, m), np.eye(2)) + def test_scalar_type_pow(self): + m = matrix([[1, 2], [3, 4]]) + for scalar_t in [np.int8, np.uint8]: + two = scalar_t(2) + assert_array_almost_equal(m ** 2, m ** two) + def test_notimplemented(self): '''Check that 'not implemented' operations produce a failure.''' A = matrix([[1., 2.], |