Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@adrinjalali
Copy link
Member

Fixes #28430
Closes #28431

This fixes an issue where previously we were going through the MRO, sorting values, then writing and overwriting defaults. Now we create the request values as we go through MRO, which avoids previous issues.

cc @YanisLalou, @thomasjpfan @glemaitre @OmarManzoor

@github-actions
Copy link

github-actions bot commented Feb 16, 2024

✔️ Linting Passed

All linting checks passed. Your pull request is in excellent shape! ☀️

Generated for commit: b82a397. Link to the linter CI: here

@adrinjalali adrinjalali added this to the 1.4.2 milestone Feb 16, 2024
@YanisLalou
Copy link

YanisLalou commented Feb 16, 2024

Not sure if it's linked or not, but I observed another kind of strange behaviour with class inheritance.
Here's an example:

from sklearn.base import BaseEstimator
from sklearn.utils import metadata_routing

class Base_1(BaseEstimator):
	__metadata_request__split = {"groups": metadata_routing.UNUSED}

class Class_1(Base_1):
	__metadata_request__split = {"groups": "sample_domain"}

print(Class_1()._get_metadata_request())

Expected result:

{"split": {"groups": "sample_domain"}}

Actual result:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/yanislalou/Documents/CMAP/scikit-learn/sklearn/utils/_metadata_requests.py", line 1459, in _get_metadata_request
    requests = self._get_default_requests()
  File "/Users/yanislalou/Documents/CMAP/scikit-learn/sklearn/utils/_metadata_requests.py", line 1441, in _get_default_requests
    getattr(requests, method).add_request(param=prop, alias=alias)
  File "/Users/yanislalou/Documents/CMAP/scikit-learn/sklearn/utils/_metadata_requests.py", line 350, in add_request
    raise ValueError(
ValueError: Trying to remove parameter groups with UNUSED which doesn't exist.

@adrinjalali
Copy link
Member Author

adrinjalali commented Feb 16, 2024

class Base_1(BaseEstimator):
	__metadata_request__split = {"groups": metadata_routing.UNUSED}

here you're trying to remove groups which doesn't exist in BaseEstimator.split. So the exception is the expected behavior.

@YanisLalou
Copy link

YanisLalou commented Feb 16, 2024

class Base_1(BaseEstimator):
	__metadata_request__split = {"groups": metadata_routing.UNUSED}

here you're trying to remove groups which doesn't exist in BaseEstimator.split. So the exception is the expected behavior.

Makes sense 👍

@glemaitre glemaitre self-requested a review February 16, 2024 17:35
Copy link
Member

@thomasjpfan thomasjpfan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to dislike walking through the MRO, but in this case the alternative is not great either:

diff --git a/sklearn/utils/_metadata_requests.py b/sklearn/utils/_metadata_requests.py
index a914addc7f..7cd0b12a0e 100644
--- a/sklearn/utils/_metadata_requests.py
+++ b/sklearn/utils/_metadata_requests.py
@@ -1403,6 +1403,24 @@ def _build_request_for_signature(cls, router, method):
             )
         return mmr
 
+    @classmethod
+    def _configure_metadata_request(cls, requests):
+        from contextlib import suppress
+
+        with suppress(AttributeError):
+            requests = super()._configure_metadata_request(requests)
+
+        substr = "__metadata_request__"
+        for attr, value in vars(cls).items():
+            if substr not in attr:
+                continue
+
+            method = attr[attr.index(substr) + len(substr) :]
+            for prop, alias in value.items():
+                getattr(requests, method).add_request(param=prop, alias=alias)
+
+        return requests
+
     @classmethod
     def _get_default_requests(cls):
         """Collect default request values.
@@ -1424,28 +1442,7 @@ class attributes, as well as determining request keys from method
         # __metadata_request__* attributes. Defaults set in
         # __metadata_request__* attributes take precedence over signature
         # sniffing.
-
-        # need to go through the MRO since this is a class attribute and
-        # ``vars`` doesn't report the parent class attributes. We go through
-        # the reverse of the MRO so that child classes have precedence over
-        # their parents.
-        substr = "__metadata_request__"
-        for base_class in reversed(inspect.getmro(cls)):
-            for attr, value in vars(base_class).items():
-                if substr not in attr:
-                    continue
-                # we don't check for attr.startswith() since python prefixes attrs
-                # starting with __ with the `_ClassName`.
-                method = attr[attr.index(substr) + len(substr) :]
-                for prop, alias in value.items():
-                    # here we "add" request values specified via those class attributes
-                    # to the `MetadataRequest` object. Adding a request which already
-                    # exists will override the previous one. Since we go through the
-                    # MRO in reverse order, the one specified by the lowest most classes
-                    # in the inheritance tree are the ones which take effect.
-                    getattr(requests, method).add_request(param=prop, alias=alias)
-
-        return requests
+        return cls._configure_metadata_request(requests)
 
     def _get_metadata_request(self):
         """Get requested data properties.

I am okay with the current approach in this PR.

Copy link
Member

@glemaitre glemaitre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@glemaitre glemaitre enabled auto-merge (squash) February 19, 2024 10:25
@glemaitre
Copy link
Member

Enable auto-merge.

@glemaitre glemaitre merged commit 6e53035 into scikit-learn:main Feb 19, 2024
@adrinjalali adrinjalali deleted the array-api/simple branch February 19, 2024 11:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Name of a class impacts the value of its __metadata_request__* variables

4 participants