summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2020-09-02 20:49:52 +0300
committerGitHub <noreply@github.com>2020-09-02 20:49:52 +0300
commite3c84a44b68966ab887a3623a0ff57169e508deb (patch)
tree916fc0dc60406da4c33f3cb04757d8bf283dde6e
parent5b37a4b5b640d0c1f9714f1200309a25a54b7999 (diff)
parentdf85fb95cfece479ec06357c01753c0c0b670317 (diff)
downloadnumpy-e3c84a44b68966ab887a3623a0ff57169e508deb.tar.gz
Merge pull request #17168 from seberg/test_metadata_promotion
TST: Add tests mapping out the rules for metadata in promotion
-rw-r--r--numpy/core/tests/test_numeric.py104
1 files changed, 104 insertions, 0 deletions
diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py
index 0b27c54dd..ae5ee4c88 100644
--- a/numpy/core/tests/test_numeric.py
+++ b/numpy/core/tests/test_numeric.py
@@ -922,6 +922,110 @@ class TestTypes:
assert_equal(np.promote_types('u8', 'S1'), np.dtype('S20'))
assert_equal(np.promote_types('u8', 'S30'), np.dtype('S30'))
+ @pytest.mark.parametrize("dtype",
+ list(np.typecodes["All"]) +
+ ["i,i", "S3", "S100", "U3", "U100", rational])
+ def test_promote_identical_types_metadata(self, dtype):
+ # The same type passed in twice to promote types always
+ # preserves metadata
+ metadata = {1: 1}
+ dtype = np.dtype(dtype, metadata=metadata)
+
+ res = np.promote_types(dtype, dtype)
+ assert res.metadata == dtype.metadata
+
+ # byte-swapping preserves and makes the dtype native:
+ dtype = dtype.newbyteorder()
+ if dtype.isnative:
+ # The type does not have byte swapping
+ return
+
+ res = np.promote_types(dtype, dtype)
+ if res.char in "?bhilqpBHILQPefdgFDGOmM":
+ # Metadata is lost for simple promotions (they create a new dtype)
+ assert res.metadata is None
+ else:
+ assert res.metadata == metadata
+ if dtype.kind != "V":
+ # the result is native (except for structured void)
+ assert res.isnative
+
+ @pytest.mark.slow
+ @pytest.mark.parametrize(["dtype1", "dtype2"],
+ itertools.product(
+ list(np.typecodes["All"]) +
+ ["i,i", "S3", "S100", "U3", "U100", rational],
+ repeat=2))
+ def test_promote_types_metadata(self, dtype1, dtype2):
+ """Metadata handling in promotion does not appear formalized
+ right now in NumPy. This test should thus be considered to
+ document behaviour, rather than test the correct definition of it.
+
+ This test is very ugly, it was useful for rewriting part of the
+ promotion, but probably should eventually be replaced/deleted
+ (i.e. when metadata handling in promotion is better defined).
+ """
+ metadata1 = {1: 1}
+ metadata2 = {2: 2}
+ dtype1 = np.dtype(dtype1, metadata=metadata1)
+ dtype2 = np.dtype(dtype2, metadata=metadata2)
+
+ try:
+ res = np.promote_types(dtype1, dtype2)
+ except TypeError:
+ # Promotion failed, this test only checks metadata
+ return
+
+ # The rules for when metadata is preserved and which dtypes metadta
+ # will be used are very confusing and depend on multiple paths.
+ # This long if statement attempts to reproduce this:
+ if dtype1.type is rational or dtype2.type is rational:
+ # User dtype promotion preserves byte-order here:
+ if np.can_cast(res, dtype1):
+ assert res.metadata == dtype1.metadata
+ else:
+ assert res.metadata == dtype2.metadata
+
+ elif res.char in "?bhilqpBHILQPefdgFDGOmM":
+ # All simple types lose metadata (due to using promotion table):
+ assert res.metadata is None
+ elif res.kind in "SU" and dtype1 == dtype2:
+ # Strings give precedence to the second dtype:
+ assert res is dtype2
+ elif res == dtype1:
+ # If one result is the result, it is usually returned unchanged:
+ assert res is dtype1
+ elif res == dtype2:
+ # If one result is the result, it is usually returned unchanged:
+ assert res is dtype2
+ elif dtype1.kind == "S" and dtype2.kind == "U":
+ # Promotion creates a new unicode dtype from scratch
+ assert res.metadata is None
+ elif dtype1.kind == "U" and dtype2.kind == "S":
+ # Promotion creates a new unicode dtype from scratch
+ assert res.metadata is None
+ elif res.kind in "SU" and dtype2.kind != res.kind:
+ # We build on top of dtype1:
+ assert res.metadata == dtype1.metadata
+ elif res.kind in "SU" and res.kind == dtype1.kind:
+ assert res.metadata == dtype1.metadata
+ elif res.kind in "SU" and res.kind == dtype2.kind:
+ assert res.metadata == dtype2.metadata
+ else:
+ assert res.metadata is None
+
+ # Try again for byteswapped version
+ dtype1 = dtype1.newbyteorder()
+ assert dtype1.metadata == metadata1
+ res_bs = np.promote_types(dtype1, dtype2)
+ if res_bs.names is not None:
+ # Structured promotion doesn't remove byteswap:
+ assert res_bs.newbyteorder() == res
+ else:
+ assert res_bs == res
+ assert res_bs.metadata == res.metadata
+
+
def test_can_cast(self):
assert_(np.can_cast(np.int32, np.int64))
assert_(np.can_cast(np.float64, complex))