summaryrefslogtreecommitdiff
path: root/numpy/core/src
diff options
context:
space:
mode:
authorSebastian Berg <sebastianb@nvidia.com>2023-02-03 12:21:43 +0100
committerSebastian Berg <sebastianb@nvidia.com>2023-02-03 12:40:15 +0100
commitd9e22c0fa0c0f5fb30832cfc1c15f71571663669 (patch)
treeb2159e1ff8f637dd323965a3a000b9e1d068e514 /numpy/core/src
parent9db4a50c7310744f00ae83f93ac936047d9c9165 (diff)
downloadnumpy-d9e22c0fa0c0f5fb30832cfc1c15f71571663669.tar.gz
ENH: Create new DType class aware converters for `dtype=`
This commit also moves the check for legacy dtypes (but does not yet delete it).
Diffstat (limited to 'numpy/core/src')
-rw-r--r--numpy/core/src/multiarray/descriptor.c114
-rw-r--r--numpy/core/src/multiarray/descriptor.h23
2 files changed, 137 insertions, 0 deletions
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c
index ac8aeef1a..80990ee4c 100644
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -1390,6 +1390,120 @@ PyArray_DescrConverter2(PyObject *obj, PyArray_Descr **at)
}
}
+
+/**
+ * Check the descriptor is a legacy "flexible" DType instance, this is
+ * an instance which is (normally) not attached to an array, such as a string
+ * of length 0 or a datetime with no unit.
+ * These should be largely deprecated, and represent only the DType class
+ * for most `dtype` parameters.
+ *
+ * TODO: This function should eventually receive a deprecation warning and
+ * be removed.
+ *
+ * @param descr
+ * @return 1 if this is not a concrete dtype instance 0 otherwise
+ */
+static int
+descr_is_legacy_parametric_instance(PyArray_Descr *descr,
+ PyArray_DTypeMeta *DType)
+{
+ if (!NPY_DT_is_legacy(DType)) {
+ return 0;
+ }
+
+ if (PyDataType_ISUNSIZED(descr)) {
+ return 1;
+ }
+ /* Flexible descr with generic time unit (which can be adapted) */
+ if (PyDataType_ISDATETIME(descr)) {
+ PyArray_DatetimeMetaData *meta;
+ meta = get_datetime_metadata_from_dtype(descr);
+ if (meta->base == NPY_FR_GENERIC) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * Given a descriptor (dtype instance), handles conversion of legacy flexible
+ * "unsized" descriptors to their DType. It returns the DType and descriptor
+ * both results can be NULL (if the input is). But it always sets the DType
+ * when a descriptor is set.
+ *
+ * @param dtype
+ * @param out_descr
+ * @param out_DType
+ * @return 0 on success -1 on failure
+ */
+NPY_NO_EXPORT int
+PyArray_ExtractDTypeAndDescriptor(PyArray_Descr *dtype,
+ PyArray_Descr **out_descr, PyArray_DTypeMeta **out_DType)
+{
+ *out_DType = NULL;
+ *out_descr = NULL;
+
+ if (dtype != NULL) {
+ *out_DType = NPY_DTYPE(dtype);
+ Py_INCREF(*out_DType);
+ if (!descr_is_legacy_parametric_instance((PyArray_Descr *)dtype,
+ *out_DType)) {
+ *out_descr = (PyArray_Descr *)dtype;
+ Py_INCREF(*out_descr);
+ }
+ }
+ return 0;
+}
+
+
+NPY_NO_EXPORT int
+PyArray_DTypeOrDescrConverterRequired(PyObject *obj, npy_dtype_info *dt_info)
+{
+ /*
+ * Allow dtype classes pass, this could also be generalized to at least
+ * some scalar types (right now most of these give instances or)
+ */
+ dt_info->dtype = NULL;
+ dt_info->descr = NULL;
+
+ if (PyObject_TypeCheck(obj, &PyArrayDTypeMeta_Type)) {
+ Py_INCREF(obj);
+ dt_info->dtype = obj;
+ dt_info->descr = NULL;
+ return NPY_SUCCEED;
+ }
+ PyArray_Descr *descr;
+ if (PyArray_DescrConverter(obj, &descr) != NPY_SUCCEED) {
+ return NPY_FAIL;
+ }
+ /*
+ * The above converts e.g. "S" or "S0" to the prototype instance, we make
+ * it behave the same as the DType. This is not fully correct, "S0" should
+ * be considered an instance with actual 0 length.
+ * TODO: It would be nice to fix that eventually.
+ */
+ int res = PyArray_ExtractDTypeAndDescriptor(
+ descr, &dt_info->descr, &dt_info->dtype);
+ Py_DECREF(descr);
+ if (res < 0) {
+ return NPY_FAIL;
+ }
+ return NPY_SUCCEED;
+}
+
+
+NPY_NO_EXPORT int
+PyArray_DTypeOrDescrConverterOptional(PyObject *obj, npy_dtype_info *dt_info)
+{
+ if (obj == Py_None) {
+ /* caller must have initialized for the optional version */
+ return NPY_SUCCEED;
+ }
+ return PyArray_DTypeOrDescrConverterRequired(obj, dt_info);
+}
+
/**
* Get a dtype instance from a python type
*/
diff --git a/numpy/core/src/multiarray/descriptor.h b/numpy/core/src/multiarray/descriptor.h
index 7e6f212f2..b14edc3aa 100644
--- a/numpy/core/src/multiarray/descriptor.h
+++ b/numpy/core/src/multiarray/descriptor.h
@@ -1,6 +1,29 @@
#ifndef NUMPY_CORE_SRC_MULTIARRAY_DESCRIPTOR_H_
#define NUMPY_CORE_SRC_MULTIARRAY_DESCRIPTOR_H_
+
+/*
+ * In some API calls we wish to allow users to pass a DType class or a
+ * dtype instances with different meanings.
+ * This struct is mainly used for the argument parsing in
+ * `PyArray_DTypeOrDescrConverter`.
+ */
+typedef struct {
+ PyArray_DTypeMeta *dtype;
+ PyArray_Descr *descr;
+} npy_dtype_info;
+
+
+NPY_NO_EXPORT int
+PyArray_DTypeOrDescrConverterOptional(PyObject *, npy_dtype_info *dt_info);
+
+NPY_NO_EXPORT int
+PyArray_DTypeOrDescrConverterRequired(PyObject *, npy_dtype_info *dt_info);
+
+NPY_NO_EXPORT int
+PyArray_ExtractDTypeAndDescriptor(PyArray_Descr *dtype,
+ PyArray_Descr **out_descr, PyArray_DTypeMeta **out_DType);
+
NPY_NO_EXPORT PyObject *arraydescr_protocol_typestr_get(
PyArray_Descr *, void *);
NPY_NO_EXPORT PyObject *arraydescr_protocol_descr_get(