From 16fb7e9314431181ee7cd78918fc405b3e54faab Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Wed, 1 Mar 2023 13:22:30 -0700 Subject: [PATCH] Apply NPY_DT_NUMERIC flag where appropriate --- .pre-commit-config.yaml | 17 +++++ asciidtype/tests/test_asciidtype.py | 4 ++ metadatadtype/metadatadtype/src/dtype.c | 2 +- metadatadtype/tests/test_metadatadtype.py | 6 +- mpfdtype/mpfdtype/src/dtype.c | 86 ++++++++--------------- mpfdtype/mpfdtype/tests/test_array.py | 10 +-- quaddtype/quaddtype/src/dtype.c | 1 + quaddtype/tests/test_quaddtype.py | 13 ++-- stringdtype/tests/test_stringdtype.py | 4 ++ unytdtype/tests/test_unytdtype.py | 8 ++- unytdtype/unytdtype/src/dtype.c | 2 +- 11 files changed, 83 insertions(+), 70 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bd771361..430ee980 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -102,3 +102,20 @@ repos: rev: 22.12.0 hooks: - id: black + name: 'black for asciidtype' + files: '^asciidtype/.*\.py' + - id: black + name: 'black for metadatadtype' + files: '^metadatadtype/.*\.py' + - id: black + name: 'black for mpfdtype' + files: '^mpfdtype/.*\.py' + - id: black + name: 'black for quaddtype' + files: '^quaddtype/.*\.py' + - id: black + name: 'black for stringdtype' + files: '^stringdtype/.*\.py' + - id: black + name: 'black for unytdtype' + files: '^unytdtype/.*\.py' diff --git a/asciidtype/tests/test_asciidtype.py b/asciidtype/tests/test_asciidtype.py index 0173a7f1..8e7c7ba4 100644 --- a/asciidtype/tests/test_asciidtype.py +++ b/asciidtype/tests/test_asciidtype.py @@ -248,3 +248,7 @@ def test_pickle(): assert res[1] == dtype os.remove(f.name) + + +def test_is_numeric(): + assert not ASCIIDType._is_numeric diff --git a/metadatadtype/metadatadtype/src/dtype.c b/metadatadtype/metadatadtype/src/dtype.c index 3ec9a606..92d4f9e3 100644 --- a/metadatadtype/metadatadtype/src/dtype.c +++ b/metadatadtype/metadatadtype/src/dtype.c @@ -257,7 +257,7 @@ init_metadata_dtype(void) PyArrayMethod_Spec **casts = get_casts(); PyArrayDTypeMeta_Spec MetadataDType_DTypeSpec = { - .flags = NPY_DT_PARAMETRIC, + .flags = NPY_DT_PARAMETRIC | NPY_DT_NUMERIC, .casts = casts, .typeobj = MetadataScalar_Type, .slots = MetadataDType_Slots, diff --git a/metadatadtype/tests/test_metadatadtype.py b/metadatadtype/tests/test_metadatadtype.py index 91186bbd..7a429c59 100644 --- a/metadatadtype/tests/test_metadatadtype.py +++ b/metadatadtype/tests/test_metadatadtype.py @@ -50,5 +50,9 @@ def test_cast_to_float64(): dtype = MetadataDType("test") scalar = MetadataScalar(1, dtype) arr = np.array([scalar, scalar, scalar]) - conv = arr.astype('float64') + conv = arr.astype("float64") assert str(conv) == "[1. 1. 1.]" + + +def test_is_numeric(): + assert MetadataDType._is_numeric diff --git a/mpfdtype/mpfdtype/src/dtype.c b/mpfdtype/mpfdtype/src/dtype.c index 9be7b842..90d4573a 100644 --- a/mpfdtype/mpfdtype/src/dtype.c +++ b/mpfdtype/mpfdtype/src/dtype.c @@ -13,8 +13,6 @@ #include "casts.h" #include "dtype.h" - - /* * Internal helper to create new instances. */ @@ -27,9 +25,8 @@ new_MPFDType_instance(mpfr_prec_t precision) * set in that case. */ if (precision < MPFR_PREC_MIN || precision > MPFR_PREC_MAX) { - PyErr_Format(PyExc_ValueError, - "precision must be between %d and %d.", - MPFR_PREC_MIN, MPFR_PREC_MAX); + PyErr_Format(PyExc_ValueError, "precision must be between %d and %d.", MPFR_PREC_MIN, + MPFR_PREC_MAX); return NULL; } @@ -43,7 +40,7 @@ new_MPFDType_instance(mpfr_prec_t precision) size_t size = mpfr_custom_get_size(precision); if (size > NPY_MAX_INT - sizeof(mpf_field)) { PyErr_SetString(PyExc_TypeError, - "storage of single float would be too large for precision."); + "storage of single float would be too large for precision."); } new->base.elsize = sizeof(mpf_storage) + size; new->base.alignment = _Alignof(mpf_field); @@ -52,7 +49,6 @@ new_MPFDType_instance(mpfr_prec_t precision) return new; } - static MPFDTypeObject * ensure_canonical(MPFDTypeObject *self) { @@ -60,7 +56,6 @@ ensure_canonical(MPFDTypeObject *self) return self; } - static MPFDTypeObject * common_instance(MPFDTypeObject *dtype1, MPFDTypeObject *dtype2) { @@ -74,7 +69,6 @@ common_instance(MPFDTypeObject *dtype1, MPFDTypeObject *dtype2) } } - static PyArray_DTypeMeta * common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) { @@ -82,9 +76,8 @@ common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) * Typenum is useful for NumPy, but there it can still be convenient. * (New-style user dtypes will probably get -1 as type number...) */ - if (other->type_num >= 0 - && PyTypeNum_ISNUMBER(other->type_num) - && !PyTypeNum_ISCOMPLEX(other->type_num)) { + if (other->type_num >= 0 && PyTypeNum_ISNUMBER(other->type_num) && + !PyTypeNum_ISCOMPLEX(other->type_num)) { /* * A (simple) builtin numeric type (not complex) promotes to fixed * precision. @@ -96,18 +89,15 @@ common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) return (PyArray_DTypeMeta *)Py_NotImplemented; } - /* * Functions dealing with scalar logic */ static PyArray_Descr * -mpf_discover_descriptor_from_pyobject( - PyArray_DTypeMeta *NPY_UNUSED(cls), PyObject *obj) +mpf_discover_descriptor_from_pyobject(PyArray_DTypeMeta *NPY_UNUSED(cls), PyObject *obj) { if (Py_TYPE(obj) != &MPFloat_Type) { - PyErr_SetString(PyExc_TypeError, - "Can only store MPFloat in a MPFDType array."); + PyErr_SetString(PyExc_TypeError, "Can only store MPFloat in a MPFDType array."); return NULL; } mpfr_prec_t prec = get_prec_from_object(obj); @@ -117,7 +107,6 @@ mpf_discover_descriptor_from_pyobject( return (PyArray_Descr *)new_MPFDType_instance(prec); } - static int mpf_setitem(MPFDTypeObject *descr, PyObject *obj, char *dataptr) { @@ -167,18 +156,14 @@ mpf_getitem(MPFDTypeObject *descr, char *dataptr) return (PyObject *)new; } - static PyType_Slot MPFDType_Slots[] = { - {NPY_DT_ensure_canonical, &ensure_canonical}, - {NPY_DT_common_instance, &common_instance}, - {NPY_DT_common_dtype, &common_dtype}, - {NPY_DT_discover_descr_from_pyobject, - &mpf_discover_descriptor_from_pyobject}, - {NPY_DT_setitem, &mpf_setitem}, - {NPY_DT_getitem, &mpf_getitem}, - {0, NULL} -}; - + {NPY_DT_ensure_canonical, &ensure_canonical}, + {NPY_DT_common_instance, &common_instance}, + {NPY_DT_common_dtype, &common_dtype}, + {NPY_DT_discover_descr_from_pyobject, &mpf_discover_descriptor_from_pyobject}, + {NPY_DT_setitem, &mpf_setitem}, + {NPY_DT_getitem, &mpf_getitem}, + {0, NULL}}; /* * The following defines everything type object related (i.e. not NumPy @@ -195,59 +180,49 @@ MPFDType_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwds) Py_ssize_t precision; - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "n:MPFDType", kwargs_strs, &precision)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "n:MPFDType", kwargs_strs, &precision)) { return NULL; } return (PyObject *)new_MPFDType_instance(precision); } - static PyObject * MPFDType_repr(MPFDTypeObject *self) { - PyObject *res = PyUnicode_FromFormat( - "MPFDType(%ld)", (long)self->precision); + PyObject *res = PyUnicode_FromFormat("MPFDType(%ld)", (long)self->precision); return res; } - PyObject * MPFDType_get_prec(MPFDTypeObject *self) { return PyLong_FromLong(self->precision); } - NPY_NO_EXPORT PyGetSetDef mpfdtype_getsetlist[] = { - {"prec", - (getter)MPFDType_get_prec, - NULL, - NULL, NULL}, - {NULL, NULL, NULL, NULL, NULL}, /* Sentinel */ + {"prec", (getter)MPFDType_get_prec, NULL, NULL, NULL}, + {NULL, NULL, NULL, NULL, NULL}, /* Sentinel */ }; - /* * This is the basic things that you need to create a Python Type/Class in C. * However, there is a slight difference here because we create a * PyArray_DTypeMeta, which is a larger struct than a typical type. * (This should get a bit nicer eventually with Python >3.11.) */ -PyArray_DTypeMeta MPFDType = {{{ - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "MPFDType.MPFDType", - .tp_basicsize = sizeof(MPFDTypeObject), - .tp_new = MPFDType_new, - .tp_repr = (reprfunc)MPFDType_repr, - .tp_str = (reprfunc)MPFDType_repr, - .tp_getset = mpfdtype_getsetlist, - }}, - /* rest, filled in during DTypeMeta initialization */ +PyArray_DTypeMeta MPFDType = { + {{ + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "MPFDType.MPFDType", + .tp_basicsize = sizeof(MPFDTypeObject), + .tp_new = MPFDType_new, + .tp_repr = (reprfunc)MPFDType_repr, + .tp_str = (reprfunc)MPFDType_repr, + .tp_getset = mpfdtype_getsetlist, + }}, + /* rest, filled in during DTypeMeta initialization */ }; - int init_mpf_dtype(void) { @@ -258,7 +233,7 @@ init_mpf_dtype(void) PyArrayMethod_Spec **casts = init_casts(); PyArrayDTypeMeta_Spec MPFDType_DTypeSpec = { - .flags = NPY_DT_PARAMETRIC, + .flags = NPY_DT_PARAMETRIC | NPY_DT_NUMERIC, .casts = casts, .typeobj = &MPFloat_Type, .slots = MPFDType_Slots, @@ -271,8 +246,7 @@ init_mpf_dtype(void) return -1; } - if (PyArrayInitDTypeMeta_FromSpec( - &MPFDType, &MPFDType_DTypeSpec) < 0) { + if (PyArrayInitDTypeMeta_FromSpec(&MPFDType, &MPFDType_DTypeSpec) < 0) { free_casts(); return -1; } diff --git a/mpfdtype/mpfdtype/tests/test_array.py b/mpfdtype/mpfdtype/tests/test_array.py index e81a252d..02dd511c 100644 --- a/mpfdtype/mpfdtype/tests/test_array.py +++ b/mpfdtype/mpfdtype/tests/test_array.py @@ -1,11 +1,7 @@ -import pytest - -import sys import numpy as np -import operator from numpy.testing import assert_array_equal -from mpfdtype import MPFDType, MPFloat +from mpfdtype import MPFDType def test_advanced_indexing(): @@ -16,3 +12,7 @@ def test_advanced_indexing(): b = arr[[1, 2, 3, 4]] b[...] = 5 # does not mutate arr (internal references not broken) assert_array_equal(arr, orig) + + +def test_is_numeric(): + assert MPFDType._is_numeric diff --git a/quaddtype/quaddtype/src/dtype.c b/quaddtype/quaddtype/src/dtype.c index 21932c82..9042364b 100644 --- a/quaddtype/quaddtype/src/dtype.c +++ b/quaddtype/quaddtype/src/dtype.c @@ -164,6 +164,7 @@ init_quad_dtype(void) }; PyArrayDTypeMeta_Spec QuadDType_DTypeSpec = { + .flags = NPY_DT_NUMERIC, .casts = casts, .typeobj = QuadScalar_Type, .slots = QuadDType_Slots, diff --git a/quaddtype/tests/test_quaddtype.py b/quaddtype/tests/test_quaddtype.py index 0e8d971f..fc65b232 100644 --- a/quaddtype/tests/test_quaddtype.py +++ b/quaddtype/tests/test_quaddtype.py @@ -12,17 +12,22 @@ def test_scalar_creation(): def test_create_with_explicit_dtype(): - assert repr( - np.array([3.0, 3.1, 3.2], dtype=QuadDType()) - ) == "array([3.0, 3.1, 3.2], dtype=This is a quad (128-bit float) dtype.)" + assert ( + repr(np.array([3.0, 3.1, 3.2], dtype=QuadDType())) + == "array([3.0, 3.1, 3.2], dtype=This is a quad (128-bit float) dtype.)" + ) def test_multiply(): x = np.array([3, 8.0], dtype=QuadDType()) - assert str(x * x) == '[9.0 64.0]' + assert str(x * x) == "[9.0 64.0]" def test_bytes(): """Check that each quad is 16 bytes.""" x = np.array([3, 8.0, 1.4], dtype=QuadDType()) assert len(x.tobytes()) == x.size * 16 + + +def test_is_numeric(): + assert QuadDType._is_numeric diff --git a/stringdtype/tests/test_stringdtype.py b/stringdtype/tests/test_stringdtype.py index 82b82456..b0cd97f1 100644 --- a/stringdtype/tests/test_stringdtype.py +++ b/stringdtype/tests/test_stringdtype.py @@ -194,3 +194,7 @@ def test_creation_functions(): # make sure getitem works too assert np.empty(3, dtype=StringDType())[0] == "" + + +def test_is_numeric(): + assert not StringDType._is_numeric diff --git a/unytdtype/tests/test_unytdtype.py b/unytdtype/tests/test_unytdtype.py index 8123f377..ca91e174 100644 --- a/unytdtype/tests/test_unytdtype.py +++ b/unytdtype/tests/test_unytdtype.py @@ -8,7 +8,7 @@ def test_dtype_creation(): dtype = UnytDType("m") assert str(dtype) == "UnytDType('m')" - dtype2 = UnytDType(unyt.Unit('m')) + dtype2 = UnytDType(unyt.Unit("m")) assert str(dtype2) == "UnytDType('m')" assert dtype == dtype2 @@ -69,5 +69,9 @@ def test_insert_with_different_unit(): def test_cast_to_float64(): meter = UnytScalar(1, unyt.m) arr = np.array([meter, meter, meter]) - conv = arr.astype('float64') + conv = arr.astype("float64") assert str(conv) == "[1. 1. 1.]" + + +def test_is_numeric(): + assert UnytDType._is_numeric diff --git a/unytdtype/unytdtype/src/dtype.c b/unytdtype/unytdtype/src/dtype.c index b6cbe845..de96ccaf 100644 --- a/unytdtype/unytdtype/src/dtype.c +++ b/unytdtype/unytdtype/src/dtype.c @@ -280,7 +280,7 @@ init_unyt_dtype(void) PyArrayMethod_Spec **casts = get_casts(); PyArrayDTypeMeta_Spec UnytDType_DTypeSpec = { - .flags = NPY_DT_PARAMETRIC, + .flags = (NPY_DT_PARAMETRIC | NPY_DT_NUMERIC), .casts = casts, .typeobj = UnytScalar_Type, .slots = UnytDType_Slots,