summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2018-09-09 15:26:16 -0700
committerEric Wieser <wieser.eric@gmail.com>2018-09-10 19:15:42 -0700
commitdfead64385e5ffb95b3053d5f955117fd81d4705 (patch)
tree8a79980c93cca87b9a74bb2c7c14d46f27a32033
parent00e50a3a38d1f86e027dd1d77fd5491ade7d248a (diff)
downloadnumpy-dfead64385e5ffb95b3053d5f955117fd81d4705.tar.gz
MAINT: Don't use `__name__` to inspect type information
It makes much more sense to get this information directly from the typeinfo / dtype, rather than roundtripping it though a string. The following code produces the same output (on 64-bit windows, python 3) before and after this change: ``` In [4]: { ...: name: np.core.numerictypes.bitname(info.type) ...: for name, info in sorted(np.core.numerictypes._concrete_typeinfo.items()) ...: } Out[4]: {'bool': ('bool', 8, 'b1'), 'byte': ('int', 8, 'i1'), 'cdouble': ('complex', 128, 'c16'), 'cfloat': ('complex', 64, 'c8'), 'clongdouble': ('complex', 128, 'c16'), 'datetime': ('datetime', 64, 'M8'), 'double': ('float', 64, 'f8'), 'float': ('float', 32, 'f4'), 'half': ('float', 16, 'f2'), 'int': ('int', 32, 'i4'), 'intp': ('int', 64, 'i8'), 'long': ('int', 32, 'i4'), 'longdouble': ('float', 64, 'f8'), 'longlong': ('int', 64, 'i8'), 'object': ('object', 0, 'O'), 'short': ('int', 16, 'i2'), 'string': ('bytes', 0, 'S'), 'timedelta': ('timedelta', 64, 'm8'), 'ubyte': ('uint', 8, 'u1'), 'uint': ('uint', 32, 'u4'), 'uintp': ('uint', 64, 'u8'), 'ulong': ('uint', 32, 'u4'), 'ulonglong': ('uint', 64, 'u8'), 'unicode': ('str', 0, 'U'), 'ushort': ('uint', 16, 'u2'), 'void': ('void', 0, 'V')} ``` Before this change, it was possible to call `bitname` on the abstract types, and get garbage results: ``` In [7]: { ...: name: np.core.numerictypes.bitname(t) ...: for name, t in sorted(np.core.numerictypes._abstract_types.items()) ...: } Out[7]: {'character': ('character', 0, 'c'), 'complexfloating': ('complexfloating', 0, 'c'), 'flexible': ('flexible', 0, 'f'), 'floating': ('floating', 0, 'f'), 'generic': ('generic', 0, 'g'), 'inexact': ('inexact', 0, 'i'), 'integer': ('integer', 0, 'i'), 'number': ('number', 0, 'n'), 'signedinteger': ('signedinteger', 0, 's'), 'unsignedinteger': ('unsignedinteger', 0, 'u')} ``` Now it raises a ValueError for all abstract types
-rw-r--r--numpy/core/numerictypes.py113
-rw-r--r--numpy/core/tests/test_numerictypes.py5
2 files changed, 51 insertions, 67 deletions
diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py
index 817af4c7b..259aef2c6 100644
--- a/numpy/core/numerictypes.py
+++ b/numpy/core/numerictypes.py
@@ -238,71 +238,53 @@ for k, v in typeinfo.items():
_concrete_typeinfo[k] = v
-def _evalname(name):
- k = 0
- for ch in name:
- if ch in '0123456789':
- break
- k += 1
- try:
- bits = int(name[k:])
- except ValueError:
- bits = 0
- base = name[:k]
- return base, bits
+_kind_to_stem = {
+ 'u': 'uint',
+ 'i': 'int',
+ 'c': 'complex',
+ 'f': 'float',
+ 'b': 'bool',
+ 'V': 'void',
+ 'O': 'object',
+ 'M': 'datetime',
+ 'm': 'timedelta'
+}
+if sys.version_info[0] >= 3:
+ _kind_to_stem.update({
+ 'S': 'bytes',
+ 'U': 'str'
+ })
+else:
+ _kind_to_stem.update({
+ 'S': 'string',
+ 'U': 'unicode'
+ })
-def bitname(obj):
- """Return a bit-width name for a given type object"""
- name = obj.__name__
- base = ''
- char = ''
+
+def _bits_of(obj):
try:
- if name[-1] == '_':
- newname = name[:-1]
- else:
- newname = name
- info = _concrete_typeinfo[english_lower(newname)]
- assert(info.type == obj) # sanity check
- bits = info.bits
-
- except KeyError: # bit-width name
- base, bits = _evalname(name)
- char = base[0]
-
- if name == 'bool_':
- char = 'b'
- base = 'bool'
- elif name == 'void':
- char = 'V'
- base = 'void'
- elif name == 'object_':
- char = 'O'
- base = 'object'
- bits = 0
- elif name == 'datetime64':
- char = 'M'
- elif name == 'timedelta64':
- char = 'm'
+ info = next(v for v in _concrete_typeinfo.values() if v.type is obj)
+ except StopIteration:
+ if obj in _abstract_types.values():
+ raise ValueError("Cannot count the bits of an abstract type")
- if sys.version_info[0] >= 3:
- if name == 'bytes_':
- char = 'S'
- base = 'bytes'
- elif name == 'str_':
- char = 'U'
- base = 'str'
+ # some third-party type - make a best-guess
+ return dtype(obj).itemsize * 8
else:
- if name == 'string_':
- char = 'S'
- base = 'string'
- elif name == 'unicode_':
- char = 'U'
- base = 'unicode'
+ return info.bits
- bytes = bits // 8
- if char != '' and bytes != 0:
- char = "%s%d" % (char, bytes)
+def bitname(obj):
+ """Return a bit-width name for a given type object"""
+ bits = _bits_of(obj)
+ char = dtype(obj).kind
+ base = _kind_to_stem[char]
+
+ if base == 'object':
+ bits = 0
+
+ if bits != 0:
+ char = "%s%d" % (char, bits // 8)
return base, bits, char
@@ -336,7 +318,6 @@ def _add_aliases():
# insert bit-width version for this class (if relevant)
base, bit, char = bitname(info.type)
- assert base != ''
myname = "%s%d" % (base, bit)
# ensure that (c)longdouble does not overwrite the aliases assigned to
@@ -363,7 +344,6 @@ def _add_aliases():
sctypeNA[info.type] = na_name
sctypeNA[info.char] = na_name
- assert char != ''
sctypeDict[char] = info.type
sctypeNA[char] = na_name
_add_aliases()
@@ -543,13 +523,12 @@ def maximum_sctype(t):
if g is None:
return t
t = g
- name = t.__name__
- base, bits = _evalname(name)
- if bits == 0:
- return t
- else:
+ bits = _bits_of(t)
+ base = _kind_to_stem[dtype(t).kind]
+ if base in sctypes:
return sctypes[base][-1]
-
+ else:
+ return t
def issctype(rep):
"""
diff --git a/numpy/core/tests/test_numerictypes.py b/numpy/core/tests/test_numerictypes.py
index 22abe00fd..b387880ef 100644
--- a/numpy/core/tests/test_numerictypes.py
+++ b/numpy/core/tests/test_numerictypes.py
@@ -415,6 +415,11 @@ def TestSctypeDict(object):
assert_(np.sctypeDict['c16'] is not np.clongdouble)
+class TestBitName(object):
+ def test_abstract(self):
+ assert_raises(ValueError, np.core.numerictypes.bitname, np.floating)
+
+
class TestMaximumSctype(object):
# note that parametrizing with sctype['int'] and similar would skip types