fix a pypy related regression - re-allow self.NAME style collection tree customization

This commit is contained in:
holger krekel 2011-01-18 12:47:31 +01:00
parent aea4d1bd7a
commit b8f0d10f80
3 changed files with 61 additions and 13 deletions

View File

@ -152,6 +152,7 @@ class Node(object):
Module = compatproperty("Module") Module = compatproperty("Module")
Class = compatproperty("Class") Class = compatproperty("Class")
Instance = compatproperty("Instance")
Function = compatproperty("Function") Function = compatproperty("Function")
File = compatproperty("File") File = compatproperty("File")
Item = compatproperty("Item") Item = compatproperty("Item")

View File

@ -73,7 +73,7 @@ def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
if collector._istestclasscandidate(name, obj): if collector._istestclasscandidate(name, obj):
#if hasattr(collector.obj, 'unittest'): #if hasattr(collector.obj, 'unittest'):
# return # we assume it's a mixin class for a TestCase derived one # return # we assume it's a mixin class for a TestCase derived one
return Class(name, parent=collector) return collector.Class(name, parent=collector)
elif collector.funcnamefilter(name) and hasattr(obj, '__call__'): elif collector.funcnamefilter(name) and hasattr(obj, '__call__'):
if is_generator(obj): if is_generator(obj):
return Generator(name, parent=collector) return Generator(name, parent=collector)
@ -160,7 +160,7 @@ class PyCollectorMixin(PyobjMixin, pytest.Collector):
for prefix in self.config.getini("python_functions"): for prefix in self.config.getini("python_functions"):
if name.startswith(prefix): if name.startswith(prefix):
return True return True
def classnamefilter(self, name): def classnamefilter(self, name):
for prefix in self.config.getini("python_classes"): for prefix in self.config.getini("python_classes"):
if name.startswith(prefix): if name.startswith(prefix):
@ -214,11 +214,11 @@ class PyCollectorMixin(PyobjMixin, pytest.Collector):
plugins = self.getplugins() + extra plugins = self.getplugins() + extra
gentesthook.pcall(plugins, metafunc=metafunc) gentesthook.pcall(plugins, metafunc=metafunc)
if not metafunc._calls: if not metafunc._calls:
return Function(name, parent=self) return self.Function(name, parent=self)
l = [] l = []
for callspec in metafunc._calls: for callspec in metafunc._calls:
subname = "%s[%s]" %(name, callspec.id) subname = "%s[%s]" %(name, callspec.id)
function = Function(name=subname, parent=self, function = self.Function(name=subname, parent=self,
callspec=callspec, callobj=funcobj, keywords={callspec.id:True}) callspec=callspec, callobj=funcobj, keywords={callspec.id:True})
l.append(function) l.append(function)
return l return l
@ -272,7 +272,7 @@ class Module(pytest.File, PyCollectorMixin):
class Class(PyCollectorMixin, pytest.Collector): class Class(PyCollectorMixin, pytest.Collector):
def collect(self): def collect(self):
return [Instance(name="()", parent=self)] return [self.Instance(name="()", parent=self)]
def setup(self): def setup(self):
setup_class = getattr(self.obj, 'setup_class', None) setup_class = getattr(self.obj, 'setup_class', None)
@ -394,7 +394,7 @@ class Generator(FunctionMixin, PyCollectorMixin, pytest.Collector):
if name in seen: if name in seen:
raise ValueError("%r generated tests with non-unique name %r" %(self, name)) raise ValueError("%r generated tests with non-unique name %r" %(self, name))
seen[name] = True seen[name] = True
l.append(Function(name, self, args=args, callobj=call)) l.append(self.Function(name, self, args=args, callobj=call))
return l return l
def getcallargs(self, obj): def getcallargs(self, obj):
@ -528,10 +528,10 @@ class Metafunc:
def addcall(self, funcargs=None, id=_notexists, param=_notexists): def addcall(self, funcargs=None, id=_notexists, param=_notexists):
""" add a new call to the underlying test function during the """ add a new call to the underlying test function during the
collection phase of a test run. collection phase of a test run.
:arg funcargs: argument keyword dictionary used when invoking :arg funcargs: argument keyword dictionary used when invoking
the test function. the test function.
:arg id: used for reporting and identification purposes. If you :arg id: used for reporting and identification purposes. If you
don't supply an `id` the length of the currently don't supply an `id` the length of the currently
list of calls to the test function will be used. list of calls to the test function will be used.
@ -562,7 +562,7 @@ class FuncargRequest:
class LookupError(LookupError): class LookupError(LookupError):
""" error on performing funcarg request. """ """ error on performing funcarg request. """
def __init__(self, pyfuncitem): def __init__(self, pyfuncitem):
self._pyfuncitem = pyfuncitem self._pyfuncitem = pyfuncitem
if hasattr(pyfuncitem, '_requestparam'): if hasattr(pyfuncitem, '_requestparam'):
@ -590,7 +590,7 @@ class FuncargRequest:
def module(self): def module(self):
""" module where the test function was collected. """ """ module where the test function was collected. """
return self._pyfuncitem.getparent(pytest.Module).obj return self._pyfuncitem.getparent(pytest.Module).obj
@property @property
def cls(self): def cls(self):
""" class (can be None) where the test function was collected. """ """ class (can be None) where the test function was collected. """
@ -606,7 +606,7 @@ class FuncargRequest:
def config(self): def config(self):
""" the pytest config object associated with this request. """ """ the pytest config object associated with this request. """
return self._pyfuncitem.config return self._pyfuncitem.config
@property @property
def fspath(self): def fspath(self):
""" the file system path of the test module which collected this test. """ """ the file system path of the test module which collected this test. """
@ -780,7 +780,7 @@ def getlocation(function, curdir):
def raises(ExpectedException, *args, **kwargs): def raises(ExpectedException, *args, **kwargs):
""" assert that a code block/function call raises @ExpectedException """ assert that a code block/function call raises @ExpectedException
and raise a failure exception otherwise. and raise a failure exception otherwise.
If using Python 2.5 or above, you may use this function as a If using Python 2.5 or above, you may use this function as a
context manager:: context manager::
@ -803,7 +803,7 @@ def raises(ExpectedException, *args, **kwargs):
A third possibility is to use a string which which will A third possibility is to use a string which which will
be executed:: be executed::
>>> raises(ZeroDivisionError, "f(0)") >>> raises(ZeroDivisionError, "f(0)")
<ExceptionInfo ...> <ExceptionInfo ...>
""" """
@ -852,3 +852,4 @@ class RaisesContext(object):
pytest.fail("DID NOT RAISE") pytest.fail("DID NOT RAISE")
self.excinfo.__init__(tp) self.excinfo.__init__(tp)
return issubclass(self.excinfo.type, self.ExpectedException) return issubclass(self.excinfo.type, self.ExpectedException)

View File

@ -1280,3 +1280,49 @@ def test_customized_python_discovery(testdir):
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
"*2 passed*", "*2 passed*",
]) ])
def test_collector_attributes(testdir):
testdir.makeconftest("""
import pytest
def pytest_pycollect_makeitem(collector):
assert collector.Function == pytest.Function
assert collector.Class == pytest.Class
assert collector.Instance == pytest.Instance
assert collector.Module == pytest.Module
""")
testdir.makepyfile("""
def test_hello():
pass
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines([
"*1 passed*",
])
def test_customize_through_attributes(testdir):
testdir.makeconftest("""
import pytest
class MyFunction(pytest.Function):
pass
class MyInstance(pytest.Instance):
Function = MyFunction
class MyClass(pytest.Class):
Instance = MyInstance
def pytest_pycollect_makeitem(collector, name, obj):
if name.startswith("MyTestClass"):
return MyClass(name, parent=collector)
""")
testdir.makepyfile("""
class MyTestClass:
def test_hello(self):
pass
""")
result = testdir.runpytest("--collectonly")
result.stdout.fnmatch_lines([
"*MyClass*",
"*MyInstance*",
"*MyFunction*test_hello*",
])