diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2017-05-05 12:24:03 +0100 |
---|---|---|
committer | Eric Wieser <wieser.eric@gmail.com> | 2017-05-05 12:35:13 +0100 |
commit | 3c4545fd1be20ad2dc8168ef49c9c4f3486d1435 (patch) | |
tree | d6fad2ba02dd8f165ce4f1feed9c6414a7b5b045 /numpy/core/_internal.py | |
parent | fed2e1a4304123f8a0cc4313cae9ffe3d6614b78 (diff) | |
download | numpy-3c4545fd1be20ad2dc8168ef49c9c4f3486d1435.tar.gz |
BUG: Fix non-determinism in order of fields created from pep3118 formats
Fixes gh-9053
Diffstat (limited to 'numpy/core/_internal.py')
-rw-r--r-- | numpy/core/_internal.py | 48 |
1 files changed, 28 insertions, 20 deletions
diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py index 23df0a7ad..d69b7be8d 100644 --- a/numpy/core/_internal.py +++ b/numpy/core/_internal.py @@ -459,10 +459,16 @@ def _dtype_from_pep3118(spec): stream = Stream(spec) - return __dtype_from_pep3118(stream, is_subdtype=False) + dtype, align = __dtype_from_pep3118(stream, is_subdtype=False) + return dtype def __dtype_from_pep3118(stream, is_subdtype): - fields = {} + field_spec = dict( + names=[], + formats=[], + offsets=[], + itemsize=0 + ) offset = 0 explicit_name = False this_explicit_name = False @@ -478,7 +484,7 @@ def __dtype_from_pep3118(stream, is_subdtype): def get_dummy_name(): while True: name = 'f%d' % dummy_name_index[0] - if name not in fields: + if name not in field_spec['names']: return name next_dummy_name() @@ -586,35 +592,37 @@ def __dtype_from_pep3118(stream, is_subdtype): name = get_dummy_name() if not is_padding or this_explicit_name: - if name in fields: + if name in field_spec['names']: raise RuntimeError("Duplicate field name '%s' in PEP3118 format" % name) - fields[name] = (value, offset) + field_spec['names'].append(name) + field_spec['formats'].append(value) + field_spec['offsets'].append(offset) + if not this_explicit_name: next_dummy_name() offset += value.itemsize offset += extra_offset - # Check if this was a simple 1-item type - if (len(fields) == 1 and not explicit_name and - fields['f0'][1] == 0 and not is_subdtype): - ret = fields['f0'][0] - else: - ret = dtype(fields) + field_spec['itemsize'] = offset - # Trailing padding must be explicitly added - padding = offset - ret.itemsize + # extra final padding for aligned types if stream.byteorder == '@': - padding += (-offset) % common_alignment - if is_padding and not this_explicit_name: - ret = _add_trailing_padding(ret, padding) + field_spec['itemsize'] += (-offset) % common_alignment + + # Check if this was a simple 1-item type, and unwrap it + if (len(field_spec['names']) == 1 + and field_spec['offsets'][0] == 0 + and field_spec['itemsize'] == field_spec['formats'][0].itemsize + and not explicit_name + and not is_subdtype): + ret = field_spec['formats'][0] + else: + ret = dtype(field_spec) # Finished - if is_subdtype: - return ret, common_alignment - else: - return ret + return ret, common_alignment def _add_trailing_padding(value, padding): """Inject the specified number of padding bytes at the end of a dtype""" |