Skip to content
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

CLN: callable to Series.iloc returning a tuple #58626

Merged
merged 4 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
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 @@ -223,6 +223,7 @@ Removal of prior version deprecations/changes
- 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`)
- Changed behavior of :meth:`Series.__getitem__` and :meth:`Series.__setitem__` to always treat integer keys as labels, never as positional, consistent with :class:`DataFrame` behavior (:issue:`50617`)
- Disallow allowing a callable argument to :meth:`Series.iloc` to return a ``tuple`` (:issue:`53769`)
mroeschke marked this conversation as resolved.
Show resolved Hide resolved
- Disallow 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 automatic casting to object in :class:`Series` logical operations (``&``, ``^``, ``||``) between series with mismatched indexes and dtypes other than ``object`` or ``bool`` (:issue:`52538`)
- Disallow calling :meth:`Series.replace` or :meth:`DataFrame.replace` without a ``value`` and with non-dict-like ``to_replace`` (:issue:`33302`)
Expand Down
15 changes: 6 additions & 9 deletions pandas/core/indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def iloc(self) -> _iLocIndexer:
"""
Purely integer-location based indexing for selection by position.

.. deprecated:: 2.2.0
.. versionchanged:: 3.0

Returning a tuple from a callable is deprecated.

Expand Down Expand Up @@ -905,7 +905,7 @@ def __setitem__(self, key, value) -> None:
key = tuple(com.apply_if_callable(x, self.obj) for x in key)
else:
maybe_callable = com.apply_if_callable(key, self.obj)
key = self._check_deprecated_callable_usage(key, maybe_callable)
key = self._raise_callable_usage(key, maybe_callable)
indexer = self._get_setitem_indexer(key)
self._has_valid_setitem_indexer(key)

Expand Down Expand Up @@ -1164,14 +1164,11 @@ def _contains_slice(x: object) -> bool:
def _convert_to_indexer(self, key, axis: AxisInt):
raise AbstractMethodError(self)

def _check_deprecated_callable_usage(self, key: Any, maybe_callable: T) -> T:
def _raise_callable_usage(self, key: Any, maybe_callable: T) -> T:
# GH53533
if self.name == "iloc" and callable(key) and isinstance(maybe_callable, tuple):
warnings.warn(
"Returning a tuple from a callable with iloc "
"is deprecated and will be removed in a future version",
FutureWarning,
stacklevel=find_stack_level(),
raise ValueError(
"Returning a tuple from a callable with iloc is not allowed.",
)
return maybe_callable

Expand All @@ -1189,7 +1186,7 @@ def __getitem__(self, key):
axis = self.axis or 0

maybe_callable = com.apply_if_callable(key, self.obj)
maybe_callable = self._check_deprecated_callable_usage(key, maybe_callable)
maybe_callable = self._raise_callable_usage(key, maybe_callable)
return self._getitem_axis(maybe_callable, axis=axis)

def _is_scalar_access(self, key: tuple):
Expand Down
10 changes: 5 additions & 5 deletions pandas/tests/frame/indexing/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1019,13 +1019,13 @@ def test_single_element_ix_dont_upcast(self, float_frame):
result = df.loc[[0], "b"]
tm.assert_series_equal(result, expected)

def test_iloc_callable_tuple_return_value(self):
# GH53769
def test_iloc_callable_tuple_return_value_raises(self):
# GH53769: Enforced pandas 3.0
df = DataFrame(np.arange(40).reshape(10, 4), index=range(0, 20, 2))
msg = "callable with iloc"
with tm.assert_produces_warning(FutureWarning, match=msg):
msg = "Returning a tuple from"
with pytest.raises(ValueError, match=msg):
df.iloc[lambda _: (0,)]
with tm.assert_produces_warning(FutureWarning, match=msg):
with pytest.raises(ValueError, match=msg):
df.iloc[lambda _: (0,)] = 1

def test_iloc_row(self):
Expand Down
Loading