summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2020-10-08 05:17:58 +0300
committerGitHub <noreply@github.com>2020-10-08 05:17:58 +0300
commit1947bca958a101484d942b864482418cbcbf2fde (patch)
tree0116022cb0193e2ed998abe00dd54ecdaeea2d06
parent382758355998951cea2b9f6ad1fb83e7dc4c3a02 (diff)
downloadnumpy-revert-17320-relax-object-dtype-with-ref.tar.gz
Revert "BUG: allow registration of hard-coded structured dtypes"revert-17320-relax-object-dtype-with-ref
-rw-r--r--numpy/core/src/multiarray/_multiarray_tests.c.src68
-rw-r--r--numpy/core/src/multiarray/dtypemeta.c22
-rw-r--r--numpy/core/src/multiarray/usertypes.c35
-rw-r--r--numpy/core/tests/test_dtype.py37
4 files changed, 13 insertions, 149 deletions
diff --git a/numpy/core/src/multiarray/_multiarray_tests.c.src b/numpy/core/src/multiarray/_multiarray_tests.c.src
index 5b6b6dc78..0bf6958cd 100644
--- a/numpy/core/src/multiarray/_multiarray_tests.c.src
+++ b/numpy/core/src/multiarray/_multiarray_tests.c.src
@@ -619,71 +619,6 @@ fromstring_null_term_c_api(PyObject *dummy, PyObject *byte_obj)
}
-/*
- * Create a custom field dtype from an existing void one (and test some errors).
- * The dtypes created by this function may be not be usable (or even crash
- * while using).
- */
-static PyObject *
-create_custom_field_dtype(PyObject *NPY_UNUSED(mod), PyObject *args)
-{
- PyArray_Descr *dtype;
- PyTypeObject *scalar_type;
- PyTypeObject *original_type = NULL;
- int error_path;
-
- if (!PyArg_ParseTuple(args, "O!O!i",
- &PyArrayDescr_Type, &dtype,
- &PyType_Type, &scalar_type,
- &error_path)) {
- return NULL;
- }
- /* check that the result should be more or less valid */
- if (dtype->type_num != NPY_VOID || dtype->fields == NULL ||
- !PyDict_CheckExact(dtype->fields) ||
- PyTuple_Size(dtype->names) != 1 ||
- !PyDataType_REFCHK(dtype) ||
- dtype->elsize != sizeof(PyObject *)) {
- PyErr_SetString(PyExc_ValueError,
- "Bad dtype passed to test function, must be an object "
- "containing void with a single field.");
- return NULL;
- }
-
- /* Copy and then appropriate this dtype */
- original_type = Py_TYPE(dtype);
- dtype = PyArray_DescrNew(dtype);
- if (dtype == NULL) {
- return NULL;
- }
-
- Py_INCREF(scalar_type);
- Py_SETREF(dtype->typeobj, scalar_type);
- if (error_path == 1) {
- /* Test that we reject this, if fields was not already set */
- Py_SETREF(dtype->fields, NULL);
- }
- else if (error_path == 2) {
- /*
- * Test that we reject this if the type is not set to something that
- * we are pretty sure can be safely replaced.
- */
- Py_SET_TYPE(dtype, scalar_type);
- }
- else if (error_path != 0) {
- PyErr_SetString(PyExc_ValueError,
- "invalid error argument to test function.");
- }
- if (PyArray_RegisterDataType(dtype) < 0) {
- /* Fix original type in the error_path == 2 case. */
- Py_SET_TYPE(dtype, original_type);
- return NULL;
- }
- Py_INCREF(dtype);
- return (PyObject *)dtype;
-}
-
-
/* check no elison for avoided increfs */
static PyObject *
incref_elide(PyObject *dummy, PyObject *args)
@@ -2155,9 +2090,6 @@ static PyMethodDef Multiarray_TestsMethods[] = {
{"fromstring_null_term_c_api",
fromstring_null_term_c_api,
METH_O, NULL},
- {"create_custom_field_dtype",
- create_custom_field_dtype,
- METH_VARARGS, NULL},
{"incref_elide",
incref_elide,
METH_VARARGS, NULL},
diff --git a/numpy/core/src/multiarray/dtypemeta.c b/numpy/core/src/multiarray/dtypemeta.c
index af14bb7e5..109f4a225 100644
--- a/numpy/core/src/multiarray/dtypemeta.c
+++ b/numpy/core/src/multiarray/dtypemeta.c
@@ -467,28 +467,10 @@ object_common_dtype(
NPY_NO_EXPORT int
dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr)
{
- int has_type_set = Py_TYPE(descr) == &PyArrayDescr_Type;
-
- if (!has_type_set) {
- /* Accept if the type was filled in from an existing builtin dtype */
- for (int i = 0; i < NPY_NTYPES; i++) {
- PyArray_Descr *builtin = PyArray_DescrFromType(i);
- has_type_set = Py_TYPE(descr) == Py_TYPE(builtin);
- Py_DECREF(builtin);
- if (has_type_set) {
- break;
- }
- }
- }
- if (!has_type_set) {
+ if (Py_TYPE(descr) != &PyArrayDescr_Type) {
PyErr_Format(PyExc_RuntimeError,
"During creation/wrapping of legacy DType, the original class "
- "was not of PyArrayDescr_Type (it is replaced in this step). "
- "The extension creating a custom DType for type %S must be "
- "modified to ensure `Py_TYPE(descr) == &PyArrayDescr_Type` or "
- "that of an existing dtype (with the assumption it is just "
- "copied over and can be replaced).",
- descr->typeobj, Py_TYPE(descr));
+ "was not PyArrayDescr_Type (it is replaced in this step).");
return -1;
}
diff --git a/numpy/core/src/multiarray/usertypes.c b/numpy/core/src/multiarray/usertypes.c
index f8bb5ece7..3727567e0 100644
--- a/numpy/core/src/multiarray/usertypes.c
+++ b/numpy/core/src/multiarray/usertypes.c
@@ -196,7 +196,7 @@ PyArray_RegisterDataType(PyArray_Descr *descr)
}
}
typenum = NPY_USERDEF + NPY_NUMUSERTYPES;
- descr->type_num = -1;
+ descr->type_num = typenum;
if (PyDataType_ISUNSIZED(descr)) {
PyErr_SetString(PyExc_ValueError, "cannot register a" \
"flexible data-type");
@@ -215,31 +215,18 @@ PyArray_RegisterDataType(PyArray_Descr *descr)
" is missing.");
return -1;
}
+ if (descr->flags & (NPY_ITEM_IS_POINTER | NPY_ITEM_REFCOUNT)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Legacy user dtypes referencing python objects or generally "
+ "allocated memory are unsupported. "
+ "If you see this error in an existing, working code base, "
+ "please contact the NumPy developers.");
+ return -1;
+ }
if (descr->typeobj == NULL) {
PyErr_SetString(PyExc_ValueError, "missing typeobject");
return -1;
}
- if (descr->flags & (NPY_ITEM_IS_POINTER | NPY_ITEM_REFCOUNT)) {
- /*
- * User dtype can't actually do reference counting, however, there
- * are existing hacks (e.g. xpress), which use a structured one:
- * dtype((xpress.var, [('variable', 'O')]))
- * so we have to support this. But such a structure must be constant
- * (i.e. fixed at registration time, this is the case for `xpress`).
- */
- if (descr->names == NULL || descr->fields == NULL ||
- !PyDict_CheckExact(descr->fields)) {
- PyErr_Format(PyExc_ValueError,
- "Failed to register dtype for %S: Legacy user dtypes "
- "using `NPY_ITEM_IS_POINTER` or `NPY_ITEM_REFCOUNT` are"
- "unsupported. It is possible to create such a dtype only "
- "if it is a structured dtype with names and fields "
- "hardcoded at registration time.\n"
- "Please contact the NumPy developers if this used to work "
- "but now fails.", descr->typeobj);
- return -1;
- }
- }
if (test_deprecated_arrfuncs_members(f) < 0) {
return -1;
@@ -256,7 +243,7 @@ PyArray_RegisterDataType(PyArray_Descr *descr)
if (dtypemeta_wrap_legacy_descriptor(descr) < 0) {
return -1;
}
- descr->type_num = typenum;
+
return typenum;
}
@@ -316,7 +303,7 @@ PyArray_RegisterCanCast(PyArray_Descr *descr, int totype,
if (!PyTypeNum_ISUSERDEF(descr->type_num) &&
!PyTypeNum_ISUSERDEF(totype)) {
PyErr_SetString(PyExc_ValueError,
- "At least one of the types provided to "
+ "At least one of the types provided to"
"RegisterCanCast must be user-defined.");
return -1;
}
diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py
index 898ceebcd..2e2b0dbe2 100644
--- a/numpy/core/tests/test_dtype.py
+++ b/numpy/core/tests/test_dtype.py
@@ -6,7 +6,6 @@ import gc
import numpy as np
from numpy.core._rational_tests import rational
-from numpy.core._multiarray_tests import create_custom_field_dtype
from numpy.testing import (
assert_, assert_equal, assert_array_equal, assert_raises, HAS_REFCOUNT)
from numpy.compat import pickle
@@ -1339,39 +1338,3 @@ class TestFromCTypes:
pair_type = np.dtype('{},{}'.format(*pair))
expected = np.dtype([('f0', pair[0]), ('f1', pair[1])])
assert_equal(pair_type, expected)
-
-
-class TestUserDType:
- @pytest.mark.leaks_references(reason="dynamically creates custom dtype.")
- def test_custom_structured_dtype(self):
- class mytype:
- pass
-
- blueprint = np.dtype([("field", object)])
- dt = create_custom_field_dtype(blueprint, mytype, 0)
- assert dt.type == mytype
- # We cannot (currently) *create* this dtype with `np.dtype` because
- # mytype does not inherit from `np.generic`. This seems like an
- # unnecessary restriction, but one that has been around forever:
- assert np.dtype(mytype) == np.dtype("O")
-
- with pytest.raises(RuntimeError):
- # Registering a second time should fail
- create_custom_field_dtype(blueprint, mytype, 0)
-
- def test_custom_structured_dtype_errors(self):
- class mytype:
- pass
-
- blueprint = np.dtype([("field", object)])
-
- with pytest.raises(ValueError):
- # Tests what happens if fields are unset during creation
- # which is currently rejected due to the containing object
- # (see PyArray_RegisterDataType).
- create_custom_field_dtype(blueprint, mytype, 1)
-
- with pytest.raises(RuntimeError):
- # Tests that a dtype must have its type field set up to np.dtype
- # or in this case a builtin instance.
- create_custom_field_dtype(blueprint, mytype, 2)