Exemple #1
0
    def test_simple_string_casts_roundtrip(self, other_dt, string_char):
        """
        Tests casts from and to string by checking the roundtripping property.

        The test also covers some string to string casts (but not all).

        If this test creates issues, it should possibly just be simplified
        or even removed (checking whether unaligned/non-contiguous casts give
        the same results is useful, though).
        """
        string_DT = type(np.dtype(string_char))

        cast = get_castingimpl(type(other_dt), string_DT)
        cast_back = get_castingimpl(string_DT, type(other_dt))
        _, (res_other_dt, string_dt) = cast._resolve_descriptors(
            (other_dt, None))

        if res_other_dt is not other_dt:
            # do not support non-native byteorder, skip test in that case
            assert other_dt.byteorder != res_other_dt.byteorder
            return

        orig_arr, values = self.get_data(other_dt, None)
        str_arr = np.zeros(len(orig_arr), dtype=string_dt)
        string_dt_short = self.string_with_modified_length(string_dt, -1)
        str_arr_short = np.zeros(len(orig_arr), dtype=string_dt_short)
        string_dt_long = self.string_with_modified_length(string_dt, 1)
        str_arr_long = np.zeros(len(orig_arr), dtype=string_dt_long)

        assert not cast._supports_unaligned  # if support is added, should test
        assert not cast_back._supports_unaligned

        for contig in [True, False]:
            other_arr, str_arr = self.get_data_variation(
                orig_arr, str_arr, True, contig)
            _, str_arr_short = self.get_data_variation(orig_arr,
                                                       str_arr_short.copy(),
                                                       True, contig)
            _, str_arr_long = self.get_data_variation(orig_arr, str_arr_long,
                                                      True, contig)

            cast._simple_strided_call((other_arr, str_arr))

            cast._simple_strided_call((other_arr, str_arr_short))
            assert_array_equal(str_arr.astype(string_dt_short), str_arr_short)

            cast._simple_strided_call((other_arr, str_arr_long))
            assert_array_equal(str_arr, str_arr_long)

            if other_dt.kind == "b":
                # Booleans do not roundtrip
                continue

            other_arr[...] = 0
            cast_back._simple_strided_call((str_arr, other_arr))
            assert_array_equal(orig_arr, other_arr)

            other_arr[...] = 0
            cast_back._simple_strided_call((str_arr_long, other_arr))
            assert_array_equal(orig_arr, other_arr)
Exemple #2
0
    def test_simple_cancast(self, from_Dt):
        for to_Dt in simple_dtypes:
            cast = get_castingimpl(from_Dt, to_Dt)

            for from_dt in [from_Dt(), from_Dt().newbyteorder()]:
                default = cast._resolve_descriptors((from_dt, None))[1][1]
                assert default == to_Dt()
                del default

                for to_dt in [to_Dt(), to_Dt().newbyteorder()]:
                    casting, (from_res, to_res) = cast._resolve_descriptors(
                        (from_dt, to_dt))
                    assert(type(from_res) == from_Dt)
                    assert(type(to_res) == to_Dt)
                    if casting & Casting.cast_is_view:
                        # If a view is acceptable, this is "no" casting
                        # and byte order must be matching.
                        assert casting == Casting.no | Casting.cast_is_view
                        # The above table lists this as "equivalent"
                        assert Casting.equiv == CAST_TABLE[from_Dt][to_Dt]
                        # Note that to_res may not be the same as from_dt
                        assert from_res.isnative == to_res.isnative
                    else:
                        if from_Dt == to_Dt:
                            # Note that to_res may not be the same as from_dt
                            assert from_res.isnative != to_res.isnative
                        assert casting == CAST_TABLE[from_Dt][to_Dt]

                    if from_Dt is to_Dt:
                        assert(from_dt is from_res)
                        assert(to_dt is to_res)
class TestSimpleStridedCall:
    # Test mainly error paths of the resolve_descriptors function,
    # note that the `casting_unittests` tests exercise this non-error paths.

    # Casting implementations are the main/only current user:
    method = get_castingimpl(type(np.dtype("d")), type(np.dtype("f")))

    @pytest.mark.parametrize(
        ["args", "error"],
        [
            ((True, ), TypeError),  # Not a tuple
            (((None, ), ), TypeError),  # Too few elements
            ((None, None), TypeError),  # Inputs are not arrays.
            (((None, None, None), ), TypeError),  # Too many
            (((np.arange(3), np.arange(3)), ), TypeError),  # Incorrect dtypes
            (((np.ones(3, dtype=">d"), np.ones(3, dtype="<f")), ),
             TypeError),  # Does not support byte-swapping
            (((np.ones((2, 2), dtype="d"), np.ones(
                (2, 2), dtype="f")), ), ValueError),  # not 1-D
            (((np.ones(3, dtype="d"), np.ones(
                4, dtype="f")), ), ValueError),  # different length
            (((np.frombuffer(b"\0x00" * 3 * 2, dtype="d"),
               np.frombuffer(b"\0x00" * 3, dtype="f")), ),
             ValueError),  # output not writeable
        ])
    def test_invalid_arguments(self, args, error):
        # This is private API, which may be modified freely
        with pytest.raises(error):
            self.method._simple_strided_call(*args)
Exemple #4
0
    def test_simple_direct_casts(self, from_dt):
        """
        This test checks numeric direct casts for dtypes supported also by the
        struct module (plus complex).  It tries to be test a wide range of
        inputs, but skips over possibly undefined behaviour (e.g. int rollover).
        Longdouble and CLongdouble are tested, but only using double precision.

        If this test creates issues, it should possibly just be simplified
        or even removed (checking whether unaligned/non-contiguous casts give
        the same results is useful, though).
        """
        for to_dt in simple_dtype_instances():
            to_dt = to_dt.values[0]
            cast = get_castingimpl(type(from_dt), type(to_dt))

            casting, (from_res, to_res) = cast._resolve_descriptors(
                (from_dt, to_dt))

            if from_res is not from_dt or to_res is not to_dt:
                # Do not test this case, it is handled in multiple steps,
                # each of which should is tested individually.
                return

            safe = (casting & ~Casting.cast_is_view) <= Casting.safe
            del from_res, to_res, casting

            arr1, arr2, values = self.get_data(from_dt, to_dt)

            cast._simple_strided_call((arr1, arr2))

            # Check via python list
            assert arr2.tolist() == values

            # Check that the same results are achieved for strided loops
            arr1_o, arr2_o = self.get_data_variation(arr1, arr2, True, False)
            cast._simple_strided_call((arr1_o, arr2_o))

            assert_array_equal(arr2_o, arr2)
            assert arr2_o.tobytes() == arr2.tobytes()

            # Check if alignment makes a difference, but only if supported
            # and only if the alignment can be wrong
            if ((from_dt.alignment == 1 and to_dt.alignment == 1) or
                    not cast._supports_unaligned):
                return

            arr1_o, arr2_o = self.get_data_variation(arr1, arr2, False, True)
            cast._simple_strided_call((arr1_o, arr2_o))

            assert_array_equal(arr2_o, arr2)
            assert arr2_o.tobytes() == arr2.tobytes()

            arr1_o, arr2_o = self.get_data_variation(arr1, arr2, False, False)
            cast._simple_strided_call((arr1_o, arr2_o))

            assert_array_equal(arr2_o, arr2)
            assert arr2_o.tobytes() == arr2.tobytes()

            del arr1_o, arr2_o, cast
    def test_simple_to_object_resolution(self, dtype):
        # Simple test to exercise the cast when no instance is specified
        object_dtype = type(np.dtype(object))
        cast = get_castingimpl(type(dtype), object_dtype)

        safety, (_, res_dt) = cast._resolve_descriptors((dtype, None))
        assert safety == Casting.safe
        assert res_dt is np.dtype("O")
    def test_string_cancast(self, other_DT, string_char):
        fact = 1 if string_char == "S" else 4

        string_DT = type(np.dtype(string_char))
        cast = get_castingimpl(other_DT, string_DT)

        other_dt = other_DT()
        expected_length = get_expected_stringlength(other_dt)
        string_dt = np.dtype(f"{string_char}{expected_length}")

        safety, (res_other_dt, res_dt), view_off = cast._resolve_descriptors(
            (other_dt, None))
        assert res_dt.itemsize == expected_length * fact
        assert safety == Casting.safe  # we consider to string casts "safe"
        assert view_off is None
        assert isinstance(res_dt, string_DT)

        # These casts currently implement changing the string length, so
        # check the cast-safety for too long/fixed string lengths:
        for change_length in [-1, 0, 1]:
            if change_length >= 0:
                expected_safety = Casting.safe
            else:
                expected_safety = Casting.same_kind

            to_dt = self.string_with_modified_length(string_dt, change_length)
            safety, (_, res_dt), view_off = cast._resolve_descriptors(
                (other_dt, to_dt))
            assert res_dt is to_dt
            assert safety == expected_safety
            assert view_off is None

        # The opposite direction is always considered unsafe:
        cast = get_castingimpl(string_DT, other_DT)

        safety, _, view_off = cast._resolve_descriptors((string_dt, other_dt))
        assert safety == Casting.unsafe
        assert view_off is None

        cast = get_castingimpl(string_DT, other_DT)
        safety, (_, res_dt), view_off = cast._resolve_descriptors(
            (string_dt, None))
        assert safety == Casting.unsafe
        assert view_off is None
        assert other_dt is res_dt  # returns the singleton for simple dtypes
Exemple #7
0
 def test_object_to_parametric_internal_error(self):
     # We reject casting from object to a parametric type, without
     # figuring out the correct instance first.
     object_dtype = type(np.dtype(object))
     other_dtype = type(np.dtype(str))
     cast = get_castingimpl(object_dtype, other_dtype)
     with pytest.raises(TypeError,
                 match="casting from object to the parametric DType"):
         cast._resolve_descriptors((np.dtype("O"), None))
 def test_structured_view_offsets_paramteric(self, from_dt, to_dt,
                                             expected_off):
     # TODO: While this test is fairly thorough, right now, it does not
     # really test some paths that may have nonzero offsets (they don't
     # really exists).
     from_dt = np.dtype(from_dt)
     to_dt = np.dtype(to_dt)
     cast = get_castingimpl(type(from_dt), type(to_dt))
     _, _, view_off = cast._resolve_descriptors((from_dt, to_dt))
     assert view_off == expected_off
    def test_object_and_simple_resolution(self, dtype):
        # Simple test to exercise the cast when no instance is specified
        object_dtype = type(np.dtype(object))
        cast = get_castingimpl(object_dtype, type(dtype))

        safety, (_, res_dt) = cast._resolve_descriptors((np.dtype("O"), dtype))
        assert safety == Casting.unsafe
        assert res_dt is dtype

        safety, (_, res_dt) = cast._resolve_descriptors((np.dtype("O"), None))
        assert safety == Casting.unsafe
        assert res_dt == dtype.newbyteorder("=")
    def test_numeric_to_times(self, from_Dt):
        # We currently only implement contiguous loops, so only need to
        # test those.
        from_dt = from_Dt()

        time_dtypes = [
            np.dtype("M8"),
            np.dtype("M8[ms]"),
            np.dtype("M8[4D]"),
            np.dtype("m8"),
            np.dtype("m8[ms]"),
            np.dtype("m8[4D]")
        ]
        for time_dt in time_dtypes:
            cast = get_castingimpl(type(from_dt), type(time_dt))

            casting, (from_res, to_res), view_off = cast._resolve_descriptors(
                (from_dt, time_dt))

            assert from_res is from_dt
            assert to_res is time_dt
            del from_res, to_res

            assert casting & CAST_TABLE[from_Dt][type(time_dt)]
            assert view_off is None

            int64_dt = np.dtype(np.int64)
            arr1, arr2, values = self.get_data(from_dt, int64_dt)
            arr2 = arr2.view(time_dt)
            arr2[...] = np.datetime64("NaT")

            if time_dt == np.dtype("M8"):
                # This is a bit of a strange path, and could probably be removed
                arr1[-1] = 0  # ensure at least one value is not NaT

                # The cast currently succeeds, but the values are invalid:
                cast._simple_strided_call((arr1, arr2))
                with pytest.raises(ValueError):
                    str(arr2[-1])  # e.g. conversion to string fails
                return

            cast._simple_strided_call((arr1, arr2))

            assert [int(v) for v in arr2.tolist()] == values

            # Check that the same results are achieved for strided loops
            arr1_o, arr2_o = self.get_data_variation(arr1, arr2, True, False)
            cast._simple_strided_call((arr1_o, arr2_o))

            assert_array_equal(arr2_o, arr2)
            assert arr2_o.tobytes() == arr2.tobytes()
 def test_structured_field_offsets(self, to_dt, expected_off):
     # This checks the cast-safety and view offset for swapped and "shifted"
     # fields which are viewable
     from_dt = np.dtype({
         "names": ["a", "b"],
         "formats": ["i4", "f4"],
         "offsets": [0, 4]
     })
     cast = get_castingimpl(type(from_dt), type(to_dt))
     safety, _, view_off = cast._resolve_descriptors((from_dt, to_dt))
     assert safety == Casting.equiv
     # Shifting the original data pointer by -2 will align both by
     # effectively adding 2 bytes of spacing before `from_dt`.
     assert view_off == expected_off
    def test_string_to_string_cancast(self, other_dt, string_char):
        other_dt = np.dtype(other_dt)

        fact = 1 if string_char == "S" else 4
        div = 1 if other_dt.char == "S" else 4

        string_DT = type(np.dtype(string_char))
        cast = get_castingimpl(type(other_dt), string_DT)

        expected_length = other_dt.itemsize // div
        string_dt = np.dtype(f"{string_char}{expected_length}")

        safety, (res_other_dt, res_dt), view_off = cast._resolve_descriptors(
            (other_dt, None))
        assert res_dt.itemsize == expected_length * fact
        assert isinstance(res_dt, string_DT)

        expected_view_off = None
        if other_dt.char == string_char:
            if other_dt.isnative:
                expected_safety = Casting.no
                expected_view_off = 0
            else:
                expected_safety = Casting.equiv
        elif string_char == "U":
            expected_safety = Casting.safe
        else:
            expected_safety = Casting.unsafe

        assert view_off == expected_view_off
        assert expected_safety == safety

        for change_length in [-1, 0, 1]:
            to_dt = self.string_with_modified_length(string_dt, change_length)
            safety, (_, res_dt), view_off = cast._resolve_descriptors(
                (other_dt, to_dt))

            assert res_dt is to_dt
            if change_length <= 0:
                assert view_off == expected_view_off
            else:
                assert view_off is None
            if expected_safety == Casting.unsafe:
                assert safety == expected_safety
            elif change_length < 0:
                assert safety == Casting.same_kind
            elif change_length == 0:
                assert safety == expected_safety
            elif change_length > 0:
                assert safety == Casting.safe
    def test_time_to_time(self, from_dt, to_dt, expected_casting,
                          expected_view_off, nom, denom):
        from_dt = np.dtype(from_dt)
        if to_dt is not None:
            to_dt = np.dtype(to_dt)

        # Test a few values for casting (results generated with NumPy 1.19)
        values = np.array([-2**63, 1, 2**63 - 1, 10000, -10000, 2**32])
        values = values.astype(
            np.dtype("int64").newbyteorder(from_dt.byteorder))
        assert values.dtype.byteorder == from_dt.byteorder
        assert np.isnat(values.view(from_dt)[0])

        DType = type(from_dt)
        cast = get_castingimpl(DType, DType)
        casting, (from_res, to_res), view_off = cast._resolve_descriptors(
            (from_dt, to_dt))
        assert from_res is from_dt
        assert to_res is to_dt or to_dt is None
        assert casting == expected_casting
        assert view_off == expected_view_off

        if nom is not None:
            expected_out = (values * nom // denom).view(to_res)
            expected_out[0] = "NaT"
        else:
            expected_out = np.empty_like(values)
            expected_out[...] = denom
            expected_out = expected_out.view(to_dt)

        orig_arr = values.view(from_dt)
        orig_out = np.empty_like(expected_out)

        if casting == Casting.unsafe and (to_dt == "m8" or to_dt == "M8"):
            # Casting from non-generic to generic units is an error and should
            # probably be reported as an invalid cast earlier.
            with pytest.raises(ValueError):
                cast._simple_strided_call((orig_arr, orig_out))
            return

        for aligned in [True, True]:
            for contig in [True, True]:
                arr, out = self.get_data_variation(orig_arr, orig_out, aligned,
                                                   contig)
                out[...] = 0
                cast._simple_strided_call((arr, out))
                assert_array_equal(out.view("int64"),
                                   expected_out.view("int64"))
Exemple #14
0
class TestResolveDescriptors:
    # Test mainly error paths of the resolve_descriptors function,
    # note that the `casting_unittests` tests exercise this non-error paths.

    # Casting implementations are the main/only current user:
    method = get_castingimpl(type(np.dtype("d")), type(np.dtype("f")))

    @pytest.mark.parametrize("args", [
        (True,),  # Not a tuple.
        ((None,)),  # Too few elements
        ((None, None, None),),  # Too many
        ((None, None),),  # Input dtype is None, which is invalid.
        ((np.dtype("d"), True),),  # Output dtype is not a dtype
        ((np.dtype("f"), None),),  # Input dtype does not match method
    ])
    def test_invalid_arguments(self, args):
        with pytest.raises(TypeError):
            self.method._resolve_descriptors(*args)