Merge pull request #11268 from bluetech/conftest-load

config: split `_getconftestmodules` and `_loadconftestmodules`
This commit is contained in:
Ran Benita 2023-08-08 14:44:29 +03:00 committed by GitHub
commit b2186e2455
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 84 deletions

View File

@ -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

View File

@ -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.

View File

@ -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:

View File

@ -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:

View File

@ -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