Skip to content

DEPR: logical ops with dtype-less sequences #58242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ Removal of prior version deprecations/changes
- :meth:`SeriesGroupBy.agg` no longer pins the name of the group to the input passed to the provided ``func`` (:issue:`51703`)
- All arguments except ``name`` in :meth:`Index.rename` are now keyword only (:issue:`56493`)
- All arguments except the first ``path``-like argument in IO writers are now keyword only (:issue:`54229`)
- Deprecated allowing logical operations (``||``, ``&``, ``^``) between pandas objects and dtype-less sequences (e.g. ``list``, ``tuple``); wrap the objects in :class:`Series`, :class:`Index`, or ``np.array`` first instead (:issue:`52264`)
- Disallow calling :meth:`Series.replace` or :meth:`DataFrame.replace` without a ``value`` and with non-dict-like ``to_replace`` (:issue:`33302`)
- Disallow constructing a :class:`arrays.SparseArray` with scalar data (:issue:`53039`)
- Disallow non-standard (``np.ndarray``, :class:`Index`, :class:`ExtensionArray`, or :class:`Series`) to :func:`isin`, :func:`unique`, :func:`factorize` (:issue:`52986`)
Expand Down
12 changes: 4 additions & 8 deletions pandas/core/ops/array_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
TYPE_CHECKING,
Any,
)
import warnings

import numpy as np

Expand All @@ -29,7 +28,6 @@
is_supported_dtype,
is_unitless,
)
from pandas.util._exceptions import find_stack_level

from pandas.core.dtypes.cast import (
construct_1d_object_array_from_listlike,
Expand Down Expand Up @@ -424,15 +422,13 @@ def fill_bool(x, left=None):
right = lib.item_from_zerodim(right)
if is_list_like(right) and not hasattr(right, "dtype"):
# e.g. list, tuple
warnings.warn(
raise TypeError(
# GH#52264
"Logical ops (and, or, xor) between Pandas objects and dtype-less "
"sequences (e.g. list, tuple) are deprecated and will raise in a "
"future version. Wrap the object in a Series, Index, or np.array "
"sequences (e.g. list, tuple) are no longer supported. "
"Wrap the object in a Series, Index, or np.array "
"before operating instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
right = construct_1d_object_array_from_listlike(right)

# NB: We assume extract_array has already been called on left and right
lvalues = ensure_wrapped_if_datetimelike(left)
Expand Down
12 changes: 6 additions & 6 deletions pandas/tests/series/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,9 +807,6 @@ def test_series_ops_name_retention(self, flex, box, names, all_binary_operators)
r"Logical ops \(and, or, xor\) between Pandas objects and "
"dtype-less sequences"
)
warn = None
if box in [list, tuple] and is_logical:
warn = FutureWarning

right = box(right)
if flex:
Expand All @@ -818,9 +815,12 @@ def test_series_ops_name_retention(self, flex, box, names, all_binary_operators)
return
result = getattr(left, name)(right)
else:
# GH#37374 logical ops behaving as set ops deprecated
with tm.assert_produces_warning(warn, match=msg):
result = op(left, right)
if is_logical and box in [list, tuple]:
with pytest.raises(TypeError, match=msg):
# GH#52264 logical ops with dtype-less sequences deprecated
op(left, right)
return
result = op(left, right)

assert isinstance(result, Series)
if box in [Index, Series]:
Expand Down
46 changes: 19 additions & 27 deletions pandas/tests/series/test_logical_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def test_logical_operators_int_dtype_with_float(self):
# GH#9016: support bitwise op for integer types
s_0123 = Series(range(4), dtype="int64")

warn_msg = (
err_msg = (
r"Logical ops \(and, or, xor\) between Pandas objects and "
"dtype-less sequences"
)
Expand All @@ -97,9 +97,8 @@ def test_logical_operators_int_dtype_with_float(self):
with pytest.raises(TypeError, match=msg):
s_0123 & 3.14
msg = "unsupported operand type.+for &:"
with pytest.raises(TypeError, match=msg):
with tm.assert_produces_warning(FutureWarning, match=warn_msg):
s_0123 & [0.1, 4, 3.14, 2]
with pytest.raises(TypeError, match=err_msg):
s_0123 & [0.1, 4, 3.14, 2]
with pytest.raises(TypeError, match=msg):
s_0123 & np.array([0.1, 4, 3.14, 2])
with pytest.raises(TypeError, match=msg):
Expand All @@ -108,17 +107,16 @@ def test_logical_operators_int_dtype_with_float(self):
def test_logical_operators_int_dtype_with_str(self):
s_1111 = Series([1] * 4, dtype="int8")

warn_msg = (
err_msg = (
r"Logical ops \(and, or, xor\) between Pandas objects and "
"dtype-less sequences"
)

msg = "Cannot perform 'and_' with a dtyped.+array and scalar of type"
with pytest.raises(TypeError, match=msg):
s_1111 & "a"
with pytest.raises(TypeError, match="unsupported operand.+for &"):
with tm.assert_produces_warning(FutureWarning, match=warn_msg):
s_1111 & ["a", "b", "c", "d"]
with pytest.raises(TypeError, match=err_msg):
s_1111 & ["a", "b", "c", "d"]

def test_logical_operators_int_dtype_with_bool(self):
# GH#9016: support bitwise op for integer types
Expand All @@ -129,17 +127,15 @@ def test_logical_operators_int_dtype_with_bool(self):
result = s_0123 & False
tm.assert_series_equal(result, expected)

warn_msg = (
msg = (
r"Logical ops \(and, or, xor\) between Pandas objects and "
"dtype-less sequences"
)
with tm.assert_produces_warning(FutureWarning, match=warn_msg):
result = s_0123 & [False]
tm.assert_series_equal(result, expected)
with pytest.raises(TypeError, match=msg):
s_0123 & [False]

with tm.assert_produces_warning(FutureWarning, match=warn_msg):
result = s_0123 & (False,)
tm.assert_series_equal(result, expected)
with pytest.raises(TypeError, match=msg):
s_0123 & (False,)

result = s_0123 ^ False
expected = Series([False, True, True, True])
Expand Down Expand Up @@ -188,9 +184,8 @@ def test_logical_ops_bool_dtype_with_ndarray(self):
)

expected = Series([True, False, False, False, False])
with tm.assert_produces_warning(FutureWarning, match=msg):
result = left & right
tm.assert_series_equal(result, expected)
with pytest.raises(TypeError, match=msg):
left & right
result = left & np.array(right)
tm.assert_series_equal(result, expected)
result = left & Index(right)
Expand All @@ -199,9 +194,8 @@ def test_logical_ops_bool_dtype_with_ndarray(self):
tm.assert_series_equal(result, expected)

expected = Series([True, True, True, True, True])
with tm.assert_produces_warning(FutureWarning, match=msg):
result = left | right
tm.assert_series_equal(result, expected)
with pytest.raises(TypeError, match=msg):
left | right
result = left | np.array(right)
tm.assert_series_equal(result, expected)
result = left | Index(right)
Expand All @@ -210,9 +204,8 @@ def test_logical_ops_bool_dtype_with_ndarray(self):
tm.assert_series_equal(result, expected)

expected = Series([False, True, True, True, True])
with tm.assert_produces_warning(FutureWarning, match=msg):
result = left ^ right
tm.assert_series_equal(result, expected)
with pytest.raises(TypeError, match=msg):
left ^ right
result = left ^ np.array(right)
tm.assert_series_equal(result, expected)
result = left ^ Index(right)
Expand Down Expand Up @@ -273,9 +266,8 @@ def test_scalar_na_logical_ops_corners(self):
r"Logical ops \(and, or, xor\) between Pandas objects and "
"dtype-less sequences"
)
with tm.assert_produces_warning(FutureWarning, match=msg):
result = s & list(s)
tm.assert_series_equal(result, expected)
with pytest.raises(TypeError, match=msg):
s & list(s)

def test_scalar_na_logical_ops_corners_aligns(self):
s = Series([2, 3, 4, 5, 6, 7, 8, 9, datetime(2005, 1, 1)])
Expand Down