Skip to content

Commit dc27edb

Browse files
authored
Merge pull request #8297 from charris/expired-deprecations
DEP: Handle expired deprecations.
2 parents 7ec8f69 + f562e5b commit dc27edb

File tree

12 files changed

+183
-235
lines changed

12 files changed

+183
-235
lines changed

doc/release/1.13.0-notes.rst

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,22 @@ existing use.
3838

3939
DeprecationWarning to error
4040
~~~~~~~~~~~~~~~~~~~~~~~~~~~
41+
See Changes section for more detail.
42+
43+
* ``partition``, TypeError when non-integer partition index is used.
44+
* ``NpyIter_AdvancedNew``, ValueError when `oa_ndim == 0` and `op_axes` is NULL
45+
* ``negative(bool_)``, TypeError when negative applied to booleans.
46+
* ``subtract(bool_, bool_)``, TypeError when subtracting boolean from boolean.
47+
* ``np.equal, np.not_equal``, object identity doesn't override failed comparison.
48+
* ``np.equal, np.not_equal``, object identity doesn't override non-boolean comparison.
4149

4250
FutureWarning to changed behavior
4351
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52+
See Changes section for more detail.
4453

45-
* ``numpy.average`` now preserves subclasses, matching the behavior of most
46-
other numpy functions such as ``mean``. As a consequence, also calls that
47-
returned a scalar may now return a subclass array scalar.
54+
* ``numpy.average`` preserves subclasses
55+
* ``array == None`` and ``array != None`` do element-wise comparison.
56+
* ``np.equal, np.not_equal``, object identity doesn't override comparison result.
4857

4958

5059
C API
@@ -74,3 +83,15 @@ For ndarray subclasses, ``numpy.average`` will now return an instance of the
7483
subclass, matching the behavior of most other numpy functions such as ``mean``.
7584
As a consequence, also calls that returned a scalar may now return a subclass
7685
array scalar.
86+
87+
``array == None`` and ``array != None`` do element-wise comparison
88+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
89+
Previously these operations returned scalars ``False`` and ``True`` respectively.
90+
91+
``np.equal, np.not_equal`` for object arrays ignores object identity
92+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
93+
Previously, these functions always treated identical objects as equal. This had
94+
the effect of overriding comparison failures, comparison of objects that did
95+
not return booleans, such as np.arrays, and comparison of objects where the
96+
results differed from object identity, such as NaNs.
97+

numpy/core/src/multiarray/arrayobject.c

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,16 +1354,6 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op)
13541354
n_ops.less_equal);
13551355
break;
13561356
case Py_EQ:
1357-
if (other == Py_None) {
1358-
/* 2013-07-25, 1.7 */
1359-
if (DEPRECATE_FUTUREWARNING("comparison to `None` will result in "
1360-
"an elementwise object comparison in the future.") < 0) {
1361-
return NULL;
1362-
}
1363-
Py_INCREF(Py_False);
1364-
return Py_False;
1365-
}
1366-
13671357
/*
13681358
* The ufunc does not support void/structured types, so these
13691359
* need to be handled specifically. Only a few cases are supported.
@@ -1442,16 +1432,6 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op)
14421432
}
14431433
break;
14441434
case Py_NE:
1445-
if (other == Py_None) {
1446-
/* 2013-07-25, 1.8 */
1447-
if (DEPRECATE_FUTUREWARNING("comparison to `None` will result in "
1448-
"an elementwise object comparison in the future.") < 0) {
1449-
return NULL;
1450-
}
1451-
Py_INCREF(Py_True);
1452-
return Py_True;
1453-
}
1454-
14551435
/*
14561436
* The ufunc does not support void/structured types, so these
14571437
* need to be handled specifically. Only a few cases are supported.

numpy/core/src/multiarray/item_selection.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,11 +1161,8 @@ partition_prep_kth_array(PyArrayObject * ktharray,
11611161
npy_intp nkth, i;
11621162

11631163
if (!PyArray_CanCastSafely(PyArray_TYPE(ktharray), NPY_INTP)) {
1164-
/* 2013-05-18, 1.8 */
1165-
if (DEPRECATE("Calling partition with a non integer index"
1166-
" will result in an error in the future") < 0) {
1167-
return NULL;
1168-
}
1164+
PyErr_Format(PyExc_TypeError, "Partition index must be integer");
1165+
return NULL;
11691166
}
11701167

11711168
if (PyArray_NDIM(ktharray) > 1) {

numpy/core/src/multiarray/nditer_constr.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -161,17 +161,14 @@ NpyIter_AdvancedNew(int nop, PyArrayObject **op_in, npy_uint32 flags,
161161
* Before 1.8, if `oa_ndim == 0`, this meant `op_axes != NULL` was an error.
162162
* With 1.8, `oa_ndim == -1` takes this role, while op_axes in that case
163163
* enforces a 0-d iterator. Using `oa_ndim == 0` with `op_axes == NULL`
164-
* is thus deprecated with version 1.8.
164+
* is thus an error in 1.13 after deprecation.
165165
*/
166166
if ((oa_ndim == 0) && (op_axes == NULL)) {
167-
char* mesg = "using `oa_ndim == 0` when `op_axes` is NULL is "
168-
"deprecated. Use `oa_ndim == -1` or the MultiNew "
169-
"iterator for NumPy <1.8 compatibility";
170-
if (DEPRECATE(mesg) < 0) {
171-
/* 2013-02-23, 1.8 */
172-
return NULL;
173-
}
174-
oa_ndim = -1;
167+
PyErr_Format(PyExc_ValueError,
168+
"Using `oa_ndim == 0` when `op_axes` is NULL. "
169+
"Use `oa_ndim == -1` or the MultiNew "
170+
"iterator for NumPy <1.8 compatibility");
171+
return NULL;
175172
}
176173

177174
/* Error check 'oa_ndim' and 'op_axes', which must be used together */

numpy/core/src/umath/loops.c.src

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,61 +2670,19 @@ OBJECT_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS
26702670
in2 = in2 ? in2 : Py_None;
26712671

26722672
/*
2673-
* Do not use RichCompareBool because it includes an identity check
2674-
* (for == and !=).
2675-
* This is wrong for elementwise behaviour, since it means
2673+
* Do not use RichCompareBool because it includes an identity check for
2674+
* == and !=. This is wrong for elementwise behaviour, since it means
26762675
* that NaN can be equal to NaN and an array is equal to itself.
26772676
*/
26782677
ret_obj = PyObject_RichCompare(in1, in2, Py_@OP@);
26792678
if (ret_obj == NULL) {
2680-
#if @identity@ != -1
2681-
if (in1 == in2) {
2682-
/* 2014-01-26, 1.9 */
2683-
PyErr_Clear();
2684-
if (DEPRECATE("numpy @kind@ will not check object identity "
2685-
"in the future. The comparison error will "
2686-
"be raised.") < 0) {
2687-
return;
2688-
}
2689-
*((npy_bool *)op1) = @identity@;
2690-
continue;
2691-
}
2692-
#endif
26932679
return;
26942680
}
26952681
ret = PyObject_IsTrue(ret_obj);
26962682
Py_DECREF(ret_obj);
26972683
if (ret == -1) {
2698-
#if @identity@ != -1
2699-
if (in1 == in2) {
2700-
/* 2014-01-26, 1.9 */
2701-
PyErr_Clear();
2702-
if (DEPRECATE("numpy @kind@ will not check object identity "
2703-
"in the future. The error trying to get the "
2704-
"boolean value of the comparison result will "
2705-
"be raised.") < 0) {
2706-
return;
2707-
}
2708-
*((npy_bool *)op1) = @identity@;
2709-
continue;
2710-
}
2711-
#endif
27122684
return;
27132685
}
2714-
#if @identity@ != -1
2715-
if ((in1 == in2) && ((npy_bool)ret != @identity@)) {
2716-
/* 2014-01-26, 1.9 */
2717-
if (DEPRECATE_FUTUREWARNING(
2718-
"numpy @kind@ will not check object identity "
2719-
"in the future. The comparison did not return the "
2720-
"same result as suggested by the identity (`is`)) "
2721-
"and will change.") < 0) {
2722-
return;
2723-
}
2724-
*((npy_bool *)op1) = @identity@;
2725-
continue;
2726-
}
2727-
#endif
27282686
*((npy_bool *)op1) = (npy_bool)ret;
27292687
}
27302688
}

numpy/core/src/umath/ufunc_type_resolution.c

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -368,10 +368,10 @@ PyUFunc_SimpleUnaryOperationTypeResolver(PyUFuncObject *ufunc,
368368

369369
NPY_NO_EXPORT int
370370
PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc,
371-
NPY_CASTING casting,
372-
PyArrayObject **operands,
373-
PyObject *type_tup,
374-
PyArray_Descr **out_dtypes)
371+
NPY_CASTING casting,
372+
PyArrayObject **operands,
373+
PyObject *type_tup,
374+
PyArray_Descr **out_dtypes)
375375
{
376376
int ret;
377377
ret = PyUFunc_SimpleUnaryOperationTypeResolver(ufunc, casting, operands,
@@ -382,12 +382,10 @@ PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc,
382382

383383
/* The type resolver would have upcast already */
384384
if (out_dtypes[0]->type_num == NPY_BOOL) {
385-
/* 2013-12-05, 1.9 */
386-
if (DEPRECATE("numpy boolean negative, the `-` operator, is "
387-
"deprecated, use the `~` operator or the logical_not "
388-
"function instead.") < 0) {
389-
return -1;
390-
}
385+
PyErr_Format(PyExc_TypeError,
386+
"The numpy boolean negative, the `-` operator, is not supported, "
387+
"use the `~` operator or the logical_not function instead.");
388+
return -1;
391389
}
392390

393391
return ret;
@@ -798,12 +796,11 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc,
798796

799797
/* The type resolver would have upcast already */
800798
if (out_dtypes[0]->type_num == NPY_BOOL) {
801-
/* 2013-12-05, 1.9 */
802-
if (DEPRECATE("numpy boolean subtract, the `-` operator, is "
803-
"deprecated, use the bitwise_xor, the `^` operator, "
804-
"or the logical_xor function instead.") < 0) {
805-
return -1;
806-
}
799+
PyErr_Format(PyExc_TypeError,
800+
"numpy boolean subtract, the `-` operator, is deprecated, "
801+
"use the bitwise_xor, the `^` operator, or the logical_xor "
802+
"function instead.");
803+
return -1;
807804
}
808805
return ret;
809806
}

numpy/core/tests/test_deprecations.py

Lines changed: 4 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -226,42 +226,6 @@ def assert_not_deprecated(self, function, args=(), kwargs={}):
226226
exceptions=tuple(), args=args, kwargs=kwargs)
227227

228228

229-
class TestBooleanUnaryMinusDeprecation(_DeprecationTestCase):
230-
"""Test deprecation of unary boolean `-`. While + and * are well
231-
defined, unary - is not and even a corrected form seems to have
232-
no real uses.
233-
234-
The deprecation process was started in NumPy 1.9.
235-
"""
236-
message = r"numpy boolean negative, the `-` operator, .*"
237-
238-
def test_unary_minus_operator_deprecation(self):
239-
array = np.array([True])
240-
generic = np.bool_(True)
241-
242-
# Unary minus/negative ufunc:
243-
self.assert_deprecated(operator.neg, args=(array,))
244-
self.assert_deprecated(operator.neg, args=(generic,))
245-
246-
247-
class TestBooleanBinaryMinusDeprecation(_DeprecationTestCase):
248-
"""Test deprecation of binary boolean `-`. While + and * are well
249-
defined, binary - is not and even a corrected form seems to have
250-
no real uses.
251-
252-
The deprecation process was started in NumPy 1.9.
253-
"""
254-
message = r"numpy boolean subtract, the `-` operator, .*"
255-
256-
def test_operator_deprecation(self):
257-
array = np.array([True])
258-
generic = np.bool_(True)
259-
260-
# Minus operator/subtract ufunc:
261-
self.assert_deprecated(operator.sub, args=(array, array))
262-
self.assert_deprecated(operator.sub, args=(generic, generic))
263-
264-
265229
class TestRankDeprecation(_DeprecationTestCase):
266230
"""Test that np.rank is deprecated. The function should simply be
267231
removed. The VisibleDeprecationWarning may become unnecessary.
@@ -307,40 +271,6 @@ def test_string(self):
307271
# following works (and returns False) due to dtype mismatch:
308272
a == []
309273

310-
def test_none_comparison(self):
311-
# Test comparison of None, which should result in element-wise
312-
# comparison in the future. [1, 2] == None should be [False, False].
313-
with warnings.catch_warnings():
314-
warnings.filterwarnings('always', '', FutureWarning)
315-
assert_warns(FutureWarning, operator.eq, np.arange(3), None)
316-
assert_warns(FutureWarning, operator.ne, np.arange(3), None)
317-
318-
with warnings.catch_warnings():
319-
warnings.filterwarnings('error', '', FutureWarning)
320-
assert_raises(FutureWarning, operator.eq, np.arange(3), None)
321-
assert_raises(FutureWarning, operator.ne, np.arange(3), None)
322-
323-
def test_scalar_none_comparison(self):
324-
# Scalars should still just return False and not give a warnings.
325-
# The comparisons are flagged by pep8, ignore that.
326-
with warnings.catch_warnings(record=True) as w:
327-
warnings.filterwarnings('always', '', FutureWarning)
328-
assert_(not np.float32(1) == None)
329-
assert_(not np.str_('test') == None)
330-
# This is dubious (see below):
331-
assert_(not np.datetime64('NaT') == None)
332-
333-
assert_(np.float32(1) != None)
334-
assert_(np.str_('test') != None)
335-
# This is dubious (see below):
336-
assert_(np.datetime64('NaT') != None)
337-
assert_(len(w) == 0)
338-
339-
# For documentation purposes, this is why the datetime is dubious.
340-
# At the time of deprecation this was no behaviour change, but
341-
# it has to be considered when the deprecations are done.
342-
assert_(np.equal(np.datetime64('NaT'), None))
343-
344274
def test_void_dtype_equality_failures(self):
345275
class NotArray(object):
346276
def __array__(self):
@@ -393,58 +323,6 @@ def test_array_richcompare_legacy_weirdness(self):
393323
assert_warns(DeprecationWarning, f, arg1, arg2)
394324

395325

396-
class TestIdentityComparisonDeprecations(_DeprecationTestCase):
397-
"""This tests the equal and not_equal object ufuncs identity check
398-
deprecation. This was due to the usage of PyObject_RichCompareBool.
399-
400-
This tests that for example for `a = np.array([np.nan], dtype=object)`
401-
`a == a` it is warned that False and not `np.nan is np.nan` is returned.
402-
403-
Should be kept in sync with TestComparisonDeprecations and new tests
404-
added when the deprecation is over. Requires only removing of @identity@
405-
(and blocks) from the ufunc loops.c.src of the OBJECT comparisons.
406-
"""
407-
408-
message = "numpy .* will not check object identity in the future."
409-
410-
def test_identity_equality_mismatch(self):
411-
a = np.array([np.nan], dtype=object)
412-
413-
with warnings.catch_warnings():
414-
warnings.filterwarnings('always', '', FutureWarning)
415-
assert_warns(FutureWarning, np.equal, a, a)
416-
assert_warns(FutureWarning, np.not_equal, a, a)
417-
418-
with warnings.catch_warnings():
419-
warnings.filterwarnings('error', '', FutureWarning)
420-
assert_raises(FutureWarning, np.equal, a, a)
421-
assert_raises(FutureWarning, np.not_equal, a, a)
422-
# And the other do not warn:
423-
with np.errstate(invalid='ignore'):
424-
np.less(a, a)
425-
np.greater(a, a)
426-
np.less_equal(a, a)
427-
np.greater_equal(a, a)
428-
429-
def test_comparison_error(self):
430-
class FunkyType(object):
431-
def __eq__(self, other):
432-
raise TypeError("I won't compare")
433-
434-
def __ne__(self, other):
435-
raise TypeError("I won't compare")
436-
437-
a = np.array([FunkyType()])
438-
self.assert_deprecated(np.equal, args=(a, a))
439-
self.assert_deprecated(np.not_equal, args=(a, a))
440-
441-
def test_bool_error(self):
442-
# The comparison result cannot be interpreted as a bool
443-
a = np.array([np.array([1, 2, 3]), None], dtype=object)
444-
self.assert_deprecated(np.equal, args=(a, a))
445-
self.assert_deprecated(np.not_equal, args=(a, a))
446-
447-
448326
class TestAlterdotRestoredotDeprecations(_DeprecationTestCase):
449327
"""The alterdot/restoredot functions are deprecated.
450328
@@ -657,7 +535,7 @@ def foo():
657535

658536
class TestClassicIntDivision(_DeprecationTestCase):
659537
"""
660-
See #7949. Deprecate the numeric-style dtypes with -3 flag in python 2
538+
See #7949. Deprecate the numeric-style dtypes with -3 flag in python 2
661539
if used for division
662540
List of data types: http://docs.scipy.org/doc/numpy/user/basics.types.html
663541
"""
@@ -671,9 +549,9 @@ def test_int_dtypes(self):
671549
import operator as op
672550
dt2 = 'bool_'
673551
for dt1 in deprecated_types:
674-
a = np.array([1,2,3], dtype=dt1)
675-
b = np.array([1,2,3], dtype=dt2)
676-
self.assert_deprecated(op.div, args=(a,b))
552+
a = np.array([1,2,3], dtype=dt1)
553+
b = np.array([1,2,3], dtype=dt2)
554+
self.assert_deprecated(op.div, args=(a,b))
677555
dt2 = dt1
678556

679557

0 commit comments

Comments
 (0)