Skip to content

Commit

Permalink
Merge pull request #25794 from mattip/deprecations
Browse files Browse the repository at this point in the history
DEP: expire some deprecations
  • Loading branch information
charris committed Feb 9, 2024
2 parents 6db2209 + ef4b830 commit f6f3e41
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 173 deletions.
6 changes: 6 additions & 0 deletions doc/release/upcoming_changes/25794.expired.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
* Assigning to the data attribute is disallowed and will raise

* ``np.binary_repr(a, width)`` will raise if width is too small

* Using ``NPY_CHAR`` in ``PyArray_DescrFromType()`` will raise, use
``NPY_STRING`` ``NPY_UNICODE``, or ``NPY_VSTRING`` instead.
14 changes: 1 addition & 13 deletions numpy/_core/include/numpy/ndarraytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@
#define __has_extension(x) 0
#endif

#if !defined(_NPY_NO_DEPRECATIONS) && \
((defined(__GNUC__)&& __GNUC__ >= 6) || \
__has_extension(attribute_deprecated_with_message))
#define NPY_ATTR_DEPRECATE(text) __attribute__ ((deprecated (text)))
#else
#define NPY_ATTR_DEPRECATE(text)
#endif

/*
* There are several places in the code where an array of dimensions
* is allocated statically. This is the size of that static
Expand Down Expand Up @@ -69,7 +61,7 @@ enum NPY_TYPES { NPY_BOOL=0,
*/
NPY_DATETIME, NPY_TIMEDELTA, NPY_HALF,

NPY_CHAR NPY_ATTR_DEPRECATE("Use NPY_STRING"),
NPY_CHAR, /* Deprecated, will raise if used */

/*
* New types added after NumPy 2.0
Expand All @@ -92,10 +84,6 @@ enum NPY_TYPES { NPY_BOOL=0,
/* The number of legacy old-style dtypes */
#define NPY_NTYPES_LEGACY 24

#if defined(_MSC_VER) && !defined(__clang__)
#pragma deprecated(NPY_CHAR)
#endif

/* basetype array priority */
#define NPY_PRIORITY 0.0

Expand Down
13 changes: 6 additions & 7 deletions numpy/_core/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -2062,12 +2062,11 @@ def binary_repr(num, width=None):
'11101'
"""
def warn_if_insufficient(width, binwidth):
def err_if_insufficient(width, binwidth):
if width is not None and width < binwidth:
warnings.warn(
"Insufficient bit width provided. This behavior "
"will raise an error in the future.", DeprecationWarning,
stacklevel=3)
raise ValueError(
f"Insufficient bit {width=} provided for {binwidth=}"
)

# Ensure that num is a Python integer to avoid overflow or unwanted
# casts to floating point.
Expand All @@ -2081,7 +2080,7 @@ def warn_if_insufficient(width, binwidth):
binwidth = len(binary)
outwidth = (binwidth if width is None
else builtins.max(binwidth, width))
warn_if_insufficient(width, binwidth)
err_if_insufficient(width, binwidth)
return binary.zfill(outwidth)

else:
Expand All @@ -2101,7 +2100,7 @@ def warn_if_insufficient(width, binwidth):
binwidth = len(binary)

outwidth = builtins.max(binwidth, width)
warn_if_insufficient(width, binwidth)
err_if_insufficient(width, binwidth)
return '1' * (outwidth - binwidth) + binary


Expand Down
3 changes: 1 addition & 2 deletions numpy/_core/src/multiarray/_multiarray_tests.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <Python.h>

#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _NPY_NO_DEPRECATIONS /* for NPY_CHAR */
#include "numpy/arrayobject.h"
#include "numpy/arrayscalars.h"
#include "numpy/npy_math.h"
Expand Down Expand Up @@ -623,7 +622,7 @@ incref_elide_l(PyObject *dummy, PyObject *args)
return res;
}

/* used to test NPY_CHAR usage emits deprecation warning */
/* used to test NPY_CHAR usage raises an error */
static PyObject*
npy_char_deprecation(PyObject* NPY_UNUSED(self), PyObject* NPY_UNUSED(args))
{
Expand Down
18 changes: 5 additions & 13 deletions numpy/_core/src/multiarray/arraytypes.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE
#define _UMATHMODULE
#define _NPY_NO_DEPRECATIONS /* for NPY_CHAR */

#include "numpy/npy_common.h"
#include "numpy/arrayobject.h"
Expand Down Expand Up @@ -4295,18 +4294,11 @@ PyArray_DescrFromType(int type)
*/
return NULL;
}
else if ((type == NPY_CHAR) || (type == NPY_CHARLTR)) {
if (type == NPY_CHAR) {
/*
* warning added 2017-04-25, 1.13
* deprecated in 1.7
* */
if (DEPRECATE("The NPY_CHAR type_num is deprecated. "
"Please port your code to use "
"NPY_STRING instead.") < 0) {
return NULL;
}
}
else if (type == NPY_CHAR) {
/* Deprecation expired for NumPy 2.0 */
ret = NULL;
}
else if (type == NPY_CHARLTR) {
ret = PyArray_DescrNew(_builtin_descrs[NPY_STRING]);
if (ret == NULL) {
return NULL;
Expand Down
87 changes: 1 addition & 86 deletions numpy/_core/src/multiarray/getset.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,91 +337,6 @@ array_data_get(PyArrayObject *self, void *NPY_UNUSED(ignored))
return PyMemoryView_FromObject((PyObject *)self);
}

static int
array_data_set(PyArrayObject *self, PyObject *op, void *NPY_UNUSED(ignored))
{
void *buf;
Py_ssize_t buf_len;
int writeable=1;
Py_buffer view;

/* 2016-19-02, 1.12 */
int ret = DEPRECATE("Assigning the 'data' attribute is an "
"inherently unsafe operation and will "
"be removed in the future.");
if (ret < 0) {
return -1;
}

if (op == NULL) {
PyErr_SetString(PyExc_AttributeError,
"Cannot delete array data");
return -1;
}
if (PyObject_GetBuffer(op, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) {
writeable = 0;
PyErr_Clear();
if (PyObject_GetBuffer(op, &view, PyBUF_SIMPLE) < 0) {
return -1;
}
}
buf = view.buf;
buf_len = view.len;
/*
* In Python 3 both of the deprecated functions PyObject_AsWriteBuffer and
* PyObject_AsReadBuffer that this code replaces release the buffer. It is
* up to the object that supplies the buffer to guarantee that the buffer
* sticks around after the release.
*/
PyBuffer_Release(&view);

if (!PyArray_ISONESEGMENT(self)) {
PyErr_SetString(PyExc_AttributeError,
"cannot set single-segment buffer for discontiguous array");
return -1;
}
if (PyArray_NBYTES(self) > buf_len) {
PyErr_SetString(PyExc_AttributeError, "not enough data for array");
return -1;
}
if (PyArray_FLAGS(self) & NPY_ARRAY_OWNDATA) {
PyArray_XDECREF(self);
size_t nbytes = PyArray_NBYTES(self);
if (nbytes == 0) {
nbytes = 1;
}
PyObject *handler = PyArray_HANDLER(self);
if (handler == NULL) {
/* This can happen if someone arbitrarily sets NPY_ARRAY_OWNDATA */
PyErr_SetString(PyExc_RuntimeError,
"no memory handler found but OWNDATA flag set");
return -1;
}
PyDataMem_UserFREE(PyArray_DATA(self), nbytes, handler);
Py_CLEAR(((PyArrayObject_fields *)self)->mem_handler);
}
if (PyArray_BASE(self)) {
if (PyArray_FLAGS(self) & NPY_ARRAY_WRITEBACKIFCOPY) {
PyArray_ENABLEFLAGS((PyArrayObject *)PyArray_BASE(self),
NPY_ARRAY_WRITEABLE);
PyArray_CLEARFLAGS(self, NPY_ARRAY_WRITEBACKIFCOPY);
}
Py_DECREF(PyArray_BASE(self));
((PyArrayObject_fields *)self)->base = NULL;
}
Py_INCREF(op);
if (PyArray_SetBaseObject(self, op) < 0) {
return -1;
}
((PyArrayObject_fields *)self)->data = buf;
((PyArrayObject_fields *)self)->flags = NPY_ARRAY_CARRAY;
if (!writeable) {
PyArray_CLEARFLAGS(self, ~NPY_ARRAY_WRITEABLE);
}
return 0;
}


static PyObject *
array_itemsize_get(PyArrayObject *self, void* NPY_UNUSED(ignored))
{
Expand Down Expand Up @@ -992,7 +907,7 @@ NPY_NO_EXPORT PyGetSetDef array_getsetlist[] = {
NULL, NULL},
{"data",
(getter)array_data_get,
(setter)array_data_set,
NULL,
NULL, NULL},
{"itemsize",
(getter)array_itemsize_get,
Expand Down
52 changes: 0 additions & 52 deletions numpy/_core/tests/test_deprecations.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,50 +138,6 @@ class _VisibleDeprecationTestCase(_DeprecationTestCase):
warning_cls = np.exceptions.VisibleDeprecationWarning


class TestArrayDataAttributeAssignmentDeprecation(_DeprecationTestCase):
"""Assigning the 'data' attribute of an ndarray is unsafe as pointed
out in gh-7093. Eventually, such assignment should NOT be allowed, but
in the interests of maintaining backwards compatibility, only a Deprecation-
Warning will be raised instead for the time being to give developers time to
refactor relevant code.
"""

def test_data_attr_assignment(self):
a = np.arange(10)
b = np.linspace(0, 1, 10)

self.message = ("Assigning the 'data' attribute is an "
"inherently unsafe operation and will "
"be removed in the future.")
self.assert_deprecated(a.__setattr__, args=('data', b.data))


class TestBinaryReprInsufficientWidthParameterForRepresentation(_DeprecationTestCase):
"""
If a 'width' parameter is passed into ``binary_repr`` that is insufficient to
represent the number in base 2 (positive) or 2's complement (negative) form,
the function used to silently ignore the parameter and return a representation
using the minimal number of bits needed for the form in question. Such behavior
is now considered unsafe from a user perspective and will raise an error in the future.
"""

def test_insufficient_width_positive(self):
args = (10,)
kwargs = {'width': 2}

self.message = ("Insufficient bit width provided. This behavior "
"will raise an error in the future.")
self.assert_deprecated(np.binary_repr, args=args, kwargs=kwargs)

def test_insufficient_width_negative(self):
args = (-5,)
kwargs = {'width': 2}

self.message = ("Insufficient bit width provided. This behavior "
"will raise an error in the future.")
self.assert_deprecated(np.binary_repr, args=args, kwargs=kwargs)


class TestDTypeAttributeIsDTypeDeprecation(_DeprecationTestCase):
# Deprecated 2021-01-05, NumPy 1.21
message = r".*`.dtype` attribute"
Expand Down Expand Up @@ -227,14 +183,6 @@ def test_conjugate(self):
self.assert_deprecated(a.conjugate)


class TestNPY_CHAR(_DeprecationTestCase):
# 2017-05-03, 1.13.0
def test_npy_char_deprecation(self):
from numpy._core._multiarray_tests import npy_char_deprecation
self.assert_deprecated(npy_char_deprecation)
assert_(npy_char_deprecation() == 'S1')


class TestDatetimeEvent(_DeprecationTestCase):
# 2017-08-11, 1.14.0
def test_3_tuple(self):
Expand Down
25 changes: 25 additions & 0 deletions numpy/_core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -10111,6 +10111,31 @@ def test_partition_fp(N, dtype):
assert_arr_partitioned(np.sort(arr)[k], k,
arr[np.argpartition(arr, k, kind='introselect')])

def test_cannot_assign_data():
a = np.arange(10)
b = np.linspace(0, 1, 10)
with pytest.raises(AttributeError):
a.data = b.data

def test_insufficient_width():
"""
If a 'width' parameter is passed into ``binary_repr`` that is insufficient
to represent the number in base 2 (positive) or 2's complement (negative)
form, the function used to silently ignore the parameter and return a
representation using the minimal number of bits needed for the form in
question. Such behavior is now considered unsafe from a user perspective
and will raise an error.
"""
with pytest.raises(ValueError):
np.binary_repr(10, width=2)
with pytest.raises(ValueError):
np.binary_repr(-5, width=2)

def test_npy_char_raises():
from numpy._core._multiarray_tests import npy_char_deprecation
with pytest.raises(ValueError):
npy_char_deprecation()


class TestDevice:
"""
Expand Down

0 comments on commit f6f3e41

Please sign in to comment.