10000 Merge pull request #9505 from eric-wieser/fix-issubdtype · numpy/numpy@f307cec · GitHub
[go: up one dir, main page]

Skip to content

Commit f307cec

Browse files
authored
Merge pull request #9505 from eric-wieser/fix-issubdtype
BUG: issubdtype is inconsistent on types and dtypes
2 parents d653986 + 091b8c3 commit f307cec

File tree

12 files changed

+97
-22
lines changed

12 files changed

+97
-22
lines changed

doc/release/1.14.0-notes.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ Deprecations
2525
Future Changes
2626
==============
2727

28+
``np.issubdtype`` will stop downcasting dtype-like arguments
29+
------------------------------------------------------------
30+
It would be expected that ``issubdtype(np.float32, 'float64')`` and
31+
``issubdtype(np.float32, np.float64)`` mean the same thing - however, there
32+
was an undocumented special case that translated the former into
33+
``issubdtype(np.float32, np.floating)``, giving the surprising result of True.
34+
35+
This translation now gives a warning explaining what translation is occuring.
36+
In future, the translation will be disabled, and the first example will be made
37+
equivalent to the second.
38+
2839

2940
Build System Changes
3041
====================

numpy/core/function_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None):
339339
# complex and another is negative and log would produce NaN otherwise
340340
start = start + (stop - stop)
341341
stop = stop + (start - start)
342-
if _nx.issubdtype(dtype, complex):
342+
if _nx.issubdtype(dtype, _nx.complexfloating):
343343
start = start + 0j
344344
stop = stop + 0j
345345

numpy/core/numeric.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,7 @@ def count_nonzero(a, axis=None):
436436
if issubdtype(a.dtype, np.number):
437437
return (a != 0).sum(axis=axis, dtype=np.intp)
438438

439-
if (issubdtype(a.dtype, np.string_) or
440-
issubdtype(a.dtype, np.unicode_)):
439+
if issubdtype(a.dtype, np.character):
441440
nullstr = a.dtype.type('')
442441
return (a != nullstr).sum(axis=axis, dtype=np.intp)
443442

numpy/core/numerictypes.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
import types as _types
8686
import sys
8787
import numbers
88+
import warnings
8889

8990
from numpy.compat import bytes, long
9091
from numpy.core.multiarray import (
@@ -739,20 +740,46 @@ def issubdtype(arg1, arg2):
739740
740741
Examples
741742
--------
742-
>>> np.issubdtype('S1', str)
743+
>>> np.issubdtype('S1', np.string_)
743744
True
744745
>>> np.issubdtype(np.float64, np.float32)
745746
False
746747
747748
"""
748-
if issubclass_(arg2, generic):
749-
return issubclass(dtype(arg1).type, arg2)
750-
mro = dtype(arg2).type.mro()
751-
if len(mro) > 1:
752-
val = mro[1]
753-
else:
754-
val = mro[0]
755-
return issubclass(dtype(arg1).type, val)
749+
if not issubclass_(arg1, generic):
750+
arg1 = dtype(arg1).type
751+
if not issubclass_(arg2, generic):
752+
arg2_orig = arg2
753+
arg2 = dtype(arg2).type
754+
if not isinstance(arg2_orig, dtype):
755+
# weird deprecated behaviour, that tried to infer np.floating from
756+
# float, and similar less obvious things, such as np.generic from
757+
# basestring
758+
mro = arg2.mro()
759+
arg2 = mro[1] if len(mro) > 1 else mro[0]
760+
761+
def type_repr(x):
762+
""" Helper to produce clear error messages """
763+
if not isinstance(x, type):
764+
return repr(x)
765+
elif issubclass(x, generic):
766+
return "np.{}".format(x.__name__)
767+
else:
768+
return x.__name__
769+
770+
# 1.14, 2017-08-01
771+
warnings.warn(
772+
"Conversion of the second argument of issubdtype from `{raw}` "
773+
"to `{abstract}` is deprecated. In future, it will be treated "
774+
"as `{concrete} == np.dtype({raw}).type`.".format(
775+
raw=type_repr(arg2_orig),
776+
abstract=type_repr(arg2),
777+
concrete=type_repr(dtype(arg2_orig).type)
778+
),
779+
FutureWarning, stacklevel=2
780+
)
781+
782+
return issubclass(arg1, arg2)
756783

757784

758785
# This dictionary allows look up based on any alias for an array data-type

numpy/core/tests/test_numerictypes.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import division, absolute_import, print_function
22

33
import sys
4+
import itertools
45

56
import numpy as np
67
from numpy.testing import (
@@ -377,5 +378,36 @@ def test_return(self):
377378
res = self.ary[['f0', 'f2']].tolist()
378379
assert_(res == [(1, 3), (5, 7)])
379380

381+
382+
class TestIsSubDType(object):
383+
# scalar types can be promoted into dtypes
384+
wrappers = [np.dtype, lambda x: x]
385+
386+
def test_both_abstract(self):
387+
assert_(np.issubdtype(np.floating, np.inexact))
388+
assert_(not np.issubdtype(np.inexact, np.floating))
389+
390+
def test_same(self):
391+
for cls in (np.float32, np.int32):
392+
for w1, w2 in itertools.product(self.wrappers, repeat=2):
393+
assert_(np.issubdtype(w1(cls), w2(cls)))
394+
395+
def test_subclass(self):
396+
# note we cannot promote floating to a dtype, as it would turn into a
397+
# concrete type
398+
for w in self.wrappers:
399+
assert_(np.issubdtype(w(np.float32), np.floating))
400+
assert_(np.issubdtype(w(np.float64), np.floating))
401+
402+
def test_subclass_backwards(self):
403+
for w in self.wrappers:
404+
assert_(not np.issubdtype(np.floating, w(np.float32)))
405+
assert_(not np.issubdtype(np.floating, w(np.float64)))
406+
407+
def test_sibling_class(self):
408+
for w1, w2 in itertools.product(self.wrappers, repeat=2):
409+
assert_(not np.issubdtype(w1(np.float32), w2(np.float64)))
410+
assert_(not np.issubdtype(w1(np.float64), w2(np.float32)))
411+
380412
if __name__ == "__main__":
381413
run_module_suite()

numpy/doc/basics.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,10 @@
114114
>>> d
115115
dtype('int32')
116116
117-
>>> np.issubdtype(d, int)
117+
>>> np.issubdtype(d, np.integer)
118118
True
119119
120-
>>> np.issubdtype(d, float)
120+
>>> np.issubdtype(d, np.floating)
121121
False
122122
123123

numpy/lib/_iotools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ class StringConverter(object):
527527
_mapper.append((nx.int64, int, -1))
528528

529529
_mapper.extend([(nx.floating, float, nx.nan),
530-
(complex, _bytes_to_complex, nx.nan + 0j),
530+
(nx.complexfloating, _bytes_to_complex, nx.nan + 0j),
531531
(nx.longdouble, nx.longdouble, nx.nan),
532532
(nx.string_, bytes, b'???')])
533533

numpy/lib/tests/test_function_base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,16 +1711,16 @@ def test_type(self):
17111711
# Check the type of the returned histogram
17121712
a = np.arange(10) + .5
17131713
h, b = histogram(a)
1714-
assert_(np.issubdtype(h.dtype, int))
1714+
assert_(np.issubdtype(h.dtype, np.integer))
17151715

17161716
h, b = histogram(a, normed=True)
1717-
assert_(np.issubdtype(h.dtype, float))
1717+
assert_(np.issubdtype(h.dtype, np.floating))
17181718

17191719
h, b = histogram(a, weights=np.ones(10, int))
1720-
assert_(np.issubdtype(h.dtype, int))
1720+
assert_(np.issubdtype(h.dtype, np.integer))
17211721

17221722
h, b = histogram(a, weights=np.ones(10, float))
1723-
assert_(np.issubdtype(h.dtype, float))
1723+
assert_(np.issubdtype(h.dtype, np.floating))
17241724

17251725
def test_f32_rounding(self):
17261726
# gh-4799, check that the rounding of the edges works with float32

numpy/lib/tests/test_index_tricks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ def test_shape_and_dtype(self):
243243
for k, (a, sz) in enumerate(zip(arrays, sizes)):
244244
assert_equal(a.shape[k], sz)
245245
assert_(all(sh == 1 for j, sh in enumerate(a.shape) if j != k))
246-
assert_(np.issubdtype(a.dtype, int))
246+
assert_(np.issubdtype(a.dtype, np.integer))
247247

248248
def test_bool(self):
249249
bool_a = [True, False, True, True]

numpy/lib/tests/test_type_check.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ class TestArrayConversion(object):
420420
def test_asfarray(self):
421421
a = asfarray(np.array([1, 2, 3]))
422422
assert_equal(a.__class__, np.ndarray)
423-
assert_(np.issubdtype(a.dtype, float))
423+
assert_(np.issubdtype(a.dtype, np.floating))
424424

425425
if __name__ == "__main__":
426426
run_module_suite()

numpy/matrixlib/defmatrix.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def matrix_power(M, n):
137137
M = asanyarray(M)
138138
if M.ndim != 2 or M.shape[0] != M.shape[1]:
139139
raise ValueError("input must be a square array")
140-
if not issubdtype(type(n), int):
140+
if not issubdtype(type(n), N.integer):
141141
raise TypeError("exponent must be an integer")
142142

143143
from numpy.linalg import inv

numpy/matrixlib/tests/test_defmatrix.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,12 @@ def test_pow(self):
249249
assert_array_almost_equal(m4, np.dot(m2, m2))
250250
assert_array_almost_equal(np.dot(mi, m), np.eye(2))
251251

252+
def test_scalar_type_pow(self):
253+
m = matrix([[1, 2], [3, 4]])
254+
for scalar_t in [np.int8, np.uint8]:
255+
two = scalar_t(2)
256+
assert_array_almost_equal(m ** 2, m ** two)
257+
252258
def test_notimplemented(self):
253259
'''Check that 'not implemented' operations produce a failure.'''
254260
A = matrix([[1., 2.],

0 commit comments

Comments
 (0)
0