add a way to mark hooks as "tryfirst" or "trylast" to influence its position in a hook chain.

Use 'tryfirst' for capturing hooks so they can start capturing as early as possible,
including when conftests add output in runtest_setup hooks.
This commit is contained in:
holger krekel 2010-11-21 23:17:59 +01:00
parent f456e376b9
commit bc42cf8ffb
8 changed files with 62 additions and 9 deletions

View File

@ -1,6 +1,6 @@
""" per-test stdout/stderr capturing mechanisms, ``capsys`` and ``capfd`` function arguments. """ """ per-test stdout/stderr capturing mechanisms, ``capsys`` and ``capfd`` function arguments. """
import py import pytest, py
import os import os
def pytest_addoption(parser): def pytest_addoption(parser):
@ -143,13 +143,16 @@ class CaptureManager:
addouterr(rep, outerr) addouterr(rep, outerr)
return rep return rep
@pytest.mark.tryfirst
def pytest_runtest_setup(self, item): def pytest_runtest_setup(self, item):
self.resumecapture_item(item) self.resumecapture_item(item)
@pytest.mark.tryfirst
def pytest_runtest_call(self, item): def pytest_runtest_call(self, item):
self.resumecapture_item(item) self.resumecapture_item(item)
self.activate_funcargs(item) self.activate_funcargs(item)
@pytest.mark.tryfirst
def pytest_runtest_teardown(self, item): def pytest_runtest_teardown(self, item):
self.resumecapture_item(item) self.resumecapture_item(item)
@ -168,6 +171,7 @@ class CaptureManager:
if hasattr(self, '_capturing'): if hasattr(self, '_capturing'):
self.suspendcapture() self.suspendcapture()
@pytest.mark.tryfirst
def pytest_runtest_makereport(self, __multicall__, item, call): def pytest_runtest_makereport(self, __multicall__, item, call):
self.deactivate_funcargs() self.deactivate_funcargs()
rep = __multicall__.execute() rep = __multicall__.execute()

View File

@ -12,7 +12,7 @@ assert py.__version__.split(".")[:2] >= ['2', '0'], ("installation problem: "
"%s is too old, remove or upgrade 'py'" % (py.__version__)) "%s is too old, remove or upgrade 'py'" % (py.__version__))
default_plugins = ( default_plugins = (
"config session terminal runner python pdb capture unittest mark skipping " "config mark session terminal runner python pdb unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript " "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
"junitxml doctest").split() "junitxml doctest").split()
@ -272,11 +272,19 @@ class PluginManager(object):
if plugins is None: if plugins is None:
plugins = self._plugins plugins = self._plugins
l = [] l = []
last = []
for plugin in plugins: for plugin in plugins:
try: try:
l.append(getattr(plugin, attrname)) meth = getattr(plugin, attrname)
if hasattr(meth, 'tryfirst'):
last.append(meth)
elif hasattr(meth, 'trylast'):
l.insert(0, meth)
else:
l.append(meth)
except AttributeError: except AttributeError:
continue continue
l.extend(last)
return l return l
def call_plugin(self, plugin, methname, kwargs): def call_plugin(self, plugin, methname, kwargs):

View File

@ -1,6 +1,6 @@
""" interactive debugging with PDB, the Python Debugger. """ """ interactive debugging with PDB, the Python Debugger. """
import py import pytest, py
import sys import sys
def pytest_addoption(parser): def pytest_addoption(parser):
@ -45,9 +45,11 @@ class PdbInvoke:
def pytest_sessionfinish(self, session): def pytest_sessionfinish(self, session):
# don't display failures again at the end # don't display failures again at the end
session.config.option.tbstyle = "no" session.config.option.tbstyle = "no"
@pytest.mark.tryfirst
def pytest_runtest_makereport(self, item, call, __multicall__): def pytest_runtest_makereport(self, item, call, __multicall__):
if not call.excinfo or \ if not call.excinfo or \
call.excinfo.errisinstance(py.test.skip.Exception) or \ call.excinfo.errisinstance(pytest.skip.Exception) or \
call.excinfo.errisinstance(py.std.bdb.BdbQuit): call.excinfo.errisinstance(py.std.bdb.BdbQuit):
return return
rep = __multicall__.execute() rep = __multicall__.execute()

View File

@ -19,8 +19,8 @@ def pytest_cmdline_main(config):
showfuncargs(config) showfuncargs(config)
return 0 return 0
def pytest_namespace(__multicall__): @pytest.mark.trylast
__multicall__.execute() def pytest_namespace():
raises.Exception = pytest.fail.Exception raises.Exception = pytest.fail.Exception
return { return {
'raises' : raises, 'raises' : raises,

View File

@ -5,7 +5,7 @@ see http://pytest.org for documentation and details
(c) Holger Krekel and others, 2004-2010 (c) Holger Krekel and others, 2004-2010
""" """
__version__ = '2.0.0.dev31' __version__ = '2.0.0.dev32'
__all__ = ['main'] __all__ = ['main']
from _pytest.core import main, UsageError, _preloadplugins from _pytest.core import main, UsageError, _preloadplugins

View File

@ -22,7 +22,7 @@ def main():
name='pytest', name='pytest',
description='py.test: simple powerful testing with Python', description='py.test: simple powerful testing with Python',
long_description = long_description, long_description = long_description,
version='2.0.0.dev31', version='2.0.0.dev32',
url='http://pytest.org', url='http://pytest.org',
license='MIT license', license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

View File

@ -377,3 +377,13 @@ def test_fdfuncarg_skips_on_no_osdup(testdir):
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
"*1 skipped*" "*1 skipped*"
]) ])
def test_capture_conftest_runtest_setup(testdir):
testdir.makeconftest("""
def pytest_runtest_setup():
print ("hello19")
""")
testdir.makepyfile("def test_func(): pass")
result = testdir.runpytest()
assert result.ret == 0
assert 'hello19' not in result.stdout.str()

View File

@ -418,6 +418,35 @@ class TestPytestPluginInteractions:
assert not pluginmanager.listattr("hello") assert not pluginmanager.listattr("hello")
assert pluginmanager.listattr("x") == [42] assert pluginmanager.listattr("x") == [42]
def test_listattr_tryfirst(self):
class P1:
@pytest.mark.tryfirst
def m(self):
return 17
class P2:
def m(self):
return 23
class P3:
def m(self):
return 19
pluginmanager = PluginManager()
p1 = P1()
p2 = P2()
p3 = P3()
pluginmanager.register(p1)
pluginmanager.register(p2)
pluginmanager.register(p3)
methods = pluginmanager.listattr('m')
assert methods == [p2.m, p3.m, p1.m]
del P1.m.__dict__['tryfirst']
pytest.mark.trylast(getattr(P2.m, 'im_func', P2.m))
methods = pluginmanager.listattr('m')
assert methods == [p2.m, p1.m, p3.m]
def test_namespace_has_default_and_env_plugins(testdir): def test_namespace_has_default_and_env_plugins(testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
import pytest import pytest