Skip to content

Commit

Permalink
Ignore errors raised from descriptors when collecting fixtures
Browse files Browse the repository at this point in the history
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 pytest-dev#2234.
  • Loading branch information
bluetech committed Feb 7, 2017
1 parent 87fb689 commit 3a0a0c2
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 1 deletion.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Piotr Banaszkiewicz
Punyashloka Biswal
Quentin Pradet
Ralf Schmitt
Ran Benita
Raphael Pierzina
Raquel Alegre
Roberto Polli
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

*

* Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_).
Thanks to `@bluetech`_.

* Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_).
Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR.

Expand All @@ -16,6 +19,10 @@

*

.. _#2234: https://github.com/pytest-dev/pytest/issues/2234

.. _@bluetech: https://github.com/bluetech

.. _#2160: https://github.com/pytest-dev/pytest/issues/2160

.. _PEP-479: https://www.python.org/dev/peps/pep-0479/
Expand Down
5 changes: 4 additions & 1 deletion _pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
getfslineno, get_real_func,
is_generator, isclass, getimfunc,
getlocation, getfuncargnames,
safe_getattr,
)

def pytest_sessionstart(session):
Expand Down Expand Up @@ -1066,7 +1067,9 @@ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
self._holderobjseen.add(holderobj)
autousenames = []
for name in dir(holderobj):
obj = getattr(holderobj, name, None)
# The attribute can be an arbitrary descriptor, so the attribute
# access below can raise. safe_getatt() ignores such exceptions.
obj = safe_getattr(holderobj, name, None)
# fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
# or are "@pytest.fixture" marked
marker = getfixturemarker(obj)
Expand Down
10 changes: 10 additions & 0 deletions testing/python/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,16 @@ def test_issue1579_namedtuple(self, testdir):
"because it has a __new__ constructor*"
)

def test_issue2234_property(self, testdir):
testdir.makepyfile("""
class TestCase(object):
@property
def prop(self):
raise NotImplementedError()
""")
result = testdir.runpytest()
assert result.ret == EXIT_NOTESTSCOLLECTED


class TestGenerator:
def test_generative_functions(self, testdir):
Expand Down

0 comments on commit 3a0a0c2

Please sign in to comment.