Skip to content

MNT Use a pytest-like context manager in estimator_checks.py #18418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Sep 28, 2020

Conversation

NicolasHug
Copy link
Member

@NicolasHug NicolasHug commented Sep 17, 2020

This PR introduces a new raises context manager that works like pytest.raises but with a few tweaks that makes it suitable for the check_estimator suite (where pytest isn't allowed).

This allows to have a unified way of asserting exceptions in estimator_checks.py, instead of using assert_raises, assert_raises_regex, and a bunch of complextry/except statements.

It will also make #18415 more extensible and cleaner.

@NicolasHug NicolasHug changed the title [WIP] Use a pytest-like context manager in estimator_checks.py [MRG] MNT Use a pytest-like context manager in estimator_checks.py Sep 17, 2020
@NicolasHug
Copy link
Member Author

@rth @thomasjpfan I hope you guys will like this

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.

This is similar to from sklearn.utils._testing import assert_raises without the match feature:

with assert_raises(ValueError,
msg=msg.format(name, "predict")):
classifier.predict(X.T)

This is an alias to python's assertRaises

Are we able to use this instead?

return _Raises(expected_exp_type, match, may_pass, err_msg)


class _Raises(contextlib.AbstractContextManager):
Copy link
Member

Choose a reason for hiding this comment

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

Did you explore the contextlib.contextmanager implementation and found this to be cleaner?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't believe @contextlib.contextmanager allows to define the __exit__ function, which is the core of the CM for catching exceptions

Copy link
Member Author

Choose a reason for hiding this comment

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

For ref this is the pytest implem: https://github.com/pytest-dev/pytest/blob/master/src/_pytest/python_api.py#L702 (I kept it quite similar)

@NicolasHug
Copy link
Member Author

Are we able to use this instead?

No, this is different from assert_raises. The pattern matching is a major difference.

@thomasjpfan
Copy link
Member

Would assertRaisesRegex work for pattern matching?

@NicolasHug
Copy link
Member Author

Only partially. The point of this PR is to unify everything. You'll see in the diff that a lot of the code is now cleaner and simpler.

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.

When compared to assertRaisesRegex, I think this adds:

  1. Support for excepted_exp_type as a list.
  2. may_pass
  3. raised_and_matched attribute

Do we need 1 or 3 in check_estimators.py?

For may_pass, I was thinking of reusing assertRaisesRegex

  1. If may_pass=True and assertRaisesRegex raises an error than catch it and not raise anything
  2. If may_pass=False, then propagate the exception.

Overall, I am trying to think of ways to not re-implement features if we can.

@NicolasHug
Copy link
Member Author

When compared to assertRaisesRegex, I think this adds:

Most importantly, this adds:
4. consistency between the checks. This will be even more important when we start supporting strict_mode more pervasively.
5. ease of use: we already know how to use pytest.raises, now we have something similar that we can use in estimator_checks.py.

Do we need 1 or 3 in check_estimators.py?

Yes (please see the diff ;))

@NicolasHug
Copy link
Member Author

Overall, I am trying to think of ways to not re-implement features if we can.

This CM supports features that are not implemented anywhere. It will also make strict_mode adoption easier.

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.

Now that I see estimator_checks, yes this does look better.

Comment on lines +1845 to +1851
if cm.raised_and_matched:
# ValueError was raised with proper error message
return

assert_array_equal(
classifier.predict(X_test), y, err_msg=error_string_predict
)
Copy link
Member

Choose a reason for hiding this comment

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

This is the only place where we use raised_and_matched.

Do we have any classifier that actually raises an error with "Classifier can't predict predict when only one class is present"?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think so, because otherwise CodeCov would give us a warning here on the GitHub GUI.

Also note that the error isn't expected in predict but in fit.

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.

Codewise this LGTM

Documentation wise, I feel like its okay for now since we only use raises in estimator_checks.py

@rth rth self-requested a review September 26, 2020 06:50
@NicolasHug
Copy link
Member Author

Merge conflicts appeared because of #18415 but I think we should revert it (#18481)

Copy link
Member

@ogrisel ogrisel left a comment

Choose a reason for hiding this comment

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

Some comments, but besides LGTM.

@NicolasHug
Copy link
Member Author

Thanks @thomasjpfan and @ogrisel for the reviews, I addressed all comments! All green so I'll merge

@NicolasHug NicolasHug changed the title [MRG] MNT Use a pytest-like context manager in estimator_checks.py MNT Use a pytest-like context manager in estimator_checks.py Sep 28, 2020
@NicolasHug NicolasHug merged commit 5721629 into scikit-learn:master Sep 28, 2020
@NicolasHug NicolasHug deleted the cm_testing branch September 28, 2020 19:05
amrcode pushed a commit to amrcode/scikit-learn that referenced this pull request Oct 19, 2020
jayzed82 pushed a commit to jayzed82/scikit-learn that referenced this pull request Oct 22, 2020
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.

3 participants