Repository: modin-project/modin (python)

Issue:
BUG: Using extensions to override a cached_property causes an error
### Modin version checks

- [x] I have checked that this issue has not already been reported.

- [x] I have confirmed this bug exists on the latest released version of Modin.

- [x] I have confirmed this bug exists on the main branch of Modin. (In order to do this you can follow [this guide](https://modin.readthedocs.io/en/stable/getting_started/installation.html#installing-from-the-github-main-branch).)


### Reproducible Example

```python
import modin.pandas as pd
from modin.pandas.api.extensions import (
    register_dataframe_groupby_accessor,
)
from functools import cached_property

@register_dataframe_groupby_accessor("groups")
@cached_property
def groups(self):
    return {0: pd.Index([0])}

pd.DataFrame([1]).groupby(0).groups()
```

### Issue Description

The above code raises `TypeError: Cannot use cached_property instance without calling __set_name__ on it.`

When `functools.cached_property` outside of a class definition, it seems to be unable to identify the class it should be bound to. Since `cached_property` uses the object's `__dict__` as a cache, it requires `__set_name__` to be properly called first.

This issue is similar to this SO post: https://stackoverflow.com/questions/62160411/pythons-new-functools-cached-property-bug-or-limitation

### Expected Behavior

Should not error.

### Error Logs

<details>

```python-traceback

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/joshi/code/modin/modin/pandas/groupby.py", line 279, in __getattribute__
    extensions_result = self._getattribute__from_extension_impl(
  File "/Users/joshi/code/modin/modin/core/storage_formats/pandas/query_compiler_caster.py", line 344, in _getattribute__from_extension_impl
    extension.__get__(self) if hasattr(extension, "__get__") else extension
  File "/Users/joshi/miniconda3/envs/modin-dev/lib/python3.10/functools.py", line 965, in __get__
    raise TypeError(
TypeError: Cannot use cached_property instance without calling __set_name__ on it.

```

</details>


### Installed Versions

<details>

INSTALLED VERSIONS
------------------
commit                : 5bc025e5303196b4bf7a0dc41da53d141969feae
python                : 3.10.13.final.0
python-bits           : 64
OS                    : Darwin
OS-release            : 24.5.0
Version               : Darwin Kernel Version 24.5.0: Tue Apr 22 19:54:25 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6020
machine               : arm64
processor             : arm
byteorder             : little
LC_ALL                : None
LANG                  : en_US.UTF-8
LOCALE                : en_US.UTF-8

Modin dependencies
------------------
modin                 : 0.32.0+67.g5bc025e5
ray                   : 2.34.0
dask                  : 2024.8.1
distributed           : 2024.8.1

pandas dependencies
-------------------
pandas                : 2.2.2
numpy                 : 1.26.4
pytz                  : 2023.3.post1
dateutil              : 2.8.2
setuptools            : 68.0.0
pip                   : 23.3
Cython                : None
pytest                : 8.3.2
hypothesis            : None
sphinx                : 5.3.0
blosc                 : None
feather               : None
xlsxwriter            : None
lxml.etree            : 5.3.0
html5lib              : None
pymysql               : None
psycopg2              : 2.9.9
jinja2                : 3.1.4
IPython               : 8.17.2
pandas_datareader     : None
adbc-driver-postgresql: None
adbc-driver-sqlite    : None
bs4                   : 4.12.2
bottleneck            : None
dataframe-api-compat  : None
fastparquet           : 2024.5.0
fsspec                : 2024.6.1
gcsfs                 : None
matplotlib            : 3.9.2
numba                 : None
numexpr               : 2.10.1
odfpy                 : None
openpyxl              : 3.1.5
pandas_gbq            : 0.23.1
pyarrow               : 17.0.0
pyreadstat            : None
python-calamine       : None
pyxlsb                : None
s3fs                  : 2024.6.1
scipy                 : 1.14.1
sqlalchemy            : 2.0.32
tables                : 3.10.1
tabulate              : 0.9.0
xarray                : 2024.7.0
xlrd                  : 2.0.1
zstandard             : None
tzdata                : 2023.3
qtpy                  : None
pyqt5                 : None
</details>


Pull request:
FIX-#7578: Change groupby extension allow list and fix cached_property extensions
## What do these changes do?

Fixes the linked issue by calling __set_name__ on extensions defined with the cached_property decorator, and loosens restrictions on methods that can be extended on `DataFrameGroupBy` and `SeriesGroupBy` objects.

- [x] first commit message and PR title follow format outlined [here](https://modin.readthedocs.io/en/latest/development/contributing.html#commit-message-formatting)
  > **_NOTE:_**  If you edit the PR title to match this format, you need to add another commit (even if it's empty) or amend your last commit for the CI job that checks the PR title to pick up the new PR title.
- [x] passes `flake8 modin/ asv_bench/benchmarks scripts/doc_checker.py`
- [x] passes `black --check modin/ asv_bench/benchmarks scripts/doc_checker.py`
- [x] signed commit with `git commit -s` <!-- you can amend your commit with a signature via `git commit -amend -s` -->
- [x] Resolves #7578 <!-- issue must be created for each patch -->
- [x] tests added and passing
- [x] module layout described at `docs/development/architecture.rst` is up-to-date <!-- if you have added, renamed or removed files or directories please update the documentation accordingly -->


Patch:
diff --git a/modin/pandas/api/extensions/extensions.py b/modin/pandas/api/extensions/extensions.py
index fa97f1c5..c8cfef5a 100644
--- a/modin/pandas/api/extensions/extensions.py
+++ b/modin/pandas/api/extensions/extensions.py
@@ -13,6 +13,7 @@
 
 import inspect
 from collections import defaultdict
+from functools import cached_property
 from types import MethodType, ModuleType
 from typing import Any, Dict, Optional, Union
 
@@ -94,6 +95,10 @@ def _set_attribute_on_obj(
             original_attr = getattr(pd, name)
             _reexport_classes[name] = original_attr
             delattr(pd, name)
+        # If the attribute is an instance of functools.cached_property, we must manually call __set_name__ on it.
+        # https://stackoverflow.com/a/62161136
+        if isinstance(new_attr, cached_property):
+            new_attr.__set_name__(obj, name)
         extensions[None if backend is None else Backend.normalize(backend)][
             name
         ] = new_attr
diff --git a/modin/pandas/groupby.py b/modin/pandas/groupby.py
index 456fbd41..975c8fd4 100644
--- a/modin/pandas/groupby.py
+++ b/modin/pandas/groupby.py
@@ -91,6 +91,21 @@ _DEFAULT_BEHAVIOUR = EXTENSION_NO_LOOKUP | {
     "_wrap_aggregation",
 }
 
+GROUPBY_EXTENSION_NO_LOOKUP = EXTENSION_NO_LOOKUP | {
+    "_axis",
+    "_idx_name",
+    "_df",
+    "_query_compiler",
+    "_columns",
+    "_by",
+    "_drop",
+    "_return_tuple_when_iterating",
+    "_is_multi_by",
+    "_level",
+    "_kwargs",
+    "_get_query_compiler",
+}
+
 
 @_inherit_docstrings(pandas.core.groupby.DataFrameGroupBy)
 class DataFrameGroupBy(ClassLogger, QueryCompilerCaster):  # noqa: GL08
@@ -98,10 +113,6 @@ class DataFrameGroupBy(ClassLogger, QueryCompilerCaster):  # noqa: GL08
     _return_tuple_when_iterating = False
     _df: Union[DataFrame, Series]
     _query_compiler: BaseQueryCompiler
-    # TODO(https://github.com/modin-project/modin/issues/7543):
-    # Currently this _extensions dict doesn't do anything, but we should
-    # add methods to register groupby accessors and make the groupby classes
-    # use this _extensions dict.
     _extensions: EXTENSION_DICT_TYPE = EXTENSION_DICT_TYPE(dict)
 
     def __init__(
@@ -248,7 +259,7 @@ class DataFrameGroupBy(ClassLogger, QueryCompilerCaster):  # noqa: GL08
         try:
             return self._getattr__from_extension_impl(
                 key=key,
-                default_behavior_attributes=_DEFAULT_BEHAVIOUR,
+                default_behavior_attributes=GROUPBY_EXTENSION_NO_LOOKUP,
                 extensions=__class__._extensions,
             )
         except AttributeError as err:
@@ -275,7 +286,7 @@ class DataFrameGroupBy(ClassLogger, QueryCompilerCaster):  # noqa: GL08
         Any
             The value of the attribute.
         """
-        if item not in _DEFAULT_BEHAVIOUR:
+        if item not in GROUPBY_EXTENSION_NO_LOOKUP:
             extensions_result = self._getattribute__from_extension_impl(
                 item, __class__._extensions
             )
@@ -1863,11 +1874,6 @@ class DataFrameGroupBy(ClassLogger, QueryCompilerCaster):  # noqa: GL08
 @_inherit_docstrings(pandas.core.groupby.SeriesGroupBy)
 class SeriesGroupBy(DataFrameGroupBy):  # noqa: GL08
     _pandas_class = pandas.core.groupby.SeriesGroupBy
-
-    # TODO(https://github.com/modin-project/modin/issues/7543):
-    # Currently this _extensions dict doesn't do anything, but we should
-    # add methods to register groupby accessors and make the groupby classes
-    # use this _extensions dict.
     _extensions: EXTENSION_DICT_TYPE = EXTENSION_DICT_TYPE(dict)
 
     @disable_logging
@@ -1888,7 +1894,7 @@ class SeriesGroupBy(DataFrameGroupBy):  # noqa: GL08
         Any
             The value of the attribute.
         """
-        if item not in _DEFAULT_BEHAVIOUR:
+        if item not in GROUPBY_EXTENSION_NO_LOOKUP:
             extensions_result = self._getattribute__from_extension_impl(
                 item, __class__._extensions
             )
@@ -1901,7 +1907,7 @@ class SeriesGroupBy(DataFrameGroupBy):  # noqa: GL08
     def __getattr__(self, key: str) -> Any:
         return self._getattr__from_extension_impl(
             key=key,
-            default_behavior_attributes=_DEFAULT_BEHAVIOUR,
+            default_behavior_attributes=GROUPBY_EXTENSION_NO_LOOKUP,
             extensions=__class__._extensions,
         )
 


Test patch:
diff --git a/modin/tests/pandas/extensions/test_groupby_extensions.py b/modin/tests/pandas/extensions/test_groupby_extensions.py
index 6eb3f87d..8be6c69c 100644
--- a/modin/tests/pandas/extensions/test_groupby_extensions.py
+++ b/modin/tests/pandas/extensions/test_groupby_extensions.py
@@ -11,6 +11,8 @@
 # ANY KIND, either express or implied. See the License for the specific language
 # governing permissions and limitations under the License.
 
+from functools import cached_property
+
 import pytest
 
 import modin.pandas as pd
@@ -241,6 +243,16 @@ class TestProperty:
         delattr(pandas_groupby, public_property_name)
         assert not hasattr(pandas_groupby, private_property_name)
 
+    @pytest.mark.filterwarnings(default_to_pandas_ignore_string)
+    def test_override_cached_property(self, get_groupby, register_accessor):
+        @cached_property
+        def groups(self):
+            return {"group": pd.Index(["test"])}
+
+        register_accessor("groups", backend="Pandas")(groups)
+        pandas_df = pd.DataFrame({"col0": [1], "col1": [2]}).move_to("pandas")
+        assert get_groupby(pandas_df).groups == {"group": pd.Index(["test"])}
+
 
 def test_deleting_extension_that_is_not_property_raises_attribute_error():
     expected_string_val = "Some string value"