Repository: pandas-dev/pandas
Pull request #55381 (2023-10-03 20:02:39)

Backport PR #55364 on branch 2.1.x (BUG: eq not implemented for categorical and arrow backed strings)
Backport PR #55364: BUG: eq not implemented for categorical and arrow backed strings


Patch:
diff --git a/doc/source/whatsnew/v2.1.2.rst b/doc/source/whatsnew/v2.1.2.rst
index 090df1489e..ff2df2d425 100644
--- a/doc/source/whatsnew/v2.1.2.rst
+++ b/doc/source/whatsnew/v2.1.2.rst
@@ -21,7 +21,9 @@ Fixed regressions
 
 Bug fixes
 ~~~~~~~~~
+- Fixed bug in :meth:`Categorical.equals` if other has arrow backed string dtype (:issue:`55364`)
 - Fixed bug in :meth:`DataFrame.idxmin` and :meth:`DataFrame.idxmax` raising for arrow dtypes (:issue:`55368`)
+- Fixed bug in :meth:`Index.insert` raising when inserting ``None`` into :class:`Index` with ``dtype="string[pyarrow_numpy]"`` (:issue:`55365`)
 -
 
 .. ---------------------------------------------------------------------------
diff --git a/pandas/core/arrays/arrow/array.py b/pandas/core/arrays/arrow/array.py
index 190fd8fd54..2e3ad8bc13 100644
--- a/pandas/core/arrays/arrow/array.py
+++ b/pandas/core/arrays/arrow/array.py
@@ -32,6 +32,7 @@ from pandas.util._validators import validate_fillna_kwargs
 
 from pandas.core.dtypes.cast import infer_dtype_from_scalar
 from pandas.core.dtypes.common import (
+    CategoricalDtype,
     is_array_like,
     is_bool_dtype,
     is_integer,
@@ -628,7 +629,9 @@ class ArrowExtensionArray(
 
     def _cmp_method(self, other, op):
         pc_func = ARROW_CMP_FUNCS[op.__name__]
-        if isinstance(other, (ArrowExtensionArray, np.ndarray, list, BaseMaskedArray)):
+        if isinstance(
+            other, (ArrowExtensionArray, np.ndarray, list, BaseMaskedArray)
+        ) or isinstance(getattr(other, "dtype", None), CategoricalDtype):
             result = pc_func(self._pa_array, self._box_pa(other))
         elif is_scalar(other):
             try:
diff --git a/pandas/core/arrays/string_arrow.py b/pandas/core/arrays/string_arrow.py
index 0b8d3d6b13..7e200dc8af 100644
--- a/pandas/core/arrays/string_arrow.py
+++ b/pandas/core/arrays/string_arrow.py
@@ -579,3 +579,8 @@ class ArrowStringArrayNumpySemantics(ArrowStringArray):
             )
         else:
             return super()._reduce(name, skipna=skipna, keepdims=keepdims, **kwargs)
+
+    def insert(self, loc: int, item) -> ArrowStringArrayNumpySemantics:
+        if item is np.nan:
+            item = libmissing.NA
+        return super().insert(loc, item)  # type: ignore[return-value]


Test patch:
diff --git a/pandas/tests/indexes/base_class/test_reshape.py b/pandas/tests/indexes/base_class/test_reshape.py
index 5ecb2c7536..6586f5f9de 100644
--- a/pandas/tests/indexes/base_class/test_reshape.py
+++ b/pandas/tests/indexes/base_class/test_reshape.py
@@ -54,6 +54,14 @@ class TestReshape:
         tm.assert_index_equal(result, expected)
         assert type(expected[2]) is type(val)
 
+    def test_insert_none_into_string_numpy(self):
+        # GH#55365
+        pytest.importorskip("pyarrow")
+        index = Index(["a", "b", "c"], dtype="string[pyarrow_numpy]")
+        result = index.insert(-1, None)
+        expected = Index(["a", "b", None, "c"], dtype="string[pyarrow_numpy]")
+        tm.assert_index_equal(result, expected)
+
     @pytest.mark.parametrize(
         "pos,expected",
         [
diff --git a/pandas/tests/indexes/categorical/test_equals.py b/pandas/tests/indexes/categorical/test_equals.py
index 1ed8f3a903..a8353f301a 100644
--- a/pandas/tests/indexes/categorical/test_equals.py
+++ b/pandas/tests/indexes/categorical/test_equals.py
@@ -88,3 +88,9 @@ class TestEquals:
         ci = mi.to_flat_index().astype("category")
 
         assert not ci.equals(mi)
+
+    def test_equals_string_dtype(self, any_string_dtype):
+        # GH#55364
+        idx = CategoricalIndex(list("abc"), name="B")
+        other = Index(["a", "b", "c"], name="B", dtype=any_string_dtype)
+        assert idx.equals(other)