Repository navigation
ENH: show dtype in array repr when endianness is non-native - #23295
Conversation
Try to fix problem where, for example, np.array([1], dtype='>u2') and np.array([1], dtype='<u2') both got represented as np.array([1], dtype=uint16)
| if dtype.names is not None: | ||
| return False | ||
|
|
||
| if dtype.byteorder != '=' and dtype.itemsize != 1: |
There was a problem hiding this comment.
Tests are failing, I suspect dtype.isnative should work better?
There was a problem hiding this comment.
That works much better! I tried running numpy/core/tests/test_arrayprint.py with the changes in my last commit and now all the tests in that file pass.
@seberg pointed out that dtype.isnative could be used in place of my previous composite check. Now all tests in core/tests/test_arrayprint.py pass (except the one xfail)
This reverts commit eeaa2a9. I wanted to fix the linter errors, but reformatting with shed didn't help, so I'll just do it manually.
| # deal with cases like dtype('<u2') that are identical to an | ||
| # established dtype (in this case uint16) | ||
| # except that they have a different endianness. | ||
| return repr(dtype.descr[0][1]) |
There was a problem hiding this comment.
Using descr feels like a mistake to me; descr is generally poorly behaved. I think there should be another way to get this information?
There was a problem hiding this comment.
_construction_repr(..., short=True) from https://github.com/numpy/numpy/blob/main/numpy/core/_dtype.py looks like it might do the right thing.
There was a problem hiding this comment.
I'll look into that. descr was a very ad-hoc choice made because it looked like the right kind of thing.
There was a problem hiding this comment.
Yeah, the default in this branch looks like using str() or repr() one of them should be right (plausibly both, because complicated dtypes are dealt with earlier).
There was a problem hiding this comment.
Given that all tests pass when using _construction_repr(dtype) in that conditional branch (the current status as of my most recent commit), should I leave as is, or do you think it's better to switch back?
There was a problem hiding this comment.
That seems still very roundabout (maybe it is actually more direct, but using more internals), and str is already being used for structured dtypes?
| array(['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'], | ||
| dtype='{}')""".format(styp))) | ||
|
|
||
| @pytest.mark.parametrize( |
There was a problem hiding this comment.
Won't this test fail on big-endian systems?
There was a problem hiding this comment.
Yep, good point.
I think what I'll do is
- rename
little_endtonativeandbig_endtonon_native
import sys
if sys.byteorder == 'big':
# flip native and non-native for big-endian systems
non_native = non_native.replace('>', '<')
native = native.replace('<', '>')1. per eric-wieser's suggestion, using
_construction_repr(dtype) instead of dtype.descr[0][1]
to determine how to show non-native dtypes
2. Fix tests so they don't crash on big-endian systems.
3. Remove '<U' and '>U' tests because those are coerced
into '<U1' and '>U1' respectively by np.array.
tylerjereddy
left a comment
There was a problem hiding this comment.
I tried testing on one of the compile farm Big Endian machines, but ran into some toolchain issues unrelated to the PR (ancient gcc version, etc.).
| ''' | ||
| big_dtype = np.dtype(big_end) | ||
| little_dtype = np.dtype(little_end) | ||
| import sys |
There was a problem hiding this comment.
This import is already done at the top of the module
| non_native = non_native.replace('>', '<') | ||
| native = native.replace('<', '>') | ||
| big_dtype = np.dtype(non_native) | ||
| little_dtype = np.dtype(native) |
There was a problem hiding this comment.
I suppose these variable names might get pretty confusing if the test is to be considered agnostic to endianness.
| '(e.g., int32, bool, float64) ' | ||
| 'should not show dtype, but it does')) | ||
| # only show difference if > 1 byte | ||
| assert_(big_end_repr != little_end_repr or big_dtype.itemsize <= 1, |
There was a problem hiding this comment.
Maybe prefer plain assert for new testing code for the pytest augmentations?
|
Is there more that I need to do to move this forward, or are you all just occupied with more pressing things right now? I see that some of the build tests are failing, but I'm really not familiar with the failure modes exhibited, and it's not clear what I would do to address those issues. |
|
close/reopen to restart circleci, which seems to have a temporary failure. |
seberg
left a comment
There was a problem hiding this comment.
Just retriggered CircleCI once more. We should make sure it is happy, since this might have modified output.
The tests could be simplified a bit, but it's OK if not.
| ('float16', '>e'), | ||
| ('float32', '>f'), | ||
| ('float64', '>d'), | ||
| # ('<U', '>U'), # these get cast to '<U1' and '>U1' |
There was a problem hiding this comment.
| # ('<U', '>U'), # these get cast to '<U1' and '>U1' |
I think it is clear enough that U without a number is weird (I would love to just reject it one day for np.dtype(), but not for many dtype= calls where it has a clearer meaning).
| non_native = non_native.replace('>', '<') | ||
| native = native.replace('<', '>') | ||
| non_native_dtype = np.dtype(non_native) | ||
| native_dtype = np.dtype(native) |
There was a problem hiding this comment.
You could probably simplify all of this by just starting with only the native above, and then use:
dtype.newbyteorder()
which swaps it by default.
| if non_native_dtype.itemsize > 1: | ||
| # if the type is >1 byte, the non-native endian version | ||
| # must show endianness. | ||
| assert non_native_repr != native_repr,\ |
There was a problem hiding this comment.
I much prefer avoiding \ if possible. In this case, I personally wouldn't care about the assert text anyway (pytest failures tend to print out the code anyway).
seberg
left a comment
There was a problem hiding this comment.
Just going to remove that "U1" logic as well, thanks for the follow up.
| assert ('dtype' in native_repr) ^ (native_dtype in _typelessdata),\ | ||
| ("an array's repr should show dtype if and only if the type " | ||
| 'of the array is NOT one of the standard types ' | ||
| '(e.g., int32, bool, float64).') |
There was a problem hiding this comment.
I would prefer this as an if native_dtype in [np.int_, np.float64, np.complex128, np.bool_] (or _tyeplessdata), but that is just me nitpicking (xor is much harder to reason about).
There was a problem hiding this comment.
If you'd prefer something like
msg = ("an array's repr should show dtype if and only if the type "
'of the array is NOT one of the standard types '
'(e.g., int32, bool, float64).')
if native_dtype in _typelessdata:
assert 'dtype' in native_repr, msg
else:
assert 'dtype' not in native_repr, msgI see nothing wrong with that. That doesn't seem much more clear to me, but maybe I'm the only one.
There was a problem hiding this comment.
To me it is a bit nicer, especially if the msg was a comment on the branch. But we already cleaned up the test a lot and this doesn't matter enough.
Thanks @molsonkiko it is a nice improvement/fix.
to avoid change in printed dtype caused by numpy/numpy#23295
to avoid change in printed dtype caused by numpy/numpy#23295
to avoid change in printed dtype caused by numpy/numpy#23295
See #19059.
Goal is to get