Skip to content

Commit 1956ada

Browse files
committed
BUG: test, fix loading structured dtypes with padding
1 parent cd39348 commit 1956ada

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

numpy/lib/format.py

+29-1
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,33 @@ def dtype_to_descr(dtype):
259259
else:
260260
return dtype.str
261261

262+
def descr_to_dtype(descr):
263+
if isinstance(descr, str):
264+
# descr was produced by dtype.str, so this always works
265+
return numpy.dtype(descr)
266+
267+
fields = []
268+
offset = 0
269+
for field in descr:
270+
if len(field) == 2:
271+
name, descr_str = field
272+
dt = descr_to_dtype(descr_str)
273+
else:
274+
name, descr_str, shape = field
275+
dt = numpy.dtype((descr_to_dtype(descr_str), shape))
276+
277+
# ignore padding bytes, which will be void bytes with '' as name
278+
# (once blank fieldnames are deprecated, only "if name == ''" needed)
279+
is_pad = (name == '' and dt.type is numpy.void and dt.names is None)
280+
if not is_pad:
281+
fields.append((name, dt, offset))
282+
283+
offset += dt.itemsize
284+
285+
names, formats, offsets = zip(*fields)
286+
return numpy.dtype({'names': names, 'formats': formats,
287+
'offsets': offsets, 'itemsize': offset})
288+
262289
def header_data_from_array_1_0(array):
263290
""" Get the dictionary of header metadata from a numpy.ndarray.
264291
@@ -523,7 +550,8 @@ def _read_array_header(fp, version):
523550
msg = "fortran_order is not a valid bool: %r"
524551
raise ValueError(msg % (d['fortran_order'],))
525552
try:
526-
dtype = numpy.dtype(d['descr'])
553+
descr = descr_to_dtype(d['descr'])
554+
dtype = numpy.dtype(descr)
527555
except TypeError as e:
528556
msg = "descr is not a valid dtype descriptor: %r"
529557
raise ValueError(msg % (d['descr'],))

numpy/lib/tests/test_format.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,26 @@ def test_compressed_roundtrip():
524524
assert_array_equal(arr, arr1)
525525

526526

527+
# aligned
528+
dt1 = np.dtype('i1, i4, i1', align=True)
529+
# non-aligned, explicit offsets
530+
dt2 = np.dtype({'names': ['a', 'b'], 'formats': ['i4', 'i4'],
531+
'offsets': [1, 6]})
532+
# nested struct-in-struct
533+
dt3 = np.dtype({'names': ['c', 'd'], 'formats': ['i4', dt2]})
534+
# field with '' name
535+
dt4 = np.dtype({'names': ['a', '', 'b'], 'formats': ['i4']*3})
536+
@pytest.mark.parametrize("dt", [dt1, dt2, dt3, dt4])
537+
def test_load_padded_dtype(dt):
538+
arr = np.zeros(3, dt)
539+
for i in range(3):
540+
arr[i] = i + 5
541+
npz_file = os.path.join(tempdir, 'aligned.npz')
542+
np.savez(npz_file, arr=arr)
543+
arr1 = np.load(npz_file)['arr']
544+
assert_array_equal(arr, arr1)
545+
546+
527547
def test_python2_python3_interoperability():
528548
if sys.version_info[0] >= 3:
529549
fname = 'win64python2.npy'
@@ -533,7 +553,6 @@ def test_python2_python3_interoperability():
533553
data = np.load(path)
534554
assert_array_equal(data, np.ones(2))
535555

536-
537556
def test_pickle_python2_python3():
538557
# Test that loading object arrays saved on Python 2 works both on
539558
# Python 2 and Python 3 and vice versa

0 commit comments

Comments
 (0)