diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2018-09-09 15:26:16 -0700 |
---|---|---|
committer | Eric Wieser <wieser.eric@gmail.com> | 2018-09-10 19:15:42 -0700 |
commit | dfead64385e5ffb95b3053d5f955117fd81d4705 (patch) | |
tree | 8a79980c93cca87b9a74bb2c7c14d46f27a32033 | |
parent | 00e50a3a38d1f86e027dd1d77fd5491ade7d248a (diff) | |
download | numpy-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.py | 113 | ||||
-rw-r--r-- | numpy/core/tests/test_numerictypes.py | 5 |
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 |