From 0ceb3dd811a71dd448cbaddf2944b8f7839b7e49 Mon Sep 17 00:00:00 2001 From: TemidayoA Date: Sun, 19 Jul 2026 12:13:30 +0100 Subject: [PATCH 1/3] Extend non-fitted error check to cover inverse_transform The shared estimator check only verified that transform() raises NotFittedError before fit. Every transformer that implements inverse_transform already guards it, but nothing tested that, so a regression would go unnoticed. The check now also calls inverse_transform on an unfitted clone, accepting NotImplementedError for transformers that deliberately do not support inversion. Related to #420 --- tests/estimator_checks/non_fitted_error_checks.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/estimator_checks/non_fitted_error_checks.py b/tests/estimator_checks/non_fitted_error_checks.py index b753484b7..9215bb396 100644 --- a/tests/estimator_checks/non_fitted_error_checks.py +++ b/tests/estimator_checks/non_fitted_error_checks.py @@ -9,13 +9,22 @@ def check_raises_non_fitted_error(estimator): """ - Check if transformer raises error when transform() method is called before - calling fit() method. + Check if transformer raises error when transform() or inverse_transform() + methods are called before calling fit() method. The functionality is provided by sklearn's `check_is_fitted` function. + + Transformers that do not support inverse_transform raise NotImplementedError + instead, regardless of whether the transformer was fitted. """ X, y = test_df() transformer = clone(estimator) # Test when fit is not called prior to transform. with pytest.raises(NotFittedError): transformer.transform(X) + + # Test when fit is not called prior to inverse_transform. + transformer = clone(estimator) + if hasattr(transformer, "inverse_transform"): + with pytest.raises((NotFittedError, NotImplementedError)): + transformer.inverse_transform(X) From 3da6997fba139b9fc233f021460da667ea4ad851 Mon Sep 17 00:00:00 2001 From: TemidayoA Date: Sun, 19 Jul 2026 12:30:45 +0100 Subject: [PATCH 2/3] Retrigger CI From 9e26ea0ccaf8d21d5da1c51264b7fd64d9af0e43 Mon Sep 17 00:00:00 2001 From: TemidayoA Date: Sun, 19 Jul 2026 17:08:20 +0100 Subject: [PATCH 3/3] Assert the specific error inverse_transform raises before fit Address review: rather than accepting either NotFittedError or NotImplementedError, determine per transformer which one is expected and assert exactly that, so a transformer whose inverse_transform stops guarding fit is no longer hidden. A transformer's inverse_transform is treated as not implemented when a fitted clone raises NotImplementedError; those are expected to raise NotImplementedError before fit, all others NotFittedError. The classification fits on a dataframe with categorical and datetime features so every transformer reaching this check can be fitted. --- .../non_fitted_error_checks.py | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/tests/estimator_checks/non_fitted_error_checks.py b/tests/estimator_checks/non_fitted_error_checks.py index 9215bb396..e15553b79 100644 --- a/tests/estimator_checks/non_fitted_error_checks.py +++ b/tests/estimator_checks/non_fitted_error_checks.py @@ -12,19 +12,58 @@ def check_raises_non_fitted_error(estimator): Check if transformer raises error when transform() or inverse_transform() methods are called before calling fit() method. - The functionality is provided by sklearn's `check_is_fitted` function. + For transform(), and for inverse_transform() when the transformer implements + it, the expected error is NotFittedError, provided by sklearn's + `check_is_fitted` function. - Transformers that do not support inverse_transform raise NotImplementedError - instead, regardless of whether the transformer was fitted. + A few transformers expose inverse_transform() but do not implement it (for + example, OneHotEncoder, RareLabelEncoder and StringSimilarityEncoder). Those + raise NotImplementedError instead, whether or not fit() was called. We assert + the specific error each transformer is expected to raise, rather than + accepting either, so that a transformer that stops guarding fit() is caught. """ X, y = test_df() - transformer = clone(estimator) + # Test when fit is not called prior to transform. + transformer = clone(estimator) with pytest.raises(NotFittedError): transformer.transform(X) # Test when fit is not called prior to inverse_transform. - transformer = clone(estimator) - if hasattr(transformer, "inverse_transform"): - with pytest.raises((NotFittedError, NotImplementedError)): + if hasattr(estimator, "inverse_transform"): + expected_error = ( + NotFittedError + if _implements_inverse_transform(estimator) + else NotImplementedError + ) + transformer = clone(estimator) + with pytest.raises(expected_error): transformer.inverse_transform(X) + + +def _implements_inverse_transform(estimator): + """Whether the transformer actually implements inverse_transform. + + Transformers that expose the method but do not support the inversion raise + NotImplementedError. We detect this on a fitted clone: if inverse_transform + raises NotImplementedError once fitted, the method is not implemented; any + other outcome means it is implemented (and should therefore raise + NotFittedError when called before fit). + + Fitting uses a dataframe with categorical and datetime features so that every + transformer that reaches this check can be fitted, matching the input used by + the other shared checks. + """ + X, y = test_df(categorical=True, datetime=True) + transformer = clone(estimator) + transformer.fit(X, y) + try: + transformer.inverse_transform(X) + except NotImplementedError: + return False + except Exception: + # inverse_transform is implemented but raised for an unrelated reason + # (e.g. the raw X is not valid transformed input); it is not "not + # implemented", which is all this check needs to establish. + return True + return True