summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorAllan Haldane <ealloc@gmail.com>2018-02-15 16:27:51 +0100
committerGitHub <noreply@github.com>2018-02-15 16:27:51 +0100
commit69fa37f422c845655492220440735e7c800306b8 (patch)
tree48b423d4b58e6e0dffe8af8c8a6d50bdaf2019f3 /numpy
parent921b42a194190345eafba0286b8f528e57526e81 (diff)
parentfed44d7c309d0c2fd7c193efa39ba59652a43c44 (diff)
downloadnumpy-69fa37f422c845655492220440735e7c800306b8.tar.gz
Merge pull request #10381 from eric-wieser/fix-segfault
BUG/ENH: Improve output for structured non-void types
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/arrayprint.py49
-rw-r--r--numpy/core/src/multiarray/descriptor.c4
-rw-r--r--numpy/core/tests/test_multiarray.py10
3 files changed, 48 insertions, 15 deletions
diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py
index 0fdd6fc7f..84943cafc 100644
--- a/numpy/core/arrayprint.py
+++ b/numpy/core/arrayprint.py
@@ -402,9 +402,6 @@ def _get_format_function(data, **options):
find the right formatting function for the dtype_
"""
dtype_ = data.dtype
- if dtype_.fields is not None:
- return StructureFormat.from_data(data, **options)
-
dtypeobj = dtype_.type
formatdict = _get_formatdict(data, **options)
if issubclass(dtypeobj, _nt.bool_):
@@ -431,7 +428,10 @@ def _get_format_function(data, **options):
elif issubclass(dtypeobj, _nt.object_):
return formatdict['object']()
elif issubclass(dtypeobj, _nt.void):
- return formatdict['void']()
+ if dtype_.names is not None:
+ return StructuredVoidFormat.from_data(data, **options)
+ else:
+ return formatdict['void']()
else:
return formatdict['numpystr']()
@@ -1233,14 +1233,21 @@ class SubArrayFormat(object):
return "[" + ", ".join(self.__call__(a) for a in arr) + "]"
-class StructureFormat(object):
+class StructuredVoidFormat(object):
+ """
+ Formatter for structured np.void objects.
+
+ This does not work on structured alias types like np.dtype(('i4', 'i2,i2')),
+ as alias scalars lose their field information, and the implementation
+ relies upon np.void.__getitem__.
+ """
def __init__(self, format_functions):
self.format_functions = format_functions
@classmethod
def from_data(cls, data, **options):
"""
- This is a second way to initialize StructureFormat, using the raw data
+ This is a second way to initialize StructuredVoidFormat, using the raw data
as input. Added to avoid changing the signature of __init__.
"""
format_functions = []
@@ -1261,13 +1268,24 @@ class StructureFormat(object):
else:
return "({})".format(", ".join(str_fields))
+
+# for backwards compatibility
+class StructureFormat(StructuredVoidFormat):
+ def __init__(self, *args, **kwargs):
+ # NumPy 1.14, 2018-02-14
+ warnings.warn(
+ "StructureFormat has been replaced by StructuredVoidFormat",
+ DeprecationWarning, stacklevel=2)
+ super(StructureFormat, self).__init__(*args, **kwargs)
+
+
def _void_scalar_repr(x):
"""
Implements the repr for structured-void scalars. It is called from the
scalartypes.c.src code, and is placed here because it uses the elementwise
formatters defined above.
"""
- return StructureFormat.from_data(array(x), **_format_options)(x)
+ return StructuredVoidFormat.from_data(array(x), **_format_options)(x)
_typelessdata = [int_, float_, complex_, bool_]
@@ -1305,6 +1323,11 @@ def dtype_is_implied(dtype):
dtype = np.dtype(dtype)
if _format_options['legacy'] == '1.13' and dtype.type == bool_:
return False
+
+ # not just void types can be structured, and names are not part of the repr
+ if dtype.names is not None:
+ return False
+
return dtype.type in _typelessdata
@@ -1317,12 +1340,12 @@ def dtype_short_repr(dtype):
>>> from numpy import *
>>> assert eval(dtype_short_repr(dt)) == dt
"""
- # handle these separately so they don't give garbage like str256
- if issubclass(dtype.type, flexible):
- if dtype.names is not None:
- return "%s" % str(dtype)
- else:
- return "'%s'" % str(dtype)
+ if dtype.names is not None:
+ # structured dtypes give a list or tuple repr
+ return str(dtype)
+ elif issubclass(dtype.type, flexible):
+ # handle these separately so they don't give garbage like str256
+ return "'%s'" % str(dtype)
typename = dtype.name
# quote typenames which can't be represented as python variable names
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c
index 80161b71c..897155238 100644
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -3340,8 +3340,8 @@ arraydescr_struct_str(PyArray_Descr *dtype, int includealignflag)
sub = arraydescr_struct_dict_str(dtype, includealignflag);
}
- /* If the data type has a non-void (subclassed) type, show it */
- if (dtype->type_num == NPY_VOID && dtype->typeobj != &PyVoidArrType_Type) {
+ /* If the data type isn't the default, void, show it */
+ if (dtype->typeobj != &PyVoidArrType_Type) {
/*
* Note: We cannot get the type name from dtype->typeobj->tp_name
* because its value depends on whether the type is dynamically or
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index e1fcf947e..3b92a0610 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -478,6 +478,16 @@ class TestDtypedescr(object):
assert_(np.dtype('<i4') != np.dtype('>i4'))
assert_(np.dtype([('a', '<i4')]) != np.dtype([('a', '>i4')]))
+ def test_structured_non_void(self):
+ fields = [('a', '<i2'), ('b', '<i2')]
+ dt_int = np.dtype(('i4', fields))
+ assert_equal(str(dt_int), "(numpy.int32, [('a', '<i2'), ('b', '<i2')])")
+
+ # gh-9821
+ arr_int = np.zeros(4, dt_int)
+ assert_equal(repr(arr_int),
+ "array([0, 0, 0, 0], dtype=(numpy.int32, [('a', '<i2'), ('b', '<i2')]))")
+
class TestZeroRank(object):
def setup(self):