merge conftest management into PytestPluginManager
--HG-- branch : plugin_no_pytest
This commit is contained in:
parent
894d7dca22
commit
d632a0d5c2
|
@ -98,6 +98,12 @@ class PytestPluginManager(PluginManager):
|
|||
self._warnings = []
|
||||
self._plugin_distinfo = []
|
||||
self._globalplugins = []
|
||||
|
||||
# state related to local conftest plugins
|
||||
self._path2confmods = {}
|
||||
self._conftestpath2mod = {}
|
||||
self._confcutdir = None
|
||||
|
||||
self.addhooks(hookspec)
|
||||
self.register(self)
|
||||
if os.environ.get('PYTEST_DEBUG'):
|
||||
|
@ -140,6 +146,89 @@ class PytestPluginManager(PluginManager):
|
|||
for warning in self._warnings:
|
||||
config.warn(code="I1", message=warning)
|
||||
|
||||
#
|
||||
# internal API for local conftest plugin handling
|
||||
#
|
||||
def _set_initial_conftests(self, namespace):
|
||||
""" load initial conftest files given a preparsed "namespace".
|
||||
As conftest files may add their own command line options
|
||||
which have arguments ('--my-opt somepath') we might get some
|
||||
false positives. All builtin and 3rd party plugins will have
|
||||
been loaded, however, so common options will not confuse our logic
|
||||
here.
|
||||
"""
|
||||
current = py.path.local()
|
||||
self._confcutdir = current.join(namespace.confcutdir, abs=True) \
|
||||
if namespace.confcutdir else None
|
||||
testpaths = namespace.file_or_dir
|
||||
foundanchor = False
|
||||
for path in testpaths:
|
||||
path = str(path)
|
||||
# remove node-id syntax
|
||||
i = path.find("::")
|
||||
if i != -1:
|
||||
path = path[:i]
|
||||
anchor = current.join(path, abs=1)
|
||||
if exists(anchor): # we found some file object
|
||||
self._try_load_conftest(anchor)
|
||||
foundanchor = True
|
||||
if not foundanchor:
|
||||
self._try_load_conftest(current)
|
||||
|
||||
def _try_load_conftest(self, anchor):
|
||||
self._getconftestmodules(anchor)
|
||||
# let's also consider test* subdirs
|
||||
if anchor.check(dir=1):
|
||||
for x in anchor.listdir("test*"):
|
||||
if x.check(dir=1):
|
||||
self._getconftestmodules(x)
|
||||
|
||||
def _getconftestmodules(self, path):
|
||||
try:
|
||||
return self._path2confmods[path]
|
||||
except KeyError:
|
||||
clist = []
|
||||
for parent in path.parts():
|
||||
if self._confcutdir and self._confcutdir.relto(parent):
|
||||
continue
|
||||
conftestpath = parent.join("conftest.py")
|
||||
if conftestpath.check(file=1):
|
||||
mod = self._importconftest(conftestpath)
|
||||
clist.append(mod)
|
||||
self._path2confmods[path] = clist
|
||||
return clist
|
||||
|
||||
def _rget_with_confmod(self, name, path):
|
||||
modules = self._getconftestmodules(path)
|
||||
for mod in reversed(modules):
|
||||
try:
|
||||
return mod, getattr(mod, name)
|
||||
except AttributeError:
|
||||
continue
|
||||
raise KeyError(name)
|
||||
|
||||
def _importconftest(self, conftestpath):
|
||||
try:
|
||||
return self._conftestpath2mod[conftestpath]
|
||||
except KeyError:
|
||||
pkgpath = conftestpath.pypkgpath()
|
||||
if pkgpath is None:
|
||||
_ensure_removed_sysmodule(conftestpath.purebasename)
|
||||
try:
|
||||
mod = conftestpath.pyimport()
|
||||
except Exception:
|
||||
raise ConftestImportFailure(conftestpath, sys.exc_info())
|
||||
self._conftestpath2mod[conftestpath] = mod
|
||||
dirpath = conftestpath.dirpath()
|
||||
if dirpath in self._path2confmods:
|
||||
for path, mods in self._path2confmods.items():
|
||||
if path and path.relto(dirpath) or path == dirpath:
|
||||
assert mod not in mods
|
||||
mods.append(mod)
|
||||
self.trace("loaded conftestmodule %r" %(mod))
|
||||
self.consider_conftest(mod)
|
||||
return mod
|
||||
|
||||
#
|
||||
# API for bootstrapping plugin loading
|
||||
#
|
||||
|
@ -572,96 +661,6 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter):
|
|||
return action._formatted_action_invocation
|
||||
|
||||
|
||||
class Conftest(object):
|
||||
""" the single place for accessing values and interacting
|
||||
towards conftest modules from pytest objects.
|
||||
"""
|
||||
def __init__(self, onimport=None):
|
||||
self._path2confmods = {}
|
||||
self._onimport = onimport
|
||||
self._conftestpath2mod = {}
|
||||
self._confcutdir = None
|
||||
|
||||
def setinitial(self, namespace):
|
||||
""" load initial conftest files given a preparsed "namespace".
|
||||
As conftest files may add their own command line options
|
||||
which have arguments ('--my-opt somepath') we might get some
|
||||
false positives. All builtin and 3rd party plugins will have
|
||||
been loaded, however, so common options will not confuse our logic
|
||||
here.
|
||||
"""
|
||||
current = py.path.local()
|
||||
self._confcutdir = current.join(namespace.confcutdir, abs=True) \
|
||||
if namespace.confcutdir else None
|
||||
testpaths = namespace.file_or_dir
|
||||
foundanchor = False
|
||||
for path in testpaths:
|
||||
path = str(path)
|
||||
# remove node-id syntax
|
||||
i = path.find("::")
|
||||
if i != -1:
|
||||
path = path[:i]
|
||||
anchor = current.join(path, abs=1)
|
||||
if exists(anchor): # we found some file object
|
||||
self._try_load_conftest(anchor)
|
||||
foundanchor = True
|
||||
if not foundanchor:
|
||||
self._try_load_conftest(current)
|
||||
|
||||
def _try_load_conftest(self, anchor):
|
||||
self.getconftestmodules(anchor)
|
||||
# let's also consider test* subdirs
|
||||
if anchor.check(dir=1):
|
||||
for x in anchor.listdir("test*"):
|
||||
if x.check(dir=1):
|
||||
self.getconftestmodules(x)
|
||||
|
||||
def getconftestmodules(self, path):
|
||||
try:
|
||||
return self._path2confmods[path]
|
||||
except KeyError:
|
||||
clist = []
|
||||
for parent in path.parts():
|
||||
if self._confcutdir and self._confcutdir.relto(parent):
|
||||
continue
|
||||
conftestpath = parent.join("conftest.py")
|
||||
if conftestpath.check(file=1):
|
||||
mod = self.importconftest(conftestpath)
|
||||
clist.append(mod)
|
||||
self._path2confmods[path] = clist
|
||||
return clist
|
||||
|
||||
def rget_with_confmod(self, name, path):
|
||||
modules = self.getconftestmodules(path)
|
||||
for mod in reversed(modules):
|
||||
try:
|
||||
return mod, getattr(mod, name)
|
||||
except AttributeError:
|
||||
continue
|
||||
raise KeyError(name)
|
||||
|
||||
def importconftest(self, conftestpath):
|
||||
try:
|
||||
return self._conftestpath2mod[conftestpath]
|
||||
except KeyError:
|
||||
pkgpath = conftestpath.pypkgpath()
|
||||
if pkgpath is None:
|
||||
_ensure_removed_sysmodule(conftestpath.purebasename)
|
||||
try:
|
||||
mod = conftestpath.pyimport()
|
||||
except Exception:
|
||||
raise ConftestImportFailure(conftestpath, sys.exc_info())
|
||||
self._conftestpath2mod[conftestpath] = mod
|
||||
dirpath = conftestpath.dirpath()
|
||||
if dirpath in self._path2confmods:
|
||||
for path, mods in self._path2confmods.items():
|
||||
if path and path.relto(dirpath) or path == dirpath:
|
||||
assert mod not in mods
|
||||
mods.append(mod)
|
||||
if self._onimport:
|
||||
self._onimport(mod)
|
||||
return mod
|
||||
|
||||
|
||||
def _ensure_removed_sysmodule(modname):
|
||||
try:
|
||||
|
@ -697,7 +696,6 @@ class Config(object):
|
|||
#: a pluginmanager instance
|
||||
self.pluginmanager = pluginmanager
|
||||
self.trace = self.pluginmanager.trace.root.get("config")
|
||||
self._conftest = Conftest(onimport=self._onimportconftest)
|
||||
self.hook = self.pluginmanager.hook
|
||||
self._inicache = {}
|
||||
self._opt2dest = {}
|
||||
|
@ -783,10 +781,6 @@ class Config(object):
|
|||
config.pluginmanager.consider_pluginarg(x)
|
||||
return config
|
||||
|
||||
def _onimportconftest(self, conftestmodule):
|
||||
self.trace("loaded conftestmodule %r" %(conftestmodule,))
|
||||
self.pluginmanager.consider_conftest(conftestmodule)
|
||||
|
||||
def _processopt(self, opt):
|
||||
for name in opt._short_opts + opt._long_opts:
|
||||
self._opt2dest[name] = opt.dest
|
||||
|
@ -797,10 +791,10 @@ class Config(object):
|
|||
|
||||
def _getmatchingplugins(self, fspath):
|
||||
return self.pluginmanager._globalplugins + \
|
||||
self._conftest.getconftestmodules(fspath)
|
||||
self.pluginmanager._getconftestmodules(fspath)
|
||||
|
||||
def pytest_load_initial_conftests(self, early_config):
|
||||
self._conftest.setinitial(early_config.known_args_namespace)
|
||||
self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
|
||||
pytest_load_initial_conftests.trylast = True
|
||||
|
||||
def _initini(self, args):
|
||||
|
@ -907,7 +901,7 @@ class Config(object):
|
|||
|
||||
def _getconftest_pathlist(self, name, path):
|
||||
try:
|
||||
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
||||
mod, relroots = self.pluginmanager._rget_with_confmod(name, path)
|
||||
except KeyError:
|
||||
return None
|
||||
modpath = py.path.local(mod.__file__).dirpath()
|
||||
|
|
|
@ -132,7 +132,7 @@ class DoctestModule(pytest.File):
|
|||
def collect(self):
|
||||
import doctest
|
||||
if self.fspath.basename == "conftest.py":
|
||||
module = self.config._conftest.importconftest(self.fspath)
|
||||
module = self.config._conftest._importconftest(self.fspath)
|
||||
else:
|
||||
try:
|
||||
module = self.fspath.pyimport()
|
||||
|
|
|
@ -1487,7 +1487,7 @@ class TestAutouseManagement:
|
|||
reprec = testdir.inline_run("-v","-s")
|
||||
reprec.assertoutcome(passed=8)
|
||||
config = reprec.getcalls("pytest_unconfigure")[0].config
|
||||
l = config._conftest.getconftestmodules(p)[0].l
|
||||
l = config.pluginmanager._getconftestmodules(p)[0].l
|
||||
assert l == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2
|
||||
|
||||
def test_scope_ordering(self, testdir):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from textwrap import dedent
|
||||
import py, pytest
|
||||
from _pytest.config import Conftest
|
||||
|
||||
from _pytest.config import PytestPluginManager
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", params=["global", "inpackage"])
|
||||
|
@ -16,7 +15,7 @@ def basedir(request):
|
|||
return tmpdir
|
||||
|
||||
def ConftestWithSetinitial(path):
|
||||
conftest = Conftest()
|
||||
conftest = PytestPluginManager()
|
||||
conftest_setinitial(conftest, [path])
|
||||
return conftest
|
||||
|
||||
|
@ -25,51 +24,41 @@ def conftest_setinitial(conftest, args, confcutdir=None):
|
|||
def __init__(self):
|
||||
self.file_or_dir = args
|
||||
self.confcutdir = str(confcutdir)
|
||||
conftest.setinitial(Namespace())
|
||||
conftest._set_initial_conftests(Namespace())
|
||||
|
||||
class TestConftestValueAccessGlobal:
|
||||
def test_basic_init(self, basedir):
|
||||
conftest = Conftest()
|
||||
conftest = PytestPluginManager()
|
||||
p = basedir.join("adir")
|
||||
assert conftest.rget_with_confmod("a", p)[1] == 1
|
||||
|
||||
def test_onimport(self, basedir):
|
||||
l = []
|
||||
conftest = Conftest(onimport=l.append)
|
||||
adir = basedir.join("adir")
|
||||
conftest_setinitial(conftest, [adir], confcutdir=basedir)
|
||||
assert len(l) == 1
|
||||
assert conftest.rget_with_confmod("a", adir)[1] == 1
|
||||
assert conftest.rget_with_confmod("b", adir.join("b"))[1] == 2
|
||||
assert len(l) == 2
|
||||
assert conftest._rget_with_confmod("a", p)[1] == 1
|
||||
|
||||
def test_immediate_initialiation_and_incremental_are_the_same(self, basedir):
|
||||
conftest = Conftest()
|
||||
conftest = PytestPluginManager()
|
||||
len(conftest._path2confmods)
|
||||
conftest.getconftestmodules(basedir)
|
||||
conftest._getconftestmodules(basedir)
|
||||
snap1 = len(conftest._path2confmods)
|
||||
#assert len(conftest._path2confmods) == snap1 + 1
|
||||
conftest.getconftestmodules(basedir.join('adir'))
|
||||
conftest._getconftestmodules(basedir.join('adir'))
|
||||
assert len(conftest._path2confmods) == snap1 + 1
|
||||
conftest.getconftestmodules(basedir.join('b'))
|
||||
conftest._getconftestmodules(basedir.join('b'))
|
||||
assert len(conftest._path2confmods) == snap1 + 2
|
||||
|
||||
def test_value_access_not_existing(self, basedir):
|
||||
conftest = ConftestWithSetinitial(basedir)
|
||||
with pytest.raises(KeyError):
|
||||
conftest.rget_with_confmod('a', basedir)
|
||||
conftest._rget_with_confmod('a', basedir)
|
||||
|
||||
def test_value_access_by_path(self, basedir):
|
||||
conftest = ConftestWithSetinitial(basedir)
|
||||
adir = basedir.join("adir")
|
||||
assert conftest.rget_with_confmod("a", adir)[1] == 1
|
||||
assert conftest.rget_with_confmod("a", adir.join("b"))[1] == 1.5
|
||||
assert conftest._rget_with_confmod("a", adir)[1] == 1
|
||||
assert conftest._rget_with_confmod("a", adir.join("b"))[1] == 1.5
|
||||
|
||||
def test_value_access_with_confmod(self, basedir):
|
||||
startdir = basedir.join("adir", "b")
|
||||
startdir.ensure("xx", dir=True)
|
||||
conftest = ConftestWithSetinitial(startdir)
|
||||
mod, value = conftest.rget_with_confmod("a", startdir)
|
||||
mod, value = conftest._rget_with_confmod("a", startdir)
|
||||
assert value == 1.5
|
||||
path = py.path.local(mod.__file__)
|
||||
assert path.dirpath() == basedir.join("adir", "b")
|
||||
|
@ -85,9 +74,9 @@ def test_conftest_in_nonpkg_with_init(tmpdir):
|
|||
def test_doubledash_considered(testdir):
|
||||
conf = testdir.mkdir("--option")
|
||||
conf.join("conftest.py").ensure()
|
||||
conftest = Conftest()
|
||||
conftest = PytestPluginManager()
|
||||
conftest_setinitial(conftest, [conf.basename, conf.basename])
|
||||
l = conftest.getconftestmodules(conf)
|
||||
l = conftest._getconftestmodules(conf)
|
||||
assert len(l) == 1
|
||||
|
||||
def test_issue151_load_all_conftests(testdir):
|
||||
|
@ -96,7 +85,7 @@ def test_issue151_load_all_conftests(testdir):
|
|||
p = testdir.mkdir(name)
|
||||
p.ensure("conftest.py")
|
||||
|
||||
conftest = Conftest()
|
||||
conftest = PytestPluginManager()
|
||||
conftest_setinitial(conftest, names)
|
||||
d = list(conftest._conftestpath2mod.values())
|
||||
assert len(d) == len(names)
|
||||
|
@ -105,15 +94,15 @@ def test_conftest_global_import(testdir):
|
|||
testdir.makeconftest("x=3")
|
||||
p = testdir.makepyfile("""
|
||||
import py, pytest
|
||||
from _pytest.config import Conftest
|
||||
conf = Conftest()
|
||||
mod = conf.importconftest(py.path.local("conftest.py"))
|
||||
from _pytest.config import PytestPluginManager
|
||||
conf = PytestPluginManager()
|
||||
mod = conf._importconftest(py.path.local("conftest.py"))
|
||||
assert mod.x == 3
|
||||
import conftest
|
||||
assert conftest is mod, (conftest, mod)
|
||||
subconf = py.path.local().ensure("sub", "conftest.py")
|
||||
subconf.write("y=4")
|
||||
mod2 = conf.importconftest(subconf)
|
||||
mod2 = conf._importconftest(subconf)
|
||||
assert mod != mod2
|
||||
assert mod2.y == 4
|
||||
import conftest
|
||||
|
@ -125,27 +114,27 @@ def test_conftest_global_import(testdir):
|
|||
def test_conftestcutdir(testdir):
|
||||
conf = testdir.makeconftest("")
|
||||
p = testdir.mkdir("x")
|
||||
conftest = Conftest()
|
||||
conftest = PytestPluginManager()
|
||||
conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p)
|
||||
l = conftest.getconftestmodules(p)
|
||||
l = conftest._getconftestmodules(p)
|
||||
assert len(l) == 0
|
||||
l = conftest.getconftestmodules(conf.dirpath())
|
||||
l = conftest._getconftestmodules(conf.dirpath())
|
||||
assert len(l) == 0
|
||||
assert conf not in conftest._conftestpath2mod
|
||||
# but we can still import a conftest directly
|
||||
conftest.importconftest(conf)
|
||||
l = conftest.getconftestmodules(conf.dirpath())
|
||||
conftest._importconftest(conf)
|
||||
l = conftest._getconftestmodules(conf.dirpath())
|
||||
assert l[0].__file__.startswith(str(conf))
|
||||
# and all sub paths get updated properly
|
||||
l = conftest.getconftestmodules(p)
|
||||
l = conftest._getconftestmodules(p)
|
||||
assert len(l) == 1
|
||||
assert l[0].__file__.startswith(str(conf))
|
||||
|
||||
def test_conftestcutdir_inplace_considered(testdir):
|
||||
conf = testdir.makeconftest("")
|
||||
conftest = Conftest()
|
||||
conftest = PytestPluginManager()
|
||||
conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath())
|
||||
l = conftest.getconftestmodules(conf.dirpath())
|
||||
l = conftest._getconftestmodules(conf.dirpath())
|
||||
assert len(l) == 1
|
||||
assert l[0].__file__.startswith(str(conf))
|
||||
|
||||
|
@ -153,7 +142,7 @@ def test_conftestcutdir_inplace_considered(testdir):
|
|||
def test_setinitial_conftest_subdirs(testdir, name):
|
||||
sub = testdir.mkdir(name)
|
||||
subconftest = sub.ensure("conftest.py")
|
||||
conftest = Conftest()
|
||||
conftest = PytestPluginManager()
|
||||
conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir)
|
||||
if name not in ('whatever', '.dotdir'):
|
||||
assert subconftest in conftest._conftestpath2mod
|
||||
|
@ -199,9 +188,9 @@ def test_conftest_import_order(testdir, monkeypatch):
|
|||
ct2.write("")
|
||||
def impct(p):
|
||||
return p
|
||||
conftest = Conftest()
|
||||
monkeypatch.setattr(conftest, 'importconftest', impct)
|
||||
assert conftest.getconftestmodules(sub) == [ct1, ct2]
|
||||
conftest = PytestPluginManager()
|
||||
monkeypatch.setattr(conftest, '_importconftest', impct)
|
||||
assert conftest._getconftestmodules(sub) == [ct1, ct2]
|
||||
|
||||
|
||||
def test_fixture_dependency(testdir, monkeypatch):
|
||||
|
|
|
@ -94,7 +94,7 @@ class TestPytestPluginInteractions:
|
|||
return xyz + 1
|
||||
""")
|
||||
config = get_plugin_manager().config
|
||||
config._conftest.importconftest(conf)
|
||||
config.pluginmanager._importconftest(conf)
|
||||
print(config.pluginmanager.getplugins())
|
||||
res = config.hook.pytest_myhook(xyz=10)
|
||||
assert res == [11]
|
||||
|
@ -143,7 +143,7 @@ class TestPytestPluginInteractions:
|
|||
parser.addoption('--test123', action="store_true",
|
||||
default=True)
|
||||
""")
|
||||
config._conftest.importconftest(p)
|
||||
config.pluginmanager._importconftest(p)
|
||||
assert config.option.test123
|
||||
|
||||
def test_configure(self, testdir):
|
||||
|
@ -849,10 +849,6 @@ class TestPytestPluginManager:
|
|||
mod = pytestpm.getplugin("pkg.plug")
|
||||
assert mod.x == 3
|
||||
|
||||
def test_config_sets_conftesthandle_onimport(self, testdir):
|
||||
config = testdir.parseconfig([])
|
||||
assert config._conftest._onimport == config._onimportconftest
|
||||
|
||||
def test_consider_conftest_deps(self, testdir, pytestpm):
|
||||
mod = testdir.makepyfile("pytest_plugins='xyz'").pyimport()
|
||||
with pytest.raises(ImportError):
|
||||
|
|
Loading…
Reference in New Issue