diff options
Diffstat (limited to 'numpy/core')
-rw-r--r-- | numpy/core/src/multiarray/dtypemeta.c | 24 | ||||
-rw-r--r-- | numpy/core/tests/test_array_coercion.py | 30 |
2 files changed, 45 insertions, 9 deletions
diff --git a/numpy/core/src/multiarray/dtypemeta.c b/numpy/core/src/multiarray/dtypemeta.c index 6f0658a6d..3026e68e9 100644 --- a/numpy/core/src/multiarray/dtypemeta.c +++ b/numpy/core/src/multiarray/dtypemeta.c @@ -258,6 +258,29 @@ datetime_known_scalar_types( } +static int +string_known_scalar_types( + PyArray_DTypeMeta *cls, PyTypeObject *pytype) { + if (python_builtins_are_known_scalar_types(cls, pytype)) { + return 1; + } + if (PyType_IsSubtype(pytype, &PyDatetimeArrType_Type)) { + /* + * TODO: This should likely be deprecated or otherwise resolved. + * Deprecation has to occur in `String->setitem` unfortunately. + * + * Datetime currently do not cast to shorter strings, but string + * coercion for arbitrary values uses `str(obj)[:len]` so it works. + * This means `np.array(np.datetime64("2020-01-01"), "U9")` + * and `np.array(np.datetime64("2020-01-01")).astype("U9")` behave + * differently. + */ + return 1; + } + return 0; +} + + /** * This function takes a PyArray_Descr and replaces its base class with * a newly created dtype subclass (DTypeMeta instances). @@ -395,6 +418,7 @@ dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) void_discover_descr_from_pyobject); } else { + dtype_class->is_known_scalar_type = string_known_scalar_types; dtype_class->discover_descr_from_pyobject = ( string_discover_descr_from_pyobject); } diff --git a/numpy/core/tests/test_array_coercion.py b/numpy/core/tests/test_array_coercion.py index d8ca3e0a8..333f357a9 100644 --- a/numpy/core/tests/test_array_coercion.py +++ b/numpy/core/tests/test_array_coercion.py @@ -354,20 +354,32 @@ class TestTimeScalars: assert_array_equal(arr, cast) assert_array_equal(cast, cast) + @pytest.mark.parametrize("dtype", ["S6", "U6"]) @pytest.mark.parametrize(["val", "unit"], [param(123, "s", id="[s]"), param(123, "D", id="[D]")]) - def test_coercion_assignment_datetime(self, val, unit): + def test_coercion_assignment_datetime(self, val, unit, dtype): + # String from datetime64 assignment is currently special cased to + # never use casting. This is because casting will error in this + # case, and traditionally in most cases the behaviour is maintained + # like this. (`np.array(scalar, dtype="U6")` would have failed before) + # TODO: This discrepency _should_ be resolved, either by relaxing the + # cast, or by deprecating the first part. scalar = np.datetime64(val, unit) + dtype = np.dtype(dtype) + cut_string = dtype.type(str(scalar)[:6]) + + arr = np.array(scalar, dtype=dtype) + assert arr[()] == cut_string + ass = np.ones((), dtype=dtype) + ass[()] = scalar + assert ass[()] == cut_string - # The error type is not ideal, fails because string is too short, - # This should possibly be allowed as an unsafe cast: - with pytest.raises(RuntimeError): - np.array(scalar, dtype="S6") - with pytest.raises(RuntimeError): - np.array(scalar).astype("S6") - ass = np.ones((), dtype="S6") with pytest.raises(RuntimeError): - ass[()] = scalar + # However, unlike the above assignment using `str(scalar)[:6]` + # due to being handled by the string DType and not be casting + # the explicit cast fails: + np.array(scalar).astype(dtype) + @pytest.mark.parametrize(["val", "unit"], [param(123, "s", id="[s]"), param(123, "D", id="[D]")]) |