fix issue358 -- introduce new pytest_load_initial_conftests hook and make capturing initialization use it, relying on a new (somewhat internal) parser.parse_known_args() method.
This also addresses issue359 -- plugins like pytest-django could implement a pytest_load_initial_conftests hook like the capture plugin.
This commit is contained in:
parent
4b709037ab
commit
db6f347db6
|
@ -75,6 +75,9 @@ new features:
|
|||
- fix issue 308 - allow to mark/xfail/skip individual parameter sets
|
||||
when parametrizing. Thanks Brianna Laugher.
|
||||
|
||||
- call new experimental pytest_load_initial_conftests hook to allow
|
||||
3rd party plugins to do something before a conftest is loaded.
|
||||
|
||||
Bug fixes:
|
||||
|
||||
- pytest now uses argparse instead of optparse (thanks Anthon) which
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
""" per-test stdout/stderr capturing mechanisms, ``capsys`` and ``capfd`` function arguments. """
|
||||
|
||||
import pytest, py
|
||||
import sys
|
||||
import os
|
||||
|
||||
def pytest_addoption(parser):
|
||||
|
@ -12,23 +13,34 @@ def pytest_addoption(parser):
|
|||
help="shortcut for --capture=no.")
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
def pytest_cmdline_parse(pluginmanager, args):
|
||||
# we want to perform capturing already for plugin/conftest loading
|
||||
if '-s' in args or "--capture=no" in args:
|
||||
method = "no"
|
||||
elif hasattr(os, 'dup') and '--capture=sys' not in args:
|
||||
def pytest_load_initial_conftests(early_config, parser, args, __multicall__):
|
||||
ns = parser.parse_known_args(args)
|
||||
method = ns.capture
|
||||
if not method:
|
||||
method = "fd"
|
||||
else:
|
||||
if method == "fd" and not hasattr(os, "dup"):
|
||||
method = "sys"
|
||||
capman = CaptureManager(method)
|
||||
pluginmanager.register(capman, "capturemanager")
|
||||
early_config.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)
|
||||
early_config.pluginmanager.add_shutdown(teardown)
|
||||
|
||||
# finally trigger conftest loading but while capturing (issue93)
|
||||
capman.resumecapture()
|
||||
try:
|
||||
try:
|
||||
return __multicall__.execute()
|
||||
finally:
|
||||
out, err = capman.suspendcapture()
|
||||
except:
|
||||
sys.stdout.write(out)
|
||||
sys.stderr.write(err)
|
||||
raise
|
||||
|
||||
def addouterr(rep, outerr):
|
||||
for secname, content in zip(["out", "err"], outerr):
|
||||
|
@ -89,7 +101,6 @@ 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'):
|
||||
|
|
|
@ -141,8 +141,14 @@ class Parser:
|
|||
self._anonymous.addoption(*opts, **attrs)
|
||||
|
||||
def parse(self, args):
|
||||
from _pytest._argcomplete import try_argcomplete, filescompleter
|
||||
self.optparser = optparser = MyOptionParser(self)
|
||||
from _pytest._argcomplete import try_argcomplete
|
||||
self.optparser = self._getparser()
|
||||
try_argcomplete(self.optparser)
|
||||
return self.optparser.parse_args([str(x) for x in args])
|
||||
|
||||
def _getparser(self):
|
||||
from _pytest._argcomplete import filescompleter
|
||||
optparser = MyOptionParser(self)
|
||||
groups = self._groups + [self._anonymous]
|
||||
for group in groups:
|
||||
if group.options:
|
||||
|
@ -155,8 +161,7 @@ class Parser:
|
|||
# bash like autocompletion for dirs (appending '/')
|
||||
optparser.add_argument(FILE_OR_DIR, nargs='*'
|
||||
).completer=filescompleter
|
||||
try_argcomplete(self.optparser)
|
||||
return self.optparser.parse_args([str(x) for x in args])
|
||||
return optparser
|
||||
|
||||
def parse_setoption(self, args, option):
|
||||
parsedoption = self.parse(args)
|
||||
|
@ -164,6 +169,11 @@ class Parser:
|
|||
setattr(option, name, value)
|
||||
return getattr(parsedoption, FILE_OR_DIR)
|
||||
|
||||
def parse_known_args(self, args):
|
||||
optparser = self._getparser()
|
||||
args = [str(x) for x in args]
|
||||
return optparser.parse_known_args(args)[0]
|
||||
|
||||
def addini(self, name, help, type=None, default=None):
|
||||
""" register an ini-file option.
|
||||
|
||||
|
@ -635,9 +645,6 @@ class Config(object):
|
|||
""" constructor useable for subprocesses. """
|
||||
pluginmanager = get_plugin_manager()
|
||||
config = pluginmanager.config
|
||||
# XXX slightly crude way to initialize capturing
|
||||
import _pytest.capture
|
||||
_pytest.capture.pytest_cmdline_parse(config.pluginmanager, args)
|
||||
config._preparse(args, addopts=False)
|
||||
config.option.__dict__.update(option_dict)
|
||||
for x in config.option.plugins:
|
||||
|
@ -663,21 +670,9 @@ class Config(object):
|
|||
plugins += self._conftest.getconftestmodules(fspath)
|
||||
return plugins
|
||||
|
||||
def _setinitialconftest(self, args):
|
||||
# capture output during conftest init (#issue93)
|
||||
# XXX introduce load_conftest hook to avoid needing to know
|
||||
# about capturing plugin here
|
||||
capman = self.pluginmanager.getplugin("capturemanager")
|
||||
capman.resumecapture()
|
||||
try:
|
||||
try:
|
||||
self._conftest.setinitial(args)
|
||||
finally:
|
||||
out, err = capman.suspendcapture() # logging might have got it
|
||||
except:
|
||||
sys.stdout.write(out)
|
||||
sys.stderr.write(err)
|
||||
raise
|
||||
def pytest_load_initial_conftests(self, parser, args):
|
||||
self._conftest.setinitial(args)
|
||||
pytest_load_initial_conftests.trylast = True
|
||||
|
||||
def _initini(self, args):
|
||||
self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
|
||||
|
@ -692,9 +687,8 @@ class Config(object):
|
|||
self.pluginmanager.consider_preparse(args)
|
||||
self.pluginmanager.consider_setuptools_entrypoints()
|
||||
self.pluginmanager.consider_env()
|
||||
self._setinitialconftest(args)
|
||||
if addopts:
|
||||
self.hook.pytest_cmdline_preparse(config=self, args=args)
|
||||
self.hook.pytest_load_initial_conftests(early_config=self,
|
||||
args=args, parser=self._parser)
|
||||
|
||||
def _checkversion(self):
|
||||
import pytest
|
||||
|
@ -715,6 +709,8 @@ class Config(object):
|
|||
"can only parse cmdline args at most once per Config object")
|
||||
self._origargs = args
|
||||
self._preparse(args)
|
||||
# XXX deprecated hook:
|
||||
self.hook.pytest_cmdline_preparse(config=self, args=args)
|
||||
self._parser.hints.extend(self.pluginmanager._hints)
|
||||
args = self._parser.parse_setoption(args, self.option)
|
||||
if not args:
|
||||
|
|
|
@ -20,7 +20,7 @@ def pytest_cmdline_parse(pluginmanager, args):
|
|||
pytest_cmdline_parse.firstresult = True
|
||||
|
||||
def pytest_cmdline_preparse(config, args):
|
||||
"""modify command line arguments before option parsing. """
|
||||
"""(deprecated) modify command line arguments before option parsing. """
|
||||
|
||||
def pytest_addoption(parser):
|
||||
"""register argparse-style options and ini-style config values.
|
||||
|
@ -52,6 +52,10 @@ def pytest_cmdline_main(config):
|
|||
implementation will invoke the configure hooks and runtest_mainloop. """
|
||||
pytest_cmdline_main.firstresult = True
|
||||
|
||||
def pytest_load_initial_conftests(args, early_config, parser):
|
||||
""" implements loading initial conftests.
|
||||
"""
|
||||
|
||||
def pytest_configure(config):
|
||||
""" called after command line options have been parsed
|
||||
and all plugins and initial conftest files been loaded.
|
||||
|
|
|
@ -484,3 +484,13 @@ def test_capture_conftest_runtest_setup(testdir):
|
|||
result = testdir.runpytest()
|
||||
assert result.ret == 0
|
||||
assert 'hello19' not in result.stdout.str()
|
||||
|
||||
def test_capture_early_option_parsing(testdir):
|
||||
testdir.makeconftest("""
|
||||
def pytest_runtest_setup():
|
||||
print ("hello19")
|
||||
""")
|
||||
testdir.makepyfile("def test_func(): pass")
|
||||
result = testdir.runpytest("-vs")
|
||||
assert result.ret == 0
|
||||
assert 'hello19' in result.stdout.str()
|
||||
|
|
|
@ -335,3 +335,18 @@ def test_notify_exception(testdir, capfd):
|
|||
out, err = capfd.readouterr()
|
||||
assert not err
|
||||
|
||||
|
||||
def test_load_initial_conftest_last_ordering(testdir):
|
||||
from _pytest.config import get_plugin_manager
|
||||
pm = get_plugin_manager()
|
||||
class My:
|
||||
def pytest_load_initial_conftests(self):
|
||||
pass
|
||||
m = My()
|
||||
pm.register(m)
|
||||
l = pm.listattr("pytest_load_initial_conftests")
|
||||
assert l[-1].__module__ == "_pytest.capture"
|
||||
assert l[-2] == m.pytest_load_initial_conftests
|
||||
assert l[-3].__module__ == "_pytest.config"
|
||||
|
||||
|
||||
|
|
|
@ -101,6 +101,12 @@ class TestParser:
|
|||
args = parser.parse([py.path.local()])
|
||||
assert getattr(args, parseopt.FILE_OR_DIR)[0] == py.path.local()
|
||||
|
||||
def test_parse_known_args(self, parser):
|
||||
args = parser.parse_known_args([py.path.local()])
|
||||
parser.addoption("--hello", action="store_true")
|
||||
ns = parser.parse_known_args(["x", "--y", "--hello", "this"])
|
||||
assert ns.hello
|
||||
|
||||
def test_parse_will_set_default(self, parser):
|
||||
parser.addoption("--hello", dest="hello", default="x", action="store")
|
||||
option = parser.parse([])
|
||||
|
|
Loading…
Reference in New Issue