Skip to content

Commit 5b16660

Browse files
authored
ENH: support pathlib.Path division in StringArray (#62229)
1 parent ef4a9c3 commit 5b16660

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

pandas/core/arrays/arrow/array.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import functools
44
import operator
5+
from pathlib import Path
56
import re
67
import textwrap
78
from typing import (
@@ -951,7 +952,23 @@ def _logical_method(self, other, op) -> Self:
951952
else:
952953
return self._evaluate_op_method(other, op, ARROW_LOGICAL_FUNCS)
953954

954-
def _arith_method(self, other, op) -> Self:
955+
def _arith_method(self, other, op) -> Self | npt.NDArray[np.object_]:
956+
if (
957+
op in [operator.truediv, roperator.rtruediv]
958+
and isinstance(other, Path)
959+
and (
960+
pa.types.is_string(self._pa_array.type)
961+
or pa.types.is_large_string(self._pa_array.type)
962+
)
963+
):
964+
# GH#61940
965+
return np.array(
966+
[
967+
op(x, other) if isinstance(x, str) else self.dtype.na_value
968+
for x in self
969+
],
970+
dtype=object,
971+
)
955972
return self._evaluate_op_method(other, op, ARROW_ARITHMETIC_FUNCS)
956973

957974
def equals(self, other) -> bool:

pandas/core/arrays/string_.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from functools import partial
44
import operator
5+
from pathlib import Path
56
from typing import (
67
TYPE_CHECKING,
78
Any,
@@ -1065,7 +1066,7 @@ def _cmp_method(self, other, op):
10651066
mask = isna(self) | isna(other)
10661067
valid = ~mask
10671068

1068-
if not lib.is_scalar(other):
1069+
if lib.is_list_like(other):
10691070
if len(other) != len(self):
10701071
# prevent improper broadcasting when other is 2D
10711072
raise ValueError(
@@ -1081,6 +1082,9 @@ def _cmp_method(self, other, op):
10811082
result = np.empty_like(self._ndarray, dtype="object")
10821083
result[mask] = self.dtype.na_value
10831084
result[valid] = op(self._ndarray[valid], other)
1085+
if isinstance(other, Path):
1086+
# GH#61940
1087+
return result
10841088
return self._from_backing_data(result)
10851089
else:
10861090
# logical

pandas/tests/strings/test_strings.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
datetime,
33
timedelta,
44
)
5+
from pathlib import Path
56

67
import numpy as np
78
import pytest
@@ -824,3 +825,24 @@ def test_reversed_logical_ops(any_string_dtype):
824825
result = left ^ right
825826
expected = left ^ right.astype(bool)
826827
tm.assert_series_equal(result, expected)
828+
829+
830+
def test_pathlib_path_division(any_string_dtype, request):
831+
# GH#61940
832+
if any_string_dtype == object:
833+
mark = pytest.mark.xfail(
834+
reason="with NA present we go through _masked_arith_op which "
835+
"raises TypeError bc Path is not recognized by lib.is_scalar."
836+
)
837+
request.applymarker(mark)
838+
839+
item = Path("/Users/Irv/")
840+
ser = Series(["A", "B", NA], dtype=any_string_dtype)
841+
842+
result = item / ser
843+
expected = Series([item / "A", item / "B", ser.dtype.na_value], dtype=object)
844+
tm.assert_series_equal(result, expected)
845+
846+
result = ser / item
847+
expected = Series(["A" / item, "B" / item, ser.dtype.na_value], dtype=object)
848+
tm.assert_series_equal(result, expected)

0 commit comments

Comments
 (0)