Repository: scikit-learn/scikit-learn
File: examples/miscellaneous/plot_metadata_routing.py (Python)

Code before:
        router = MetadataRouter(owner=self).add(
            estimator=self.estimator,
            method_mapping=MethodMapping()
            .add(caller="fit", callee="fit")
            .add(caller="predict", callee="predict")
            .add(caller="score", callee="score"),
        )
        return router

    def fit(self, X, y, **fit_params):
        # Get information on all the metadata that should be routed from here to
        # consuming methods.
        routed_params = process_routing(self, "fit", **fit_params)
        # A sub-estimator is fitted and its classes are attributed to the
        # meta-estimator.
        self.estimator_ = clone(self.estimator).fit(X, y, **routed_params.estimator.fit)
        self.classes_ = self.estimator_.classes_
        return self

    def predict(self, X, **predict_params):
        check_is_fitted(self)
        # As in `fit`, we get information on all the metadata that should be routed.
        routed_params = process_routing(self, "predict", **predict_params)
        return self.estimator_.predict(X, **routed_params.estimator.predict)


# %%
# Next, we illustrate the different behaviors and notably the type of errors
# raised.

meta_est = MetaClassifier(
    estimator=ExampleClassifier().set_fit_request(sample_weight=True)
)
meta_est.fit(X, y, sample_weight=my_weights)

# %%
# Note that the above example is calling our utility function
# `check_metadata()` via the `ExampleClassifier`. It checks that
# ``sample_weight`` is correctly passed to it. If it is not, like in the
# following example, it would print that ``sample_weight`` is ``None``:

meta_est.fit(X, y)

# %%
# If we pass an unknown metadata, an error is raised:
try:
    meta_est.fit(X, y, test=my_weights)
except TypeError as e:
    print(e)

# %%

Review comment (suggestion):
I agree that directly using `process_routing` makes the developer guide easier to follow. However we should at least introduce `process_routing` and explains what it does, see below.

Code after:
# the metadata.
#
# To make the meta-estimator a router, you only need to:
#
# - define its `get_metadata_routing` method, which returns a `MetadataRouter`
#   instance in charge of configuring the metadata routing.
# - use `process_routing` inside its methods (`fit`, `predict`, ...) to  properly
#   route the metadata from the meta-estimator to its sub-estimator.


class MetaClassifier(MetaEstimatorMixin, ClassifierMixin, BaseEstimator):
    def __init__(self, estimator):
        self.estimator = estimator

    def get_metadata_routing(self):
        # This method defines the routing for this meta-estimator.
        # In order to do so, a `MetadataRouter` instance is created, and the
        # routing is added to it.
        router = MetadataRouter(owner=self).add(
            estimator=self.estimator,
            method_mapping=MethodMapping()
            .add(caller="fit", callee="fit")
            .add(caller="predict", callee="predict")
            .add(caller="score", callee="score"),
        )
        return router

    def fit(self, X, y, **fit_params):
        # Get information on all the metadata that should be routed from here to
        # consuming methods.
        routed_params = process_routing(self, "fit", **fit_params)
        # A sub-estimator is fitted and its classes are attributed to the
        # meta-estimator. Since we call the sub-estimator's fit method, we pass the
        # the metadata stored in `routed_params.estimator.fit`.
        self.estimator_ = clone(self.estimator).fit(X, y, **routed_params.estimator.fit)
        self.classes_ = self.estimator_.classes_
        return self

    def predict(self, X, **predict_params):
        check_is_fitted(self)
        # As in `fit`, we get information on all the metadata that should be routed and
        # pass the metadata that is stored in `routed_params.estimator.predict` to the
        # sub-estimator's predict method.
        routed_params = process_routing(self, "predict", **predict_params)
        return self.estimator_.predict(X, **routed_params.estimator.predict)


# %%
# Let's break down different parts of the above code.
#
# In each method, we use the ``process_routing`` function to construct a