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" method = "sys"
capman = CaptureManager(method) capman = CaptureManager(method)
pluginmanager.register(capman, "capturemanager") 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): def addouterr(rep, outerr):
for secname, content in zip(["out", "err"], outerr): for secname, content in zip(["out", "err"], outerr):
@ -82,6 +89,7 @@ class CaptureManager:
for name, cap in self._method2capture.items(): for name, cap in self._method2capture.items():
cap.reset() cap.reset()
def resumecapture_item(self, item): def resumecapture_item(self, item):
method = self._getmethod(item.config, item.fspath) method = self._getmethod(item.config, item.fspath)
if not hasattr(item, 'outerr'): if not hasattr(item, 'outerr'):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import py, pytest
import pdb import pdb
xfail_if_pdbpp_installed = pytest.mark.xfail(hasattr(pdb, "__author__"), 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: class TestDoctests: