config: split _getconftestmodules and _loadconftestmodules
Previously, the `_getconftestmodules` function was used both to load conftest modules for a path (during `pytest_load_initial_conftests`), and to retrieve conftest modules for a path (during hook dispatch and for fetching `collect_ignore`). This made things muddy - it is usually nicer to have clear separation between "command" and "query" functions, when they occur in separate phases. So split into "load" and "get". Currently, `gethookproxy` still loads conftest itself. I hope to change this in the future.
This commit is contained in:
parent
485c555812
commit
01ac13a77d
|
@ -581,26 +581,25 @@ class PytestPluginManager(PluginManager):
|
||||||
def _try_load_conftest(
|
def _try_load_conftest(
|
||||||
self, anchor: Path, importmode: Union[str, ImportMode], rootpath: Path
|
self, anchor: Path, importmode: Union[str, ImportMode], rootpath: Path
|
||||||
) -> None:
|
) -> None:
|
||||||
self._getconftestmodules(anchor, importmode, rootpath)
|
self._loadconftestmodules(anchor, importmode, rootpath)
|
||||||
# let's also consider test* subdirs
|
# let's also consider test* subdirs
|
||||||
if anchor.is_dir():
|
if anchor.is_dir():
|
||||||
for x in anchor.glob("test*"):
|
for x in anchor.glob("test*"):
|
||||||
if x.is_dir():
|
if x.is_dir():
|
||||||
self._getconftestmodules(x, importmode, rootpath)
|
self._loadconftestmodules(x, importmode, rootpath)
|
||||||
|
|
||||||
def _getconftestmodules(
|
def _loadconftestmodules(
|
||||||
self, path: Path, importmode: Union[str, ImportMode], rootpath: Path
|
self, path: Path, importmode: Union[str, ImportMode], rootpath: Path
|
||||||
) -> Sequence[types.ModuleType]:
|
) -> None:
|
||||||
if self._noconftest:
|
if self._noconftest:
|
||||||
return []
|
return
|
||||||
|
|
||||||
directory = self._get_directory(path)
|
directory = self._get_directory(path)
|
||||||
|
|
||||||
# Optimization: avoid repeated searches in the same directory.
|
# Optimization: avoid repeated searches in the same directory.
|
||||||
# Assumes always called with same importmode and rootpath.
|
# Assumes always called with same importmode and rootpath.
|
||||||
existing_clist = self._dirpath2confmods.get(directory)
|
if directory in self._dirpath2confmods:
|
||||||
if existing_clist is not None:
|
return
|
||||||
return existing_clist
|
|
||||||
|
|
||||||
# XXX these days we may rather want to use config.rootpath
|
# XXX these days we may rather want to use config.rootpath
|
||||||
# and allow users to opt into looking into the rootdir parent
|
# and allow users to opt into looking into the rootdir parent
|
||||||
|
@ -613,16 +612,17 @@ class PytestPluginManager(PluginManager):
|
||||||
mod = self._importconftest(conftestpath, importmode, rootpath)
|
mod = self._importconftest(conftestpath, importmode, rootpath)
|
||||||
clist.append(mod)
|
clist.append(mod)
|
||||||
self._dirpath2confmods[directory] = clist
|
self._dirpath2confmods[directory] = clist
|
||||||
return clist
|
|
||||||
|
def _getconftestmodules(self, path: Path) -> Sequence[types.ModuleType]:
|
||||||
|
directory = self._get_directory(path)
|
||||||
|
return self._dirpath2confmods.get(directory, ())
|
||||||
|
|
||||||
def _rget_with_confmod(
|
def _rget_with_confmod(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
path: Path,
|
path: Path,
|
||||||
importmode: Union[str, ImportMode],
|
|
||||||
rootpath: Path,
|
|
||||||
) -> Tuple[types.ModuleType, Any]:
|
) -> Tuple[types.ModuleType, Any]:
|
||||||
modules = self._getconftestmodules(path, importmode, rootpath=rootpath)
|
modules = self._getconftestmodules(path)
|
||||||
for mod in reversed(modules):
|
for mod in reversed(modules):
|
||||||
try:
|
try:
|
||||||
return mod, getattr(mod, name)
|
return mod, getattr(mod, name)
|
||||||
|
@ -1562,13 +1562,9 @@ class Config:
|
||||||
else:
|
else:
|
||||||
return self._getini_unknown_type(name, type, value)
|
return self._getini_unknown_type(name, type, value)
|
||||||
|
|
||||||
def _getconftest_pathlist(
|
def _getconftest_pathlist(self, name: str, path: Path) -> Optional[List[Path]]:
|
||||||
self, name: str, path: Path, rootpath: Path
|
|
||||||
) -> Optional[List[Path]]:
|
|
||||||
try:
|
try:
|
||||||
mod, relroots = self.pluginmanager._rget_with_confmod(
|
mod, relroots = self.pluginmanager._rget_with_confmod(name, path)
|
||||||
name, path, self.getoption("importmode"), rootpath
|
|
||||||
)
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
assert mod.__file__ is not None
|
assert mod.__file__ is not None
|
||||||
|
|
|
@ -376,7 +376,7 @@ def _in_venv(path: Path) -> bool:
|
||||||
|
|
||||||
def pytest_ignore_collect(collection_path: Path, config: Config) -> Optional[bool]:
|
def pytest_ignore_collect(collection_path: Path, config: Config) -> Optional[bool]:
|
||||||
ignore_paths = config._getconftest_pathlist(
|
ignore_paths = config._getconftest_pathlist(
|
||||||
"collect_ignore", path=collection_path.parent, rootpath=config.rootpath
|
"collect_ignore", path=collection_path.parent
|
||||||
)
|
)
|
||||||
ignore_paths = ignore_paths or []
|
ignore_paths = ignore_paths or []
|
||||||
excludeopt = config.getoption("ignore")
|
excludeopt = config.getoption("ignore")
|
||||||
|
@ -387,7 +387,7 @@ def pytest_ignore_collect(collection_path: Path, config: Config) -> Optional[boo
|
||||||
return True
|
return True
|
||||||
|
|
||||||
ignore_globs = config._getconftest_pathlist(
|
ignore_globs = config._getconftest_pathlist(
|
||||||
"collect_ignore_glob", path=collection_path.parent, rootpath=config.rootpath
|
"collect_ignore_glob", path=collection_path.parent
|
||||||
)
|
)
|
||||||
ignore_globs = ignore_globs or []
|
ignore_globs = ignore_globs or []
|
||||||
excludeglobopt = config.getoption("ignore_glob")
|
excludeglobopt = config.getoption("ignore_glob")
|
||||||
|
@ -551,11 +551,16 @@ class Session(nodes.FSCollector):
|
||||||
pm = self.config.pluginmanager
|
pm = self.config.pluginmanager
|
||||||
# Check if we have the common case of running
|
# Check if we have the common case of running
|
||||||
# hooks with all conftest.py files.
|
# hooks with all conftest.py files.
|
||||||
my_conftestmodules = pm._getconftestmodules(
|
#
|
||||||
|
# TODO: pytest relies on this call to load non-initial conftests. This
|
||||||
|
# is incidental. It will be better to load conftests at a more
|
||||||
|
# well-defined place.
|
||||||
|
pm._loadconftestmodules(
|
||||||
path,
|
path,
|
||||||
self.config.getoption("importmode"),
|
self.config.getoption("importmode"),
|
||||||
rootpath=self.config.rootpath,
|
rootpath=self.config.rootpath,
|
||||||
)
|
)
|
||||||
|
my_conftestmodules = pm._getconftestmodules(path)
|
||||||
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
|
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
|
||||||
if remove_mods:
|
if remove_mods:
|
||||||
# One or more conftests are not in use at this fspath.
|
# One or more conftests are not in use at this fspath.
|
||||||
|
|
|
@ -2103,9 +2103,7 @@ class TestAutouseManagement:
|
||||||
reprec = pytester.inline_run("-v", "-s", "--confcutdir", pytester.path)
|
reprec = pytester.inline_run("-v", "-s", "--confcutdir", pytester.path)
|
||||||
reprec.assertoutcome(passed=8)
|
reprec.assertoutcome(passed=8)
|
||||||
config = reprec.getcalls("pytest_unconfigure")[0].config
|
config = reprec.getcalls("pytest_unconfigure")[0].config
|
||||||
values = config.pluginmanager._getconftestmodules(
|
values = config.pluginmanager._getconftestmodules(p)[0].values
|
||||||
p, importmode="prepend", rootpath=pytester.path
|
|
||||||
)[0].values
|
|
||||||
assert values == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2
|
assert values == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2
|
||||||
|
|
||||||
def test_scope_ordering(self, pytester: Pytester) -> None:
|
def test_scope_ordering(self, pytester: Pytester) -> None:
|
||||||
|
|
|
@ -642,18 +642,11 @@ class TestConfigAPI:
|
||||||
p = tmp_path.joinpath("conftest.py")
|
p = tmp_path.joinpath("conftest.py")
|
||||||
p.write_text(f"mylist = {['.', str(somepath)]}", encoding="utf-8")
|
p.write_text(f"mylist = {['.', str(somepath)]}", encoding="utf-8")
|
||||||
config = pytester.parseconfigure(p)
|
config = pytester.parseconfigure(p)
|
||||||
assert (
|
assert config._getconftest_pathlist("notexist", path=tmp_path) is None
|
||||||
config._getconftest_pathlist("notexist", path=tmp_path, rootpath=tmp_path)
|
assert config._getconftest_pathlist("mylist", path=tmp_path) == [
|
||||||
is None
|
tmp_path,
|
||||||
)
|
somepath,
|
||||||
pl = (
|
]
|
||||||
config._getconftest_pathlist("mylist", path=tmp_path, rootpath=tmp_path)
|
|
||||||
or []
|
|
||||||
)
|
|
||||||
print(pl)
|
|
||||||
assert len(pl) == 2
|
|
||||||
assert pl[0] == tmp_path
|
|
||||||
assert pl[1] == somepath
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("maybe_type", ["not passed", "None", '"string"'])
|
@pytest.mark.parametrize("maybe_type", ["not passed", "None", '"string"'])
|
||||||
def test_addini(self, pytester: Pytester, maybe_type: str) -> None:
|
def test_addini(self, pytester: Pytester, maybe_type: str) -> None:
|
||||||
|
|
|
@ -62,28 +62,22 @@ class TestConftestValueAccessGlobal:
|
||||||
def test_basic_init(self, basedir: Path) -> None:
|
def test_basic_init(self, basedir: Path) -> None:
|
||||||
conftest = PytestPluginManager()
|
conftest = PytestPluginManager()
|
||||||
p = basedir / "adir"
|
p = basedir / "adir"
|
||||||
assert (
|
conftest._loadconftestmodules(p, importmode="prepend", rootpath=basedir)
|
||||||
conftest._rget_with_confmod("a", p, importmode="prepend", rootpath=basedir)[
|
assert conftest._rget_with_confmod("a", p)[1] == 1
|
||||||
1
|
|
||||||
]
|
|
||||||
== 1
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_immediate_initialiation_and_incremental_are_the_same(
|
def test_immediate_initialiation_and_incremental_are_the_same(
|
||||||
self, basedir: Path
|
self, basedir: Path
|
||||||
) -> None:
|
) -> None:
|
||||||
conftest = PytestPluginManager()
|
conftest = PytestPluginManager()
|
||||||
assert not len(conftest._dirpath2confmods)
|
assert not len(conftest._dirpath2confmods)
|
||||||
conftest._getconftestmodules(
|
conftest._loadconftestmodules(basedir, importmode="prepend", rootpath=basedir)
|
||||||
basedir, importmode="prepend", rootpath=Path(basedir)
|
|
||||||
)
|
|
||||||
snap1 = len(conftest._dirpath2confmods)
|
snap1 = len(conftest._dirpath2confmods)
|
||||||
assert snap1 == 1
|
assert snap1 == 1
|
||||||
conftest._getconftestmodules(
|
conftest._loadconftestmodules(
|
||||||
basedir / "adir", importmode="prepend", rootpath=basedir
|
basedir / "adir", importmode="prepend", rootpath=basedir
|
||||||
)
|
)
|
||||||
assert len(conftest._dirpath2confmods) == snap1 + 1
|
assert len(conftest._dirpath2confmods) == snap1 + 1
|
||||||
conftest._getconftestmodules(
|
conftest._loadconftestmodules(
|
||||||
basedir / "b", importmode="prepend", rootpath=basedir
|
basedir / "b", importmode="prepend", rootpath=basedir
|
||||||
)
|
)
|
||||||
assert len(conftest._dirpath2confmods) == snap1 + 2
|
assert len(conftest._dirpath2confmods) == snap1 + 2
|
||||||
|
@ -91,33 +85,23 @@ class TestConftestValueAccessGlobal:
|
||||||
def test_value_access_not_existing(self, basedir: Path) -> None:
|
def test_value_access_not_existing(self, basedir: Path) -> None:
|
||||||
conftest = ConftestWithSetinitial(basedir)
|
conftest = ConftestWithSetinitial(basedir)
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
conftest._rget_with_confmod(
|
conftest._rget_with_confmod("a", basedir)
|
||||||
"a", basedir, importmode="prepend", rootpath=Path(basedir)
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_value_access_by_path(self, basedir: Path) -> None:
|
def test_value_access_by_path(self, basedir: Path) -> None:
|
||||||
conftest = ConftestWithSetinitial(basedir)
|
conftest = ConftestWithSetinitial(basedir)
|
||||||
adir = basedir / "adir"
|
adir = basedir / "adir"
|
||||||
assert (
|
conftest._loadconftestmodules(adir, importmode="prepend", rootpath=basedir)
|
||||||
conftest._rget_with_confmod(
|
assert conftest._rget_with_confmod("a", adir)[1] == 1
|
||||||
"a", adir, importmode="prepend", rootpath=basedir
|
conftest._loadconftestmodules(
|
||||||
)[1]
|
adir / "b", importmode="prepend", rootpath=basedir
|
||||||
== 1
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
conftest._rget_with_confmod(
|
|
||||||
"a", adir / "b", importmode="prepend", rootpath=basedir
|
|
||||||
)[1]
|
|
||||||
== 1.5
|
|
||||||
)
|
)
|
||||||
|
assert conftest._rget_with_confmod("a", adir / "b")[1] == 1.5
|
||||||
|
|
||||||
def test_value_access_with_confmod(self, basedir: Path) -> None:
|
def test_value_access_with_confmod(self, basedir: Path) -> None:
|
||||||
startdir = basedir / "adir" / "b"
|
startdir = basedir / "adir" / "b"
|
||||||
startdir.joinpath("xx").mkdir()
|
startdir.joinpath("xx").mkdir()
|
||||||
conftest = ConftestWithSetinitial(startdir)
|
conftest = ConftestWithSetinitial(startdir)
|
||||||
mod, value = conftest._rget_with_confmod(
|
mod, value = conftest._rget_with_confmod("a", startdir)
|
||||||
"a", startdir, importmode="prepend", rootpath=Path(basedir)
|
|
||||||
)
|
|
||||||
assert value == 1.5
|
assert value == 1.5
|
||||||
assert mod.__file__ is not None
|
assert mod.__file__ is not None
|
||||||
path = Path(mod.__file__)
|
path = Path(mod.__file__)
|
||||||
|
@ -143,9 +127,7 @@ def test_doubledash_considered(pytester: Pytester) -> None:
|
||||||
conf.joinpath("conftest.py").touch()
|
conf.joinpath("conftest.py").touch()
|
||||||
conftest = PytestPluginManager()
|
conftest = PytestPluginManager()
|
||||||
conftest_setinitial(conftest, [conf.name, conf.name])
|
conftest_setinitial(conftest, [conf.name, conf.name])
|
||||||
values = conftest._getconftestmodules(
|
values = conftest._getconftestmodules(conf)
|
||||||
conf, importmode="prepend", rootpath=pytester.path
|
|
||||||
)
|
|
||||||
assert len(values) == 1
|
assert len(values) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@ -192,26 +174,22 @@ def test_conftestcutdir(pytester: Pytester) -> None:
|
||||||
p = pytester.mkdir("x")
|
p = pytester.mkdir("x")
|
||||||
conftest = PytestPluginManager()
|
conftest = PytestPluginManager()
|
||||||
conftest_setinitial(conftest, [pytester.path], confcutdir=p)
|
conftest_setinitial(conftest, [pytester.path], confcutdir=p)
|
||||||
values = conftest._getconftestmodules(
|
conftest._loadconftestmodules(p, importmode="prepend", rootpath=pytester.path)
|
||||||
p, importmode="prepend", rootpath=pytester.path
|
values = conftest._getconftestmodules(p)
|
||||||
)
|
|
||||||
assert len(values) == 0
|
assert len(values) == 0
|
||||||
values = conftest._getconftestmodules(
|
conftest._loadconftestmodules(
|
||||||
conf.parent, importmode="prepend", rootpath=pytester.path
|
conf.parent, importmode="prepend", rootpath=pytester.path
|
||||||
)
|
)
|
||||||
|
values = conftest._getconftestmodules(conf.parent)
|
||||||
assert len(values) == 0
|
assert len(values) == 0
|
||||||
assert not conftest.has_plugin(str(conf))
|
assert not conftest.has_plugin(str(conf))
|
||||||
# but we can still import a conftest directly
|
# but we can still import a conftest directly
|
||||||
conftest._importconftest(conf, importmode="prepend", rootpath=pytester.path)
|
conftest._importconftest(conf, importmode="prepend", rootpath=pytester.path)
|
||||||
values = conftest._getconftestmodules(
|
values = conftest._getconftestmodules(conf.parent)
|
||||||
conf.parent, importmode="prepend", rootpath=pytester.path
|
|
||||||
)
|
|
||||||
assert values[0].__file__ is not None
|
assert values[0].__file__ is not None
|
||||||
assert values[0].__file__.startswith(str(conf))
|
assert values[0].__file__.startswith(str(conf))
|
||||||
# and all sub paths get updated properly
|
# and all sub paths get updated properly
|
||||||
values = conftest._getconftestmodules(
|
values = conftest._getconftestmodules(p)
|
||||||
p, importmode="prepend", rootpath=pytester.path
|
|
||||||
)
|
|
||||||
assert len(values) == 1
|
assert len(values) == 1
|
||||||
assert values[0].__file__ is not None
|
assert values[0].__file__ is not None
|
||||||
assert values[0].__file__.startswith(str(conf))
|
assert values[0].__file__.startswith(str(conf))
|
||||||
|
@ -221,9 +199,7 @@ def test_conftestcutdir_inplace_considered(pytester: Pytester) -> None:
|
||||||
conf = pytester.makeconftest("")
|
conf = pytester.makeconftest("")
|
||||||
conftest = PytestPluginManager()
|
conftest = PytestPluginManager()
|
||||||
conftest_setinitial(conftest, [conf.parent], confcutdir=conf.parent)
|
conftest_setinitial(conftest, [conf.parent], confcutdir=conf.parent)
|
||||||
values = conftest._getconftestmodules(
|
values = conftest._getconftestmodules(conf.parent)
|
||||||
conf.parent, importmode="prepend", rootpath=pytester.path
|
|
||||||
)
|
|
||||||
assert len(values) == 1
|
assert len(values) == 1
|
||||||
assert values[0].__file__ is not None
|
assert values[0].__file__ is not None
|
||||||
assert values[0].__file__.startswith(str(conf))
|
assert values[0].__file__.startswith(str(conf))
|
||||||
|
@ -433,10 +409,8 @@ def test_conftest_import_order(pytester: Pytester, monkeypatch: MonkeyPatch) ->
|
||||||
conftest = PytestPluginManager()
|
conftest = PytestPluginManager()
|
||||||
conftest._confcutdir = pytester.path
|
conftest._confcutdir = pytester.path
|
||||||
monkeypatch.setattr(conftest, "_importconftest", impct)
|
monkeypatch.setattr(conftest, "_importconftest", impct)
|
||||||
mods = cast(
|
conftest._loadconftestmodules(sub, importmode="prepend", rootpath=pytester.path)
|
||||||
List[Path],
|
mods = cast(List[Path], conftest._getconftestmodules(sub))
|
||||||
conftest._getconftestmodules(sub, importmode="prepend", rootpath=pytester.path),
|
|
||||||
)
|
|
||||||
expected = [ct1, ct2]
|
expected = [ct1, ct2]
|
||||||
assert mods == expected
|
assert mods == expected
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue