Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v2.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ Conversion

Strings
^^^^^^^
-
- Bug in :meth:`Series.str` that did not raise a ``TypeError`` when iterated (:issue:`54173`)
-

Interval
Expand Down
39 changes: 23 additions & 16 deletions pandas/core/strings/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ def __getitem__(self, key):
result = self._data.array._str_getitem(key)
return self._wrap_result(result)

def __iter__(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__iter__: ClassVar[None] # type: ignore[assignment] could potentially help type checkers (I didn't try it) or using def __iter__(self) -> NoReturn:

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this passes the is_list_like test, this appears not to completely address iter(ser.str) raising because iter still works because __getitem__ is defined

raise TypeError(f"'{type(self).__name__}' object is not iterable")

def _wrap_result(
self,
result,
Expand Down Expand Up @@ -438,22 +441,26 @@ def _get_series_list(self, others):
others = DataFrame(others, index=idx)
return [others[x] for x in others]
elif is_list_like(others, allow_sets=False):
others = list(others) # ensure iterators do not get read twice etc

# in case of list-like `others`, all elements must be
# either Series/Index/np.ndarray (1-dim)...
if all(
isinstance(x, (ABCSeries, ABCIndex))
or (isinstance(x, np.ndarray) and x.ndim == 1)
for x in others
):
los: list[Series] = []
while others: # iterate through list and append each element
los = los + self._get_series_list(others.pop(0))
return los
# ... or just strings
elif all(not is_list_like(x) for x in others):
return [Series(others, index=idx)]
try:
others = list(others) # ensure iterators do not get read twice etc
except TypeError:
# e.g. ser.str, raise below
pass
else:
# in case of list-like `others`, all elements must be
# either Series/Index/np.ndarray (1-dim)...
if all(
isinstance(x, (ABCSeries, ABCIndex))
or (isinstance(x, np.ndarray) and x.ndim == 1)
for x in others
):
los: list[Series] = []
while others: # iterate through list and append each element
los = los + self._get_series_list(others.pop(0))
return los
# ... or just strings
elif all(not is_list_like(x) for x in others):
return [Series(others, index=idx)]
raise TypeError(
"others must be Series, Index, DataFrame, np.ndarray "
"or list-like (either containing only strings or "
Expand Down
7 changes: 7 additions & 0 deletions pandas/tests/strings/test_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ def test_startswith_endswith_non_str_patterns(pattern):
ser.str.endswith(pattern)


def test_iter_raises():
# GH 54173
ser = Series(["foo", "bar"])
with pytest.raises(TypeError, match="'StringMethods' object is not iterable"):
iter(ser.str)


# test integer/float dtypes (inferred by constructor) and mixed


Expand Down