diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 0b7135b2d109d..0045fc7b9c221 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -1013,6 +1013,7 @@ Strings ^^^^^^^ - Bug in :meth:`Series.str.zfill` raising ``AttributeError`` for :class:`ArrowDtype` (:issue:`61485`) - Bug in :meth:`Series.value_counts` would not respect ``sort=False`` for series having ``string`` dtype (:issue:`55224`) +- Bug in multiplication with a :class:`StringDtype` incorrectly allowing multiplying by bools; explicitly cast to integers instead (:issue:`62595`) Interval ^^^^^^^^ diff --git a/pandas/core/arrays/string_.py b/pandas/core/arrays/string_.py index 95abd9a953e24..9b4185959e660 100644 --- a/pandas/core/arrays/string_.py +++ b/pandas/core/arrays/string_.py @@ -1113,6 +1113,16 @@ def _cmp_method(self, other, op): other = np.asarray(other) other = other[valid] + other_dtype = getattr(other, "dtype", None) + if op.__name__.strip("_") in ["mul", "rmul"] and ( + lib.is_bool(other) or lib.is_np_dtype(other_dtype, "b") + ): + # GH#62595 + raise TypeError( + "Cannot multiply StringArray by bools. " + "Explicitly cast to integers instead." + ) + if op.__name__ in ops.ARITHMETIC_BINOPS: result = np.empty_like(self._ndarray, dtype="object") result[mask] = self.dtype.na_value diff --git a/pandas/tests/arithmetic/test_string.py b/pandas/tests/arithmetic/test_string.py index 3a038dc3a1fb0..4a12b3ab7fdbc 100644 --- a/pandas/tests/arithmetic/test_string.py +++ b/pandas/tests/arithmetic/test_string.py @@ -112,3 +112,25 @@ def test_pyarrow_numpy_string_invalid(): with pytest.raises(TypeError, match="Invalid comparison"): ser > ser4 + + +def test_mul_bool_invalid(any_string_dtype): + # GH#62595 + dtype = any_string_dtype + ser = Series(["a", "b", "c"], dtype=dtype) + + if dtype == object: + pytest.skip("This is not expect to raise") + elif dtype.storage == "python": + msg = "Cannot multiply StringArray by bools. Explicitly cast to integers" + else: + msg = "Can only string multiply by an integer" + + with pytest.raises(TypeError, match=msg): + False * ser + with pytest.raises(TypeError, match=msg): + ser * True + with pytest.raises(TypeError, match=msg): + ser * np.array([True, False, True], dtype=bool) + with pytest.raises(TypeError, match=msg): + np.array([True, False, True], dtype=bool) * ser