diff options
Diffstat (limited to 'numpy/core/src/multiarray/dtypemeta.c')
-rw-r--r-- | numpy/core/src/multiarray/dtypemeta.c | 165 |
1 files changed, 120 insertions, 45 deletions
diff --git a/numpy/core/src/multiarray/dtypemeta.c b/numpy/core/src/multiarray/dtypemeta.c index cc99a3eca..9c1f384d8 100644 --- a/numpy/core/src/multiarray/dtypemeta.c +++ b/numpy/core/src/multiarray/dtypemeta.c @@ -9,7 +9,9 @@ #include <numpy/ndarraytypes.h> #include <numpy/arrayscalars.h> #include "npy_pycompat.h" +#include "npy_import.h" +#include "arraytypes.h" #include "common.h" #include "dtypemeta.h" #include "descriptor.h" @@ -18,6 +20,10 @@ #include "scalartypes.h" #include "convert_datatype.h" #include "usertypes.h" +#include "conversion_utils.h" +#include "templ_common.h" +#include "refcount.h" +#include "dtype_traversal.h" #include <assert.h> @@ -73,7 +79,7 @@ dtypemeta_init(PyTypeObject *NPY_UNUSED(type), * @param self * @return nonzero if the object is garbage collected */ -static NPY_INLINE int +static inline int dtypemeta_is_gc(PyObject *dtype_class) { return PyType_Type.tp_is_gc(dtype_class); @@ -122,6 +128,50 @@ legacy_dtype_default_new(PyArray_DTypeMeta *self, return (PyObject *)self->singleton; } +static PyObject * +string_unicode_new(PyArray_DTypeMeta *self, PyObject *args, PyObject *kwargs) +{ + npy_intp size; + + static char *kwlist[] = {"", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, + PyArray_IntpFromPyIntConverter, &size)) { + return NULL; + } + + if (size < 0) { + PyErr_Format(PyExc_ValueError, + "Strings cannot have a negative size but a size of " + "%"NPY_INTP_FMT" was given", size); + return NULL; + } + + PyArray_Descr *res = PyArray_DescrNewFromType(self->type_num); + + if (res == NULL) { + return NULL; + } + + if (self->type_num == NPY_UNICODE) { + // unicode strings are 4 bytes per character + if (npy_mul_sizes_with_overflow(&size, size, 4)) { + PyErr_SetString( + PyExc_TypeError, + "Strings too large to store inside array."); + return NULL; + } + } + + if (size > NPY_MAX_INT) { + PyErr_SetString(PyExc_TypeError, + "Strings too large to store inside array."); + return NULL; + } + + res->elsize = (int)size; + return (PyObject *)res; +} static PyArray_Descr * nonparametric_discover_descr_from_pyobject( @@ -151,7 +201,7 @@ string_discover_descr_from_pyobject( } if (itemsize > NPY_MAX_INT) { PyErr_SetString(PyExc_TypeError, - "string to large to store inside array."); + "string too large to store inside array."); } PyArray_Descr *res = PyArray_DescrNewFromType(cls->type_num); if (res == NULL) { @@ -404,7 +454,7 @@ void_common_instance(PyArray_Descr *descr1, PyArray_Descr *descr2) if (descr1->subarray == NULL && descr1->names == NULL && descr2->subarray == NULL && descr2->names == NULL) { if (descr1->elsize != descr2->elsize) { - PyErr_SetString(PyExc_TypeError, + PyErr_SetString(npy_DTypePromotionError, "Invalid type promotion with void datatypes of different " "lengths. Use the `np.bytes_` datatype instead to pad the " "shorter value with trailing zero bytes."); @@ -443,7 +493,7 @@ void_common_instance(PyArray_Descr *descr1, PyArray_Descr *descr2) return NULL; } if (!cmp) { - PyErr_SetString(PyExc_TypeError, + PyErr_SetString(npy_DTypePromotionError, "invalid type promotion with subarray datatypes " "(shape mismatch)."); return NULL; @@ -473,11 +523,12 @@ void_common_instance(PyArray_Descr *descr1, PyArray_Descr *descr2) return new_descr; } - PyErr_SetString(PyExc_TypeError, + PyErr_SetString(npy_DTypePromotionError, "invalid type promotion with structured datatype(s)."); return NULL; } + NPY_NO_EXPORT int python_builtins_are_known_scalar_types( PyArray_DTypeMeta *NPY_UNUSED(cls), PyTypeObject *pytype) @@ -617,6 +668,12 @@ string_unicode_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) static PyArray_DTypeMeta * datetime_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) { + /* + * Timedelta/datetime shouldn't actually promote at all. That they + * currently do means that we need additional hacks in the comparison + * type resolver. For comparisons we have to make sure we reject it + * nicely in order to return an array of True/False values. + */ if (cls->type_num == NPY_DATETIME && other->type_num == NPY_TIMEDELTA) { /* * TODO: We actually currently do allow promotion here. This is @@ -671,12 +728,17 @@ object_common_dtype( * be a HeapType and its instances should be exact PyArray_Descr structs. * * @param descr The descriptor that should be wrapped. - * @param name The name for the DType, if NULL the type character is used. + * @param name The name for the DType. + * @param alias A second name which is also set to the new class for builtins + * (i.e. `np.types.LongDType` for `np.types.Int64DType`). + * Some may have more aliases, as `intp` is not its own thing, + * as of writing this, these are not added here. * * @returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) +dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr, + const char *name, const char *alias) { int has_type_set = Py_TYPE(descr) == &PyArrayDescr_Type; @@ -703,47 +765,14 @@ dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) return -1; } - /* - * Note: we have no intention of freeing the memory again since this - * behaves identically to static type definition (see comment above). - * This is seems cleaner for the legacy API, in the new API both static - * and heap types are possible (some difficulty arises from the fact that - * these are instances of DTypeMeta and not type). - * In particular our own DTypes can be true static declarations. - * However, this function remains necessary for legacy user dtypes. - */ - - const char *scalar_name = descr->typeobj->tp_name; - /* - * We have to take only the name, and ignore the module to get - * a reasonable __name__, since static types are limited in this regard - * (this is not ideal, but not a big issue in practice). - * This is what Python does to print __name__ for static types. - */ - const char *dot = strrchr(scalar_name, '.'); - if (dot) { - scalar_name = dot + 1; - } - Py_ssize_t name_length = strlen(scalar_name) + 14; - - char *tp_name = PyMem_Malloc(name_length); - if (tp_name == NULL) { - PyErr_NoMemory(); - return -1; - } - - snprintf(tp_name, name_length, "numpy.dtype[%s]", scalar_name); - NPY_DType_Slots *dt_slots = PyMem_Malloc(sizeof(NPY_DType_Slots)); if (dt_slots == NULL) { - PyMem_Free(tp_name); return -1; } memset(dt_slots, '\0', sizeof(NPY_DType_Slots)); PyArray_DTypeMeta *dtype_class = PyMem_Malloc(sizeof(PyArray_DTypeMeta)); if (dtype_class == NULL) { - PyMem_Free(tp_name); PyMem_Free(dt_slots); return -1; } @@ -765,13 +794,19 @@ dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) .tp_flags = Py_TPFLAGS_DEFAULT, .tp_base = &PyArrayDescr_Type, .tp_new = (newfunc)legacy_dtype_default_new, + .tp_doc = ( + "DType class corresponding to the scalar type and dtype of " + "the same name.\n\n" + "Please see `numpy.dtype` for the typical way to create\n" + "dtype instances and :ref:`arrays.dtypes` for additional\n" + "information."), },}, .flags = NPY_DT_LEGACY, /* Further fields are not common between DTypes */ }; memcpy(dtype_class, &prototype, sizeof(PyArray_DTypeMeta)); /* Fix name of the Type*/ - ((PyTypeObject *)dtype_class)->tp_name = tp_name; + ((PyTypeObject *)dtype_class)->tp_name = name; dtype_class->dt_slots = dt_slots; /* Let python finish the initialization (probably unnecessary) */ @@ -803,6 +838,7 @@ dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) dt_slots->common_dtype = default_builtin_common_dtype; dt_slots->common_instance = NULL; dt_slots->ensure_canonical = ensure_native_byteorder; + dt_slots->get_fill_zero_loop = NULL; if (PyTypeNum_ISSIGNED(dtype_class->type_num)) { /* Convert our scalars (raise on too large unsigned and NaN, etc.) */ @@ -814,6 +850,8 @@ dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) } else if (descr->type_num == NPY_OBJECT) { dt_slots->common_dtype = object_common_dtype; + dt_slots->get_fill_zero_loop = npy_object_get_fill_zero_loop; + dt_slots->get_clear_loop = npy_get_clear_object_strided_loop; } else if (PyTypeNum_ISDATETIME(descr->type_num)) { /* Datetimes are flexible, but were not considered previously */ @@ -835,6 +873,10 @@ dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) void_discover_descr_from_pyobject); dt_slots->common_instance = void_common_instance; dt_slots->ensure_canonical = void_ensure_canonical; + dt_slots->get_fill_zero_loop = + npy_get_zerofill_void_and_legacy_user_dtype_loop; + dt_slots->get_clear_loop = + npy_get_clear_void_and_legacy_user_dtype_loop; } else { dt_slots->default_descr = string_and_unicode_default_descr; @@ -843,9 +885,14 @@ dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) string_discover_descr_from_pyobject); dt_slots->common_dtype = string_unicode_common_dtype; dt_slots->common_instance = string_unicode_common_instance; + ((PyTypeObject*)dtype_class)->tp_new = (newfunc)string_unicode_new; } } + if (PyTypeNum_ISNUMBER(descr->type_num)) { + dtype_class->flags |= NPY_DT_NUMERIC; + } + if (_PyArray_MapPyTypeToDType(dtype_class, descr->typeobj, PyTypeNum_ISUSERDEF(dtype_class->type_num)) < 0) { Py_DECREF(dtype_class); @@ -855,6 +902,21 @@ dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) /* Finally, replace the current class of the descr */ Py_SET_TYPE(descr, (PyTypeObject *)dtype_class); + /* And it to the types submodule if it is a builtin dtype */ + if (!PyTypeNum_ISUSERDEF(descr->type_num)) { + static PyObject *add_dtype_helper = NULL; + npy_cache_import("numpy.dtypes", "_add_dtype_helper", &add_dtype_helper); + if (add_dtype_helper == NULL) { + return -1; + } + + if (PyObject_CallFunction( + add_dtype_helper, + "Os", (PyObject *)dtype_class, alias) == NULL) { + return -1; + } + } + return 0; } @@ -865,22 +927,35 @@ dtypemeta_get_abstract(PyArray_DTypeMeta *self) { } static PyObject * +dtypemeta_get_legacy(PyArray_DTypeMeta *self) { + return PyBool_FromLong(NPY_DT_is_legacy(self)); +} + +static PyObject * dtypemeta_get_parametric(PyArray_DTypeMeta *self) { return PyBool_FromLong(NPY_DT_is_parametric(self)); } +static PyObject * +dtypemeta_get_is_numeric(PyArray_DTypeMeta *self) { + return PyBool_FromLong(NPY_DT_is_numeric(self)); +} + /* * Simple exposed information, defined for each DType (class). */ static PyGetSetDef dtypemeta_getset[] = { {"_abstract", (getter)dtypemeta_get_abstract, NULL, NULL, NULL}, + {"_legacy", (getter)dtypemeta_get_legacy, NULL, NULL, NULL}, {"_parametric", (getter)dtypemeta_get_parametric, NULL, NULL, NULL}, + {"_is_numeric", (getter)dtypemeta_get_is_numeric, NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL} }; static PyMemberDef dtypemeta_members[] = { {"type", - T_OBJECT, offsetof(PyArray_DTypeMeta, scalar_type), READONLY, NULL}, + T_OBJECT, offsetof(PyArray_DTypeMeta, scalar_type), READONLY, + "scalar type corresponding to the DType."}, {NULL, 0, 0, 0, NULL}, }; @@ -893,12 +968,12 @@ NPY_NO_EXPORT PyTypeObject PyArrayDTypeMeta_Type = { /* Types are garbage collected (see dtypemeta_is_gc documentation) */ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_doc = "Preliminary NumPy API: The Type of NumPy DTypes (metaclass)", - .tp_getset = dtypemeta_getset, + .tp_traverse = (traverseproc)dtypemeta_traverse, .tp_members = dtypemeta_members, + .tp_getset = dtypemeta_getset, .tp_base = NULL, /* set to PyType_Type at import time */ - .tp_alloc = dtypemeta_alloc, .tp_init = (initproc)dtypemeta_init, + .tp_alloc = dtypemeta_alloc, .tp_new = dtypemeta_new, .tp_is_gc = dtypemeta_is_gc, - .tp_traverse = (traverseproc)dtypemeta_traverse, }; |