-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
python: collect: ignore exceptions with isinstance #4284
Conversation
src/_pytest/python.py
Outdated
@@ -195,7 +195,11 @@ def pytest_pycollect_makeitem(collector, name, obj): | |||
if res is not None: | |||
return | |||
# nothing was collected elsewhere, let's do it here | |||
if isclass(obj): | |||
try: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if this was factored into a function called _hardened_isclass
the test could be a unittest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've factored it out and created a unit test, but kept the integration test, since it not only tests if _safe_isclass
is really used during collection, but would also show other issues in the future with Django's settings.
Codecov Report
@@ Coverage Diff @@
## master #4284 +/- ##
==========================================
+ Coverage 95.65% 95.78% +0.13%
==========================================
Files 109 109
Lines 24630 24666 +36
Branches 2396 2397 +1
==========================================
+ Hits 23560 23627 +67
+ Misses 759 738 -21
+ Partials 311 301 -10
Continue to review full report at Codecov.
|
src/_pytest/python.py
Outdated
try: | ||
return isclass(obj) | ||
except Exception: | ||
return False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this live in pytest._compat
, that way other uses of isclass
get the same benefits?
seems to be also used in these places:
$ git grep compat.*isclass
src/_pytest/fixtures.py:from _pytest.compat import isclass
src/_pytest/python.py:from _pytest.compat import isclass
src/_pytest/python_api.py:from _pytest.compat import isclass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've thought it could be moved when needed.
Currently there are several places that handle things safely already - i.e. with the test case for django.conf.settings I've seen it coming to the "raise" at least once before.
Therefore I've thought to keep it near to the single user for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I.e. I do not think we should wrap isclass/isinstance
unnecessarily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤷♂️ it's triggerable in raises
at least:
import pytest
class CrappyClass(Exception):
@property
def __class__(self):
assert False, 'nuuuu'
with pytest.raises(CrappyClass()):
pass
Traceback (most recent call last):
File "t.py", line 9, in <module>
with pytest.raises(CrappyClass()):
File "/tmp/pytest/venv/lib/python3.6/site-packages/_pytest/python_api.py", line 654, in raises
for exc in filterfalse(isclass, always_iterable(expected_exception, BASE_TYPE)):
File "/tmp/pytest/venv/lib/python3.6/site-packages/more_itertools/more.py", line 1305, in always_iterable
if (base_type is not None) and isinstance(obj, base_type):
File "t.py", line 6, in __class__
assert False, 'nuuuu'
AssertionError: nuuuu
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, might be good to see this for raises
after all?! Not sure.
It's not as bad as with collection at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll move it to compat and use it for your test case, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, it fails there through always_iterable
already, which does:
if (base_type is not None) and isinstance(obj, base_type):
Fixing this to use for exc in filterfalse(safe_isclass, always_iterable(expected_exception, base_type=None)):
(base_type=None
) then makes it fail with:
TypeError: exceptions must be old-style classes or derived from BaseException, not <class 'test_compat.test_safe_getclass..CrappyClass'>
(beause it returns False
)
Let's stick to "Arbitrary exceptions should not be silenced." here at least.
this seemed odd enough so I made a bpo issue for it: https://bugs.python.org/issue35137 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
either way, this seems good -- thanks for this 🎉
b2042be
to
c9adfd1
Compare
I've reworked it a bit and added tests for raises. |
c9adfd1
to
e30f709
Compare
Amended a fixup: diff --git a/testing/python/raises.py b/testing/python/raises.py
index 926a2d06..a72aeef6 100644
--- a/testing/python/raises.py
+++ b/testing/python/raises.py
@@ -181,9 +181,6 @@ class CrappyClass(Exception):
def __class__(self):
assert False, "via __class__"
- def __call__(self):
- return CrappyClass
-
if six.PY2:
with pytest.raises(pytest.fail.Exception) as excinfo:
with pytest.raises(CrappyClass()): |
Fixes #4266.