introduce pluginmanager.ensure_teardown() which allows

This commit is contained in:
holger krekel 2013-09-28 22:23:00 +02:00
parent ac19212b2d
commit a930f44e60
7 changed files with 42 additions and 30 deletions

View File

@ -22,6 +22,13 @@ def pytest_cmdline_parse(pluginmanager, args):
method = "sys"
capman = CaptureManager(method)
pluginmanager.register(capman, "capturemanager")
# make sure that capturemanager is properly reset at final shutdown
def teardown():
try:
capman.reset_capturings()
except ValueError:
pass
pluginmanager.add_shutdown(teardown)
def addouterr(rep, outerr):
for secname, content in zip(["out", "err"], outerr):
@ -82,6 +89,7 @@ class CaptureManager:
for name, cap in self._method2capture.items():
cap.reset()
def resumecapture_item(self, item):
method = self._getmethod(item.config, item.fspath)
if not hasattr(item, 'outerr'):

View File

@ -80,6 +80,7 @@ class PluginManager(object):
self._hints = []
self.trace = TagTracer().get("pluginmanage")
self._plugin_distinfo = []
self._shutdown = []
if os.environ.get('PYTEST_DEBUG'):
err = sys.stderr
encoding = getattr(err, 'encoding', 'utf8')
@ -118,6 +119,17 @@ class PluginManager(object):
if value == plugin:
del self._name2plugin[name]
def add_shutdown(self, func):
self._shutdown.append(func)
def ensure_shutdown(self):
while self._shutdown:
func = self._shutdown.pop()
func()
self._plugins = []
self._name2plugin.clear()
self._listattrcache.clear()
def isregistered(self, plugin, name=None):
if self.getplugin(name) is not None:
return True
@ -286,7 +298,7 @@ class PluginManager(object):
config = self._config
del self._config
config.hook.pytest_unconfigure(config=config)
config.pluginmanager.unregister(self)
config.pluginmanager.ensure_shutdown()
def notify_exception(self, excinfo, option=None):
if option and option.fulltrace:

View File

@ -106,6 +106,7 @@ def wrap_session(config, doit):
exitstatus=session.exitstatus)
if initstate >= 1:
config.pluginmanager.do_unconfigure(config)
config.pluginmanager.ensure_shutdown()
return session.exitstatus
def pytest_cmdline_main(config):

View File

@ -83,6 +83,7 @@ class HookRecorder:
def finish_recording(self):
for recorder in self._recorders.values():
if self._pluginmanager.isregistered(recorder):
self._pluginmanager.unregister(recorder)
self._recorders.clear()
@ -361,7 +362,7 @@ class TmpTestdir:
if not plugins:
plugins = []
plugins.append(Collect())
ret = self.pytestmain(list(args), plugins=plugins)
ret = pytest.main(list(args), plugins=plugins)
reprec = rec[0]
reprec.ret = ret
assert len(rec) == 1
@ -376,14 +377,15 @@ class TmpTestdir:
args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
import _pytest.core
config = _pytest.core._prepareconfig(args, self.plugins)
# the in-process pytest invocation needs to avoid leaking FDs
# so we register a "reset_capturings" callmon the capturing manager
# and make sure it gets called
config._cleanup.append(
config.pluginmanager.getplugin("capturemanager").reset_capturings)
import _pytest.config
self.request.addfinalizer(
lambda: _pytest.config.pytest_unconfigure(config))
# we don't know what the test will do with this half-setup config
# object and thus we make sure it gets unconfigured properly in any
# case (otherwise capturing could still be active, for example)
def ensure_unconfigure():
if hasattr(config.pluginmanager, "_config"):
config.pluginmanager.do_unconfigure(config)
config.pluginmanager.ensure_shutdown()
self.request.addfinalizer(ensure_unconfigure)
return config
def parseconfigure(self, *args):
@ -428,17 +430,6 @@ class TmpTestdir:
return py.std.subprocess.Popen(cmdargs,
stdout=stdout, stderr=stderr, **kw)
def pytestmain(self, *args, **kwargs):
class ResetCapturing:
@pytest.mark.trylast
def pytest_unconfigure(self, config):
capman = config.pluginmanager.getplugin("capturemanager")
capman.reset_capturings()
plugins = kwargs.setdefault("plugins", [])
rc = ResetCapturing()
plugins.append(rc)
return pytest.main(*args, **kwargs)
def run(self, *cmdargs):
return self._run(*cmdargs)

View File

@ -391,15 +391,15 @@ class TestInvocationVariants:
def test_equivalence_pytest_pytest(self):
assert pytest.main == py.test.cmdline.main
def test_invoke_with_string(self, testdir, capsys):
retcode = testdir.pytestmain("-h")
def test_invoke_with_string(self, capsys):
retcode = pytest.main("-h")
assert not retcode
out, err = capsys.readouterr()
assert "--help" in out
pytest.raises(ValueError, lambda: pytest.main(0))
def test_invoke_with_path(self, testdir, capsys):
retcode = testdir.pytestmain(testdir.tmpdir)
def test_invoke_with_path(self, tmpdir, capsys):
retcode = pytest.main(tmpdir)
assert not retcode
out, err = capsys.readouterr()
@ -408,7 +408,7 @@ class TestInvocationVariants:
def pytest_addoption(self, parser):
parser.addoption("--myopt")
testdir.pytestmain(["-h"], plugins=[MyPlugin()])
pytest.main(["-h"], plugins=[MyPlugin()])
out, err = capsys.readouterr()
assert "--myopt" in out

View File

@ -123,7 +123,7 @@ class TestCollectPluginHookRelay:
def pytest_collect_file(self, path, parent):
wascalled.append(path)
testdir.makefile(".abc", "xyz")
testdir.pytestmain([testdir.tmpdir], plugins=[Plugin()])
pytest.main([testdir.tmpdir], plugins=[Plugin()])
assert len(wascalled) == 1
assert wascalled[0].ext == '.abc'
@ -134,7 +134,7 @@ class TestCollectPluginHookRelay:
wascalled.append(path.basename)
testdir.mkdir("hello")
testdir.mkdir("world")
testdir.pytestmain(testdir.tmpdir, plugins=[Plugin()])
pytest.main(testdir.tmpdir, plugins=[Plugin()])
assert "hello" in wascalled
assert "world" in wascalled

View File

@ -4,7 +4,7 @@ import py, pytest
import pdb
xfail_if_pdbpp_installed = pytest.mark.xfail(hasattr(pdb, "__author__"),
reason="doctest/pdbpp problem: https://bitbucket.org/antocuni/pdb/issue/24/doctests-fail-when-pdbpp-is-installed")
reason="doctest/pdbpp problem: https://bitbucket.org/antocuni/pdb/issue/24/doctests-fail-when-pdbpp-is-installed", run=False)
class TestDoctests: