summaryrefslogtreecommitdiff
path: root/numpy/core
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/core')
-rw-r--r--numpy/core/src/multiarray/dtypemeta.c24
-rw-r--r--numpy/core/tests/test_array_coercion.py30
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]")])