Don't collect classes with truthy __getattr__.

When we have a metaclass which returns something truthy (like a method) in its
__getattr__, we collected the class because pytest thought its __test__
attribute was set to True.

We can work around this to some degree by assuming __test__ will always be set
to an explicit True if that's what the user has intended, and if it's something
other than that, this is probably a mistake.

Fixes #1204.
This commit is contained in:
Florian Bruhin 2015-11-30 16:41:13 +01:00
parent bced5a3f81
commit ba9146c131
2 changed files with 33 additions and 1 deletions

View File

@ -405,7 +405,10 @@ class PyCollector(PyobjMixin, pytest.Collector):
""" Look for the __test__ attribute, which is applied by the
@nose.tools.istest decorator
"""
return safe_getattr(obj, '__test__', False)
# We explicitly check for "is True" here to not mistakenly treat
# classes with a custom __getattr__ returning something truthy (like a
# function) as test classes.
return safe_getattr(obj, '__test__', False) is True
def classnamefilter(self, name):
return self._matches_prefix_or_glob_option('python_classes', name)

View File

@ -283,6 +283,35 @@ class TestNoselikeTestAttribute:
assert len(call.items) == 1
assert call.items[0].cls.__name__ == "TC"
def test_class_with_nasty_getattr(self, testdir):
"""Make sure we handle classes with a custom nasty __getattr__ right.
With a custom __getattr__ which e.g. returns a function (like with a
RPC wrapper), we shouldn't assume this meant "__test__ = True".
"""
# https://github.com/pytest-dev/pytest/issues/1204
testdir.makepyfile("""
class MetaModel(type):
def __getattr__(cls, key):
return lambda: None
BaseModel = MetaModel('Model', (), {})
class Model(BaseModel):
__metaclass__ = MetaModel
def test_blah(self):
pass
""")
reprec = testdir.inline_run()
assert not reprec.getfailedcollections()
call = reprec.getcalls("pytest_collection_modifyitems")[0]
assert not call.items
@pytest.mark.issue351
class TestParameterize: