Skip to content

Commit 3a0a0c2

Browse files
committed
Ignore errors raised from descriptors when collecting fixtures
Descriptors (e.g. properties) such as in the added test case are triggered during collection, executing arbitrary code which can raise. Previously, such exceptions were propagated and failed the collection. Now these exceptions are caught and the corresponding attributes are silently ignored. A better solution would be to completely skip access to all custom descriptors, such that the offending code doesn't even trigger. However I think this requires manually going through the instance and all of its MRO for each and every attribute checking if it might be a proper fixture before accessing it. So I took the easy route here. In other words, putting something like this in your test class is still a bad idea...: @Property def innocent(self): os.system('rm -rf /') Fixes #2234.
1 parent 87fb689 commit 3a0a0c2

File tree

4 files changed

+22
-1
lines changed

4 files changed

+22
-1
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ Piotr Banaszkiewicz
118118
Punyashloka Biswal
119119
Quentin Pradet
120120
Ralf Schmitt
121+
Ran Benita
121122
Raphael Pierzina
122123
Raquel Alegre
123124
Roberto Polli

CHANGELOG.rst

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
*
55

6+
* Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_).
7+
Thanks to `@bluetech`_.
8+
69
* Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_).
710
Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR.
811

@@ -16,6 +19,10 @@
1619

1720
*
1821

22+
.. _#2234: https://github.com/pytest-dev/pytest/issues/2234
23+
24+
.. _@bluetech: https://github.com/bluetech
25+
1926
.. _#2160: https://github.com/pytest-dev/pytest/issues/2160
2027

2128
.. _PEP-479: https://www.python.org/dev/peps/pep-0479/

_pytest/fixtures.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
getfslineno, get_real_func,
1515
is_generator, isclass, getimfunc,
1616
getlocation, getfuncargnames,
17+
safe_getattr,
1718
)
1819

1920
def pytest_sessionstart(session):
@@ -1066,7 +1067,9 @@ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
10661067
self._holderobjseen.add(holderobj)
10671068
autousenames = []
10681069
for name in dir(holderobj):
1069-
obj = getattr(holderobj, name, None)
1070+
# The attribute can be an arbitrary descriptor, so the attribute
1071+
# access below can raise. safe_getatt() ignores such exceptions.
1072+
obj = safe_getattr(holderobj, name, None)
10701073
# fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
10711074
# or are "@pytest.fixture" marked
10721075
marker = getfixturemarker(obj)

testing/python/collect.py

+10
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,16 @@ def test_issue1579_namedtuple(self, testdir):
166166
"because it has a __new__ constructor*"
167167
)
168168

169+
def test_issue2234_property(self, testdir):
170+
testdir.makepyfile("""
171+
class TestCase(object):
172+
@property
173+
def prop(self):
174+
raise NotImplementedError()
175+
""")
176+
result = testdir.runpytest()
177+
assert result.ret == EXIT_NOTESTSCOLLECTED
178+
169179

170180
class TestGenerator:
171181
def test_generative_functions(self, testdir):

0 commit comments

Comments
 (0)