Repository: pandas-dev/pandas (Python)

Issue:
API: Index.__array_wrap__ stop special-casing bool dtype
For the "just do in 3.0" pile.  `Index.__array_wrap__` reads

```
    def __array_wrap__(self, result, context=None):
        """
        Gets called after a ufunc and other functions e.g. np.split.
        """
        result = lib.item_from_zerodim(result)
        if is_bool_dtype(result) or lib.is_scalar(result) or np.ndim(result) > 1:
            return result

        return Index(result, name=self.name)
```

The is_bool_dtype check is left over from before we supported bool-dtype Indexes.  Affected tests are pandas/tests/indexes/test_numpy_compat.py::test_numpy_ufuncs_other



Discussion:

Patch:
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index 977186d808e81..f2650a64d2c59 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -414,6 +414,7 @@ Other API changes
 - Index set operations (like union or intersection) will now ignore the dtype of
   an empty ``RangeIndex`` or empty ``Index`` with object dtype when determining
   the dtype of the resulting Index (:issue:`60797`)
+- Numpy functions like ``np.isinf`` that return a bool dtype when called on a :class:`Index` object now return a bool-dtype :class:`Index` instead of ``np.ndarray`` (:issue:`52676`)
 
 .. ---------------------------------------------------------------------------
 .. _whatsnew_300.deprecations:
diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py
index 0b719ae21d5b9..fb395f4f7bb1a 100644
--- a/pandas/core/indexes/base.py
+++ b/pandas/core/indexes/base.py
@@ -965,12 +965,8 @@ def __array_wrap__(self, result, context=None, return_scalar=False):
         Gets called after a ufunc and other functions e.g. np.split.
         """
         result = lib.item_from_zerodim(result)
-        if (not isinstance(result, Index) and is_bool_dtype(result.dtype)) or np.ndim(
-            result
-        ) > 1:
-            # exclude Index to avoid warning from is_bool_dtype deprecation;
-            #  in the Index case it doesn't matter which path we go down.
-            # reached in plotting tests with e.g. np.nonzero(index)
+        if np.ndim(result) > 1:
+            # Reached in plotting tests with e.g. np.nonzero(index)
             return result
 
         return Index(result, name=self.name)


Test patch:
diff --git a/pandas/tests/indexes/test_numpy_compat.py b/pandas/tests/indexes/test_numpy_compat.py
index ace78d77350cb..86d0ca1280596 100644
--- a/pandas/tests/indexes/test_numpy_compat.py
+++ b/pandas/tests/indexes/test_numpy_compat.py
@@ -2,6 +2,7 @@
 import pytest
 
 from pandas import (
+    BooleanDtype,
     CategoricalIndex,
     DatetimeIndex,
     Index,
@@ -14,7 +15,6 @@
     is_complex_dtype,
     is_numeric_dtype,
 )
-from pandas.core.arrays import BooleanArray
 from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin
 
 
@@ -111,11 +111,10 @@ def test_numpy_ufuncs_other(index, func):
         if func in (np.isfinite, np.isinf, np.isnan):
             # numpy 1.18 changed isinf and isnan to not raise on dt64/td64
             result = func(index)
-            assert isinstance(result, np.ndarray)
 
             out = np.empty(index.shape, dtype=bool)
             func(index, out=out)
-            tm.assert_numpy_array_equal(out, result)
+            tm.assert_index_equal(Index(out), result)
         else:
             with tm.external_error_raised(TypeError):
                 func(index)
@@ -129,19 +128,20 @@ def test_numpy_ufuncs_other(index, func):
     ):
         # Results in bool array
         result = func(index)
+        assert isinstance(result, Index)
         if not isinstance(index.dtype, np.dtype):
             # e.g. Int64 we expect to get BooleanArray back
-            assert isinstance(result, BooleanArray)
+            assert isinstance(result.dtype, BooleanDtype)
         else:
-            assert isinstance(result, np.ndarray)
+            assert isinstance(result.dtype, np.dtype)
 
         out = np.empty(index.shape, dtype=bool)
         func(index, out=out)
 
         if not isinstance(index.dtype, np.dtype):
-            tm.assert_numpy_array_equal(out, result._data)
+            tm.assert_index_equal(result, Index(out, dtype="boolean"))
         else:
-            tm.assert_numpy_array_equal(out, result)
+            tm.assert_index_equal(result, Index(out))
 
     elif len(index) == 0:
         pass