Some py.path.local -> pathlib.Path

- Some conftest related functions
- _confcutdir
- Allow arbitrary os.PathLike[str] in gethookproxy.
This commit is contained in:
Ran Benita 2020-12-11 13:40:37 +02:00
parent 902739cfc3
commit ed658d6829
10 changed files with 141 additions and 126 deletions

View File

@ -50,9 +50,11 @@ from _pytest.compat import final
from _pytest.compat import importlib_metadata from _pytest.compat import importlib_metadata
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.outcomes import Skipped from _pytest.outcomes import Skipped
from _pytest.pathlib import absolutepath
from _pytest.pathlib import bestrelpath from _pytest.pathlib import bestrelpath
from _pytest.pathlib import import_path from _pytest.pathlib import import_path
from _pytest.pathlib import ImportMode from _pytest.pathlib import ImportMode
from _pytest.pathlib import resolve_package_path
from _pytest.store import Store from _pytest.store import Store
from _pytest.warning_types import PytestConfigWarning from _pytest.warning_types import PytestConfigWarning
@ -102,9 +104,7 @@ class ExitCode(enum.IntEnum):
class ConftestImportFailure(Exception): class ConftestImportFailure(Exception):
def __init__( def __init__(
self, self, path: Path, excinfo: Tuple[Type[Exception], Exception, TracebackType],
path: py.path.local,
excinfo: Tuple[Type[Exception], Exception, TracebackType],
) -> None: ) -> None:
super().__init__(path, excinfo) super().__init__(path, excinfo)
self.path = path self.path = path
@ -342,9 +342,9 @@ class PytestPluginManager(PluginManager):
self._conftest_plugins: Set[types.ModuleType] = set() self._conftest_plugins: Set[types.ModuleType] = set()
# State related to local conftest plugins. # State related to local conftest plugins.
self._dirpath2confmods: Dict[py.path.local, List[types.ModuleType]] = {} self._dirpath2confmods: Dict[Path, List[types.ModuleType]] = {}
self._conftestpath2mod: Dict[Path, types.ModuleType] = {} self._conftestpath2mod: Dict[Path, types.ModuleType] = {}
self._confcutdir: Optional[py.path.local] = None self._confcutdir: Optional[Path] = None
self._noconftest = False self._noconftest = False
self._duplicatepaths: Set[py.path.local] = set() self._duplicatepaths: Set[py.path.local] = set()
@ -479,9 +479,9 @@ class PytestPluginManager(PluginManager):
All builtin and 3rd party plugins will have been loaded, however, so All builtin and 3rd party plugins will have been loaded, however, so
common options will not confuse our logic here. common options will not confuse our logic here.
""" """
current = py.path.local() current = Path.cwd()
self._confcutdir = ( self._confcutdir = (
current.join(namespace.confcutdir, abs=True) absolutepath(current / namespace.confcutdir)
if namespace.confcutdir if namespace.confcutdir
else None else None
) )
@ -495,7 +495,7 @@ class PytestPluginManager(PluginManager):
i = path.find("::") i = path.find("::")
if i != -1: if i != -1:
path = path[:i] path = path[:i]
anchor = current.join(path, abs=1) anchor = absolutepath(current / path)
if anchor.exists(): # we found some file object if anchor.exists(): # we found some file object
self._try_load_conftest(anchor, namespace.importmode) self._try_load_conftest(anchor, namespace.importmode)
foundanchor = True foundanchor = True
@ -503,24 +503,24 @@ class PytestPluginManager(PluginManager):
self._try_load_conftest(current, namespace.importmode) self._try_load_conftest(current, namespace.importmode)
def _try_load_conftest( def _try_load_conftest(
self, anchor: py.path.local, importmode: Union[str, ImportMode] self, anchor: Path, importmode: Union[str, ImportMode]
) -> None: ) -> None:
self._getconftestmodules(anchor, importmode) self._getconftestmodules(anchor, importmode)
# let's also consider test* subdirs # let's also consider test* subdirs
if anchor.check(dir=1): if anchor.is_dir():
for x in anchor.listdir("test*"): for x in anchor.glob("test*"):
if x.check(dir=1): if x.is_dir():
self._getconftestmodules(x, importmode) self._getconftestmodules(x, importmode)
@lru_cache(maxsize=128) @lru_cache(maxsize=128)
def _getconftestmodules( def _getconftestmodules(
self, path: py.path.local, importmode: Union[str, ImportMode], self, path: Path, importmode: Union[str, ImportMode],
) -> List[types.ModuleType]: ) -> List[types.ModuleType]:
if self._noconftest: if self._noconftest:
return [] return []
if path.isfile(): if path.is_file():
directory = path.dirpath() directory = path.parent
else: else:
directory = path directory = path
@ -528,18 +528,18 @@ class PytestPluginManager(PluginManager):
# and allow users to opt into looking into the rootdir parent # and allow users to opt into looking into the rootdir parent
# directories instead of requiring to specify confcutdir. # directories instead of requiring to specify confcutdir.
clist = [] clist = []
for parent in directory.parts(): for parent in reversed((directory, *directory.parents)):
if self._confcutdir and self._confcutdir.relto(parent): if self._confcutdir and parent in self._confcutdir.parents:
continue continue
conftestpath = parent.join("conftest.py") conftestpath = parent / "conftest.py"
if conftestpath.isfile(): if conftestpath.is_file():
mod = self._importconftest(conftestpath, importmode) mod = self._importconftest(conftestpath, importmode)
clist.append(mod) clist.append(mod)
self._dirpath2confmods[directory] = clist self._dirpath2confmods[directory] = clist
return clist return clist
def _rget_with_confmod( def _rget_with_confmod(
self, name: str, path: py.path.local, importmode: Union[str, ImportMode], self, name: str, path: Path, importmode: Union[str, ImportMode],
) -> Tuple[types.ModuleType, Any]: ) -> Tuple[types.ModuleType, Any]:
modules = self._getconftestmodules(path, importmode) modules = self._getconftestmodules(path, importmode)
for mod in reversed(modules): for mod in reversed(modules):
@ -550,21 +550,21 @@ class PytestPluginManager(PluginManager):
raise KeyError(name) raise KeyError(name)
def _importconftest( def _importconftest(
self, conftestpath: py.path.local, importmode: Union[str, ImportMode], self, conftestpath: Path, importmode: Union[str, ImportMode],
) -> types.ModuleType: ) -> types.ModuleType:
# Use a resolved Path object as key to avoid loading the same conftest # Use a resolved Path object as key to avoid loading the same conftest
# twice with build systems that create build directories containing # twice with build systems that create build directories containing
# symlinks to actual files. # symlinks to actual files.
# Using Path().resolve() is better than py.path.realpath because # Using Path().resolve() is better than py.path.realpath because
# it resolves to the correct path/drive in case-insensitive file systems (#5792) # it resolves to the correct path/drive in case-insensitive file systems (#5792)
key = Path(str(conftestpath)).resolve() key = conftestpath.resolve()
with contextlib.suppress(KeyError): with contextlib.suppress(KeyError):
return self._conftestpath2mod[key] return self._conftestpath2mod[key]
pkgpath = conftestpath.pypkgpath() pkgpath = resolve_package_path(conftestpath)
if pkgpath is None: if pkgpath is None:
_ensure_removed_sysmodule(conftestpath.purebasename) _ensure_removed_sysmodule(conftestpath.stem)
try: try:
mod = import_path(conftestpath, mode=importmode) mod = import_path(conftestpath, mode=importmode)
@ -577,10 +577,10 @@ class PytestPluginManager(PluginManager):
self._conftest_plugins.add(mod) self._conftest_plugins.add(mod)
self._conftestpath2mod[key] = mod self._conftestpath2mod[key] = mod
dirpath = conftestpath.dirpath() dirpath = conftestpath.parent
if dirpath in self._dirpath2confmods: if dirpath in self._dirpath2confmods:
for path, mods in self._dirpath2confmods.items(): for path, mods in self._dirpath2confmods.items():
if path and path.relto(dirpath) or path == dirpath: if path and dirpath in path.parents or path == dirpath:
assert mod not in mods assert mod not in mods
mods.append(mod) mods.append(mod)
self.trace(f"loading conftestmodule {mod!r}") self.trace(f"loading conftestmodule {mod!r}")
@ -588,7 +588,7 @@ class PytestPluginManager(PluginManager):
return mod return mod
def _check_non_top_pytest_plugins( def _check_non_top_pytest_plugins(
self, mod: types.ModuleType, conftestpath: py.path.local, self, mod: types.ModuleType, conftestpath: Path,
) -> None: ) -> None:
if ( if (
hasattr(mod, "pytest_plugins") hasattr(mod, "pytest_plugins")
@ -1412,21 +1412,23 @@ class Config:
assert type in [None, "string"] assert type in [None, "string"]
return value return value
def _getconftest_pathlist( def _getconftest_pathlist(self, name: str, path: Path) -> Optional[List[Path]]:
self, name: str, path: py.path.local
) -> Optional[List[py.path.local]]:
try: try:
mod, relroots = self.pluginmanager._rget_with_confmod( mod, relroots = self.pluginmanager._rget_with_confmod(
name, path, self.getoption("importmode") name, path, self.getoption("importmode")
) )
except KeyError: except KeyError:
return None return None
modpath = py.path.local(mod.__file__).dirpath() modpath = Path(mod.__file__).parent
values: List[py.path.local] = [] values: List[Path] = []
for relroot in relroots: for relroot in relroots:
if not isinstance(relroot, py.path.local): if isinstance(relroot, Path):
pass
elif isinstance(relroot, py.path.local):
relroot = Path(relroot)
else:
relroot = relroot.replace("/", os.sep) relroot = relroot.replace("/", os.sep)
relroot = modpath.join(relroot, abs=True) relroot = absolutepath(modpath / relroot)
values.append(relroot) values.append(relroot)
return values return values

View File

@ -7,6 +7,7 @@ import traceback
import types import types
import warnings import warnings
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import Dict from typing import Dict
@ -525,7 +526,7 @@ class DoctestModule(pytest.Module):
if self.fspath.basename == "conftest.py": if self.fspath.basename == "conftest.py":
module = self.config.pluginmanager._importconftest( module = self.config.pluginmanager._importconftest(
self.fspath, self.config.getoption("importmode") Path(self.fspath), self.config.getoption("importmode")
) )
else: else:
try: try:

View File

@ -371,22 +371,23 @@ def _in_venv(path: py.path.local) -> bool:
def pytest_ignore_collect(path: py.path.local, config: Config) -> Optional[bool]: def pytest_ignore_collect(path: py.path.local, config: Config) -> Optional[bool]:
ignore_paths = config._getconftest_pathlist("collect_ignore", path=path.dirpath()) path_ = Path(path)
ignore_paths = config._getconftest_pathlist("collect_ignore", path=path_.parent)
ignore_paths = ignore_paths or [] ignore_paths = ignore_paths or []
excludeopt = config.getoption("ignore") excludeopt = config.getoption("ignore")
if excludeopt: if excludeopt:
ignore_paths.extend([py.path.local(x) for x in excludeopt]) ignore_paths.extend(absolutepath(x) for x in excludeopt)
if py.path.local(path) in ignore_paths: if path_ in ignore_paths:
return True return True
ignore_globs = config._getconftest_pathlist( ignore_globs = config._getconftest_pathlist(
"collect_ignore_glob", path=path.dirpath() "collect_ignore_glob", path=path_.parent
) )
ignore_globs = ignore_globs or [] ignore_globs = ignore_globs or []
excludeglobopt = config.getoption("ignore_glob") excludeglobopt = config.getoption("ignore_glob")
if excludeglobopt: if excludeglobopt:
ignore_globs.extend([py.path.local(x) for x in excludeglobopt]) ignore_globs.extend(absolutepath(x) for x in excludeglobopt)
if any(fnmatch.fnmatch(str(path), str(glob)) for glob in ignore_globs): if any(fnmatch.fnmatch(str(path), str(glob)) for glob in ignore_globs):
return True return True
@ -512,12 +513,12 @@ class Session(nodes.FSCollector):
def isinitpath(self, path: py.path.local) -> bool: def isinitpath(self, path: py.path.local) -> bool:
return path in self._initialpaths return path in self._initialpaths
def gethookproxy(self, fspath: py.path.local): def gethookproxy(self, fspath: "os.PathLike[str]"):
# 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.
pm = self.config.pluginmanager pm = self.config.pluginmanager
my_conftestmodules = pm._getconftestmodules( my_conftestmodules = pm._getconftestmodules(
fspath, self.config.getoption("importmode") Path(fspath), self.config.getoption("importmode")
) )
remove_mods = pm._conftest_plugins.difference(my_conftestmodules) remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
if remove_mods: if remove_mods:
@ -668,8 +669,9 @@ class Session(nodes.FSCollector):
# No point in finding packages when collecting doctests. # No point in finding packages when collecting doctests.
if not self.config.getoption("doctestmodules", False): if not self.config.getoption("doctestmodules", False):
pm = self.config.pluginmanager pm = self.config.pluginmanager
confcutdir = py.path.local(pm._confcutdir) if pm._confcutdir else None
for parent in reversed(argpath.parts()): for parent in reversed(argpath.parts()):
if pm._confcutdir and pm._confcutdir.relto(parent): if confcutdir and confcutdir.relto(parent):
break break
if parent.isdir(): if parent.isdir():

View File

@ -520,7 +520,7 @@ class FSCollector(Collector):
"""The public constructor.""" """The public constructor."""
return super().from_parent(parent=parent, fspath=fspath, **kw) return super().from_parent(parent=parent, fspath=fspath, **kw)
def gethookproxy(self, fspath: py.path.local): def gethookproxy(self, fspath: "os.PathLike[str]"):
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.gethookproxy(fspath) return self.session.gethookproxy(fspath)

View File

@ -653,7 +653,7 @@ class Package(Module):
func = partial(_call_with_optional_argument, teardown_module, self.obj) func = partial(_call_with_optional_argument, teardown_module, self.obj)
self.addfinalizer(func) self.addfinalizer(func)
def gethookproxy(self, fspath: py.path.local): def gethookproxy(self, fspath: "os.PathLike[str]"):
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.gethookproxy(fspath) return self.session.gethookproxy(fspath)

View File

@ -8,6 +8,7 @@ from _pytest.compat import getfuncargnames
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from _pytest.pytester import get_public_names from _pytest.pytester import get_public_names
from _pytest.pytester import Pytester
from _pytest.pytester import Testdir from _pytest.pytester import Testdir
@ -1961,8 +1962,10 @@ class TestAutouseManagement:
reprec = testdir.inline_run("-v", "-s") reprec = testdir.inline_run("-v", "-s")
reprec.assertoutcome(passed=4) reprec.assertoutcome(passed=4)
def test_class_function_parametrization_finalization(self, testdir): def test_class_function_parametrization_finalization(
p = testdir.makeconftest( self, pytester: Pytester
) -> None:
p = pytester.makeconftest(
""" """
import pytest import pytest
import pprint import pprint
@ -1984,7 +1987,7 @@ class TestAutouseManagement:
request.addfinalizer(fin) request.addfinalizer(fin)
""" """
) )
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@ -1996,8 +1999,7 @@ class TestAutouseManagement:
pass pass
""" """
) )
confcut = f"--confcutdir={testdir.tmpdir}" reprec = pytester.inline_run("-v", "-s", "--confcutdir", pytester.path)
reprec = testdir.inline_run("-v", "-s", confcut)
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(p, importmode="prepend")[ values = config.pluginmanager._getconftestmodules(p, importmode="prepend")[

View File

@ -364,7 +364,9 @@ class TestCustomConftests:
def test_collectignore_exclude_on_option(self, pytester: Pytester) -> None: def test_collectignore_exclude_on_option(self, pytester: Pytester) -> None:
pytester.makeconftest( pytester.makeconftest(
""" """
collect_ignore = ['hello', 'test_world.py'] import py
from pathlib import Path
collect_ignore = [py.path.local('hello'), 'test_world.py', Path('bye')]
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption("--XX", action="store_true", default=False) parser.addoption("--XX", action="store_true", default=False)
def pytest_configure(config): def pytest_configure(config):

View File

@ -574,16 +574,16 @@ class TestConfigAPI:
config.getvalue("x") config.getvalue("x")
assert config.getoption("x", 1) == 1 assert config.getoption("x", 1) == 1
def test_getconftest_pathlist(self, pytester: Pytester, tmpdir) -> None: def test_getconftest_pathlist(self, pytester: Pytester, tmp_path: Path) -> None:
somepath = tmpdir.join("x", "y", "z") somepath = tmp_path.joinpath("x", "y", "z")
p = tmpdir.join("conftest.py") p = tmp_path.joinpath("conftest.py")
p.write("pathlist = ['.', %r]" % str(somepath)) p.write_text(f"pathlist = ['.', {str(somepath)!r}]")
config = pytester.parseconfigure(p) config = pytester.parseconfigure(p)
assert config._getconftest_pathlist("notexist", path=tmpdir) is None assert config._getconftest_pathlist("notexist", path=tmp_path) is None
pl = config._getconftest_pathlist("pathlist", path=tmpdir) or [] pl = config._getconftest_pathlist("pathlist", path=tmp_path) or []
print(pl) print(pl)
assert len(pl) == 2 assert len(pl) == 2
assert pl[0] == tmpdir assert pl[0] == tmp_path
assert pl[1] == somepath assert pl[1] == somepath
@pytest.mark.parametrize("maybe_type", ["not passed", "None", '"string"']) @pytest.mark.parametrize("maybe_type", ["not passed", "None", '"string"'])
@ -1935,10 +1935,10 @@ class TestPytestPluginsVariable:
assert msg not in res.stdout.str() assert msg not in res.stdout.str()
def test_conftest_import_error_repr(tmpdir: py.path.local) -> None: def test_conftest_import_error_repr(tmp_path: Path) -> None:
"""`ConftestImportFailure` should use a short error message and readable """`ConftestImportFailure` should use a short error message and readable
path to the failed conftest.py file.""" path to the failed conftest.py file."""
path = tmpdir.join("foo/conftest.py") path = tmp_path.joinpath("foo/conftest.py")
with pytest.raises( with pytest.raises(
ConftestImportFailure, ConftestImportFailure,
match=re.escape(f"RuntimeError: some error (from {path})"), match=re.escape(f"RuntimeError: some error (from {path})"),

View File

@ -4,6 +4,7 @@ import textwrap
from pathlib import Path from pathlib import Path
from typing import cast from typing import cast
from typing import Dict from typing import Dict
from typing import Generator
from typing import List from typing import List
from typing import Optional from typing import Optional
@ -15,7 +16,7 @@ from _pytest.config import PytestPluginManager
from _pytest.monkeypatch import MonkeyPatch from _pytest.monkeypatch import MonkeyPatch
from _pytest.pathlib import symlink_or_skip from _pytest.pathlib import symlink_or_skip
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
from _pytest.pytester import Testdir from _pytest.tmpdir import TempPathFactory
def ConftestWithSetinitial(path) -> PytestPluginManager: def ConftestWithSetinitial(path) -> PytestPluginManager:
@ -25,12 +26,12 @@ def ConftestWithSetinitial(path) -> PytestPluginManager:
def conftest_setinitial( def conftest_setinitial(
conftest: PytestPluginManager, args, confcutdir: Optional[py.path.local] = None conftest: PytestPluginManager, args, confcutdir: Optional["os.PathLike[str]"] = None
) -> None: ) -> None:
class Namespace: class Namespace:
def __init__(self) -> None: def __init__(self) -> None:
self.file_or_dir = args self.file_or_dir = args
self.confcutdir = str(confcutdir) self.confcutdir = os.fspath(confcutdir) if confcutdir is not None else None
self.noconftest = False self.noconftest = False
self.pyargs = False self.pyargs = False
self.importmode = "prepend" self.importmode = "prepend"
@ -42,54 +43,58 @@ def conftest_setinitial(
@pytest.mark.usefixtures("_sys_snapshot") @pytest.mark.usefixtures("_sys_snapshot")
class TestConftestValueAccessGlobal: class TestConftestValueAccessGlobal:
@pytest.fixture(scope="module", params=["global", "inpackage"]) @pytest.fixture(scope="module", params=["global", "inpackage"])
def basedir(self, request, tmpdir_factory): def basedir(
tmpdir = tmpdir_factory.mktemp("basedir", numbered=True) self, request, tmp_path_factory: TempPathFactory
tmpdir.ensure("adir/conftest.py").write("a=1 ; Directory = 3") ) -> Generator[Path, None, None]:
tmpdir.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") tmpdir = tmp_path_factory.mktemp("basedir", numbered=True)
tmpdir.joinpath("adir/b").mkdir(parents=True)
tmpdir.joinpath("adir/conftest.py").write_text("a=1 ; Directory = 3")
tmpdir.joinpath("adir/b/conftest.py").write_text("b=2 ; a = 1.5")
if request.param == "inpackage": if request.param == "inpackage":
tmpdir.ensure("adir/__init__.py") tmpdir.joinpath("adir/__init__.py").touch()
tmpdir.ensure("adir/b/__init__.py") tmpdir.joinpath("adir/b/__init__.py").touch()
yield tmpdir yield tmpdir
def test_basic_init(self, basedir): def test_basic_init(self, basedir: Path) -> None:
conftest = PytestPluginManager() conftest = PytestPluginManager()
p = basedir.join("adir") p = basedir / "adir"
assert conftest._rget_with_confmod("a", p, importmode="prepend")[1] == 1 assert conftest._rget_with_confmod("a", p, importmode="prepend")[1] == 1
def test_immediate_initialiation_and_incremental_are_the_same(self, basedir): def test_immediate_initialiation_and_incremental_are_the_same(
self, basedir: Path
) -> None:
conftest = PytestPluginManager() conftest = PytestPluginManager()
assert not len(conftest._dirpath2confmods) assert not len(conftest._dirpath2confmods)
conftest._getconftestmodules(basedir, importmode="prepend") conftest._getconftestmodules(basedir, importmode="prepend")
snap1 = len(conftest._dirpath2confmods) snap1 = len(conftest._dirpath2confmods)
assert snap1 == 1 assert snap1 == 1
conftest._getconftestmodules(basedir.join("adir"), importmode="prepend") conftest._getconftestmodules(basedir / "adir", importmode="prepend")
assert len(conftest._dirpath2confmods) == snap1 + 1 assert len(conftest._dirpath2confmods) == snap1 + 1
conftest._getconftestmodules(basedir.join("b"), importmode="prepend") conftest._getconftestmodules(basedir / "b", importmode="prepend")
assert len(conftest._dirpath2confmods) == snap1 + 2 assert len(conftest._dirpath2confmods) == snap1 + 2
def test_value_access_not_existing(self, basedir): 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("a", basedir, importmode="prepend") conftest._rget_with_confmod("a", basedir, importmode="prepend")
def test_value_access_by_path(self, basedir): def test_value_access_by_path(self, basedir: Path) -> None:
conftest = ConftestWithSetinitial(basedir) conftest = ConftestWithSetinitial(basedir)
adir = basedir.join("adir") adir = basedir / "adir"
assert conftest._rget_with_confmod("a", adir, importmode="prepend")[1] == 1 assert conftest._rget_with_confmod("a", adir, importmode="prepend")[1] == 1
assert ( assert (
conftest._rget_with_confmod("a", adir.join("b"), importmode="prepend")[1] conftest._rget_with_confmod("a", adir / "b", importmode="prepend")[1] == 1.5
== 1.5
) )
def test_value_access_with_confmod(self, basedir): def test_value_access_with_confmod(self, basedir: Path) -> None:
startdir = basedir.join("adir", "b") startdir = basedir / "adir" / "b"
startdir.ensure("xx", dir=True) startdir.joinpath("xx").mkdir()
conftest = ConftestWithSetinitial(startdir) conftest = ConftestWithSetinitial(startdir)
mod, value = conftest._rget_with_confmod("a", startdir, importmode="prepend") mod, value = conftest._rget_with_confmod("a", startdir, importmode="prepend")
assert value == 1.5 assert value == 1.5
path = py.path.local(mod.__file__) path = py.path.local(mod.__file__)
assert path.dirpath() == basedir.join("adir", "b") assert path.dirpath() == basedir / "adir" / "b"
assert path.purebasename.startswith("conftest") assert path.purebasename.startswith("conftest")
@ -102,12 +107,12 @@ def test_conftest_in_nonpkg_with_init(tmp_path: Path, _sys_snapshot) -> None:
ConftestWithSetinitial(tmp_path.joinpath("adir-1.0", "b")) ConftestWithSetinitial(tmp_path.joinpath("adir-1.0", "b"))
def test_doubledash_considered(testdir: Testdir) -> None: def test_doubledash_considered(pytester: Pytester) -> None:
conf = testdir.mkdir("--option") conf = pytester.mkdir("--option")
conf.join("conftest.py").ensure() conf.joinpath("conftest.py").touch()
conftest = PytestPluginManager() conftest = PytestPluginManager()
conftest_setinitial(conftest, [conf.basename, conf.basename]) conftest_setinitial(conftest, [conf.name, conf.name])
values = conftest._getconftestmodules(py.path.local(conf), importmode="prepend") values = conftest._getconftestmodules(conf, importmode="prepend")
assert len(values) == 1 assert len(values) == 1
@ -127,15 +132,18 @@ def test_conftest_global_import(pytester: Pytester) -> None:
pytester.makeconftest("x=3") pytester.makeconftest("x=3")
p = pytester.makepyfile( p = pytester.makepyfile(
""" """
import py, pytest from pathlib import Path
import pytest
from _pytest.config import PytestPluginManager from _pytest.config import PytestPluginManager
conf = PytestPluginManager() conf = PytestPluginManager()
mod = conf._importconftest(py.path.local("conftest.py"), importmode="prepend") mod = conf._importconftest(Path("conftest.py"), importmode="prepend")
assert mod.x == 3 assert mod.x == 3
import conftest import conftest
assert conftest is mod, (conftest, mod) assert conftest is mod, (conftest, mod)
subconf = py.path.local().ensure("sub", "conftest.py") sub = Path("sub")
subconf.write("y=4") sub.mkdir()
subconf = sub / "conftest.py"
subconf.write_text("y=4")
mod2 = conf._importconftest(subconf, importmode="prepend") mod2 = conf._importconftest(subconf, importmode="prepend")
assert mod != mod2 assert mod != mod2
assert mod2.y == 4 assert mod2.y == 4
@ -147,19 +155,19 @@ def test_conftest_global_import(pytester: Pytester) -> None:
assert res.ret == 0 assert res.ret == 0
def test_conftestcutdir(testdir: Testdir) -> None: def test_conftestcutdir(pytester: Pytester) -> None:
conf = testdir.makeconftest("") conf = pytester.makeconftest("")
p = testdir.mkdir("x") p = pytester.mkdir("x")
conftest = PytestPluginManager() conftest = PytestPluginManager()
conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p) conftest_setinitial(conftest, [pytester.path], confcutdir=p)
values = conftest._getconftestmodules(p, importmode="prepend") values = conftest._getconftestmodules(p, importmode="prepend")
assert len(values) == 0 assert len(values) == 0
values = conftest._getconftestmodules(conf.dirpath(), importmode="prepend") values = conftest._getconftestmodules(conf.parent, importmode="prepend")
assert len(values) == 0 assert len(values) == 0
assert Path(conf) not in conftest._conftestpath2mod assert Path(conf) not in conftest._conftestpath2mod
# but we can still import a conftest directly # but we can still import a conftest directly
conftest._importconftest(conf, importmode="prepend") conftest._importconftest(conf, importmode="prepend")
values = conftest._getconftestmodules(conf.dirpath(), importmode="prepend") values = conftest._getconftestmodules(conf.parent, importmode="prepend")
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(p, importmode="prepend") values = conftest._getconftestmodules(p, importmode="prepend")
@ -170,10 +178,8 @@ def test_conftestcutdir(testdir: Testdir) -> None:
def test_conftestcutdir_inplace_considered(pytester: Pytester) -> None: def test_conftestcutdir_inplace_considered(pytester: Pytester) -> None:
conf = pytester.makeconftest("") conf = pytester.makeconftest("")
conftest = PytestPluginManager() conftest = PytestPluginManager()
conftest_setinitial(conftest, [conf.parent], confcutdir=py.path.local(conf.parent)) conftest_setinitial(conftest, [conf.parent], confcutdir=conf.parent)
values = conftest._getconftestmodules( values = conftest._getconftestmodules(conf.parent, importmode="prepend")
py.path.local(conf.parent), importmode="prepend"
)
assert len(values) == 1 assert len(values) == 1
assert values[0].__file__.startswith(str(conf)) assert values[0].__file__.startswith(str(conf))
@ -184,7 +190,7 @@ def test_setinitial_conftest_subdirs(pytester: Pytester, name: str) -> None:
subconftest = sub.joinpath("conftest.py") subconftest = sub.joinpath("conftest.py")
subconftest.touch() subconftest.touch()
conftest = PytestPluginManager() conftest = PytestPluginManager()
conftest_setinitial(conftest, [sub.parent], confcutdir=py.path.local(pytester.path)) conftest_setinitial(conftest, [sub.parent], confcutdir=pytester.path)
key = subconftest.resolve() key = subconftest.resolve()
if name not in ("whatever", ".dotdir"): if name not in ("whatever", ".dotdir"):
assert key in conftest._conftestpath2mod assert key in conftest._conftestpath2mod
@ -337,22 +343,19 @@ def test_conftest_existing_junitxml(pytester: Pytester) -> None:
result.stdout.fnmatch_lines(["*--xyz*"]) result.stdout.fnmatch_lines(["*--xyz*"])
def test_conftest_import_order(testdir: Testdir, monkeypatch: MonkeyPatch) -> None: def test_conftest_import_order(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
ct1 = testdir.makeconftest("") ct1 = pytester.makeconftest("")
sub = testdir.mkdir("sub") sub = pytester.mkdir("sub")
ct2 = sub.join("conftest.py") ct2 = sub / "conftest.py"
ct2.write("") ct2.write_text("")
def impct(p, importmode): def impct(p, importmode):
return p return p
conftest = PytestPluginManager() conftest = PytestPluginManager()
conftest._confcutdir = testdir.tmpdir conftest._confcutdir = pytester.path
monkeypatch.setattr(conftest, "_importconftest", impct) monkeypatch.setattr(conftest, "_importconftest", impct)
mods = cast( mods = cast(List[Path], conftest._getconftestmodules(sub, importmode="prepend"))
List[py.path.local],
conftest._getconftestmodules(py.path.local(sub), importmode="prepend"),
)
expected = [ct1, ct2] expected = [ct1, ct2]
assert mods == expected assert mods == expected

View File

@ -8,6 +8,7 @@ from _pytest.config import ExitCode
from _pytest.config import PytestPluginManager from _pytest.config import PytestPluginManager
from _pytest.config.exceptions import UsageError from _pytest.config.exceptions import UsageError
from _pytest.main import Session from _pytest.main import Session
from _pytest.pytester import Pytester
@pytest.fixture @pytest.fixture
@ -16,14 +17,16 @@ def pytestpm() -> PytestPluginManager:
class TestPytestPluginInteractions: class TestPytestPluginInteractions:
def test_addhooks_conftestplugin(self, testdir, _config_for_test): def test_addhooks_conftestplugin(
testdir.makepyfile( self, pytester: Pytester, _config_for_test
) -> None:
pytester.makepyfile(
newhooks=""" newhooks="""
def pytest_myhook(xyz): def pytest_myhook(xyz):
"new hook" "new hook"
""" """
) )
conf = testdir.makeconftest( conf = pytester.makeconftest(
""" """
import newhooks import newhooks
def pytest_addhooks(pluginmanager): def pytest_addhooks(pluginmanager):
@ -54,10 +57,10 @@ class TestPytestPluginInteractions:
assert res.ret != 0 assert res.ret != 0
res.stderr.fnmatch_lines(["*did not find*sys*"]) res.stderr.fnmatch_lines(["*did not find*sys*"])
def test_do_option_postinitialize(self, testdir): def test_do_option_postinitialize(self, pytester: Pytester) -> None:
config = testdir.parseconfigure() config = pytester.parseconfigure()
assert not hasattr(config.option, "test123") assert not hasattr(config.option, "test123")
p = testdir.makepyfile( p = pytester.makepyfile(
""" """
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption('--test123', action="store_true", parser.addoption('--test123', action="store_true",
@ -120,20 +123,20 @@ class TestPytestPluginInteractions:
finally: finally:
undo() undo()
def test_hook_proxy(self, testdir): def test_hook_proxy(self, pytester: Pytester) -> None:
"""Test the gethookproxy function(#2016)""" """Test the gethookproxy function(#2016)"""
config = testdir.parseconfig() config = pytester.parseconfig()
session = Session.from_config(config) session = Session.from_config(config)
testdir.makepyfile(**{"tests/conftest.py": "", "tests/subdir/conftest.py": ""}) pytester.makepyfile(**{"tests/conftest.py": "", "tests/subdir/conftest.py": ""})
conftest1 = testdir.tmpdir.join("tests/conftest.py") conftest1 = pytester.path.joinpath("tests/conftest.py")
conftest2 = testdir.tmpdir.join("tests/subdir/conftest.py") conftest2 = pytester.path.joinpath("tests/subdir/conftest.py")
config.pluginmanager._importconftest(conftest1, importmode="prepend") config.pluginmanager._importconftest(conftest1, importmode="prepend")
ihook_a = session.gethookproxy(testdir.tmpdir.join("tests")) ihook_a = session.gethookproxy(pytester.path / "tests")
assert ihook_a is not None assert ihook_a is not None
config.pluginmanager._importconftest(conftest2, importmode="prepend") config.pluginmanager._importconftest(conftest2, importmode="prepend")
ihook_b = session.gethookproxy(testdir.tmpdir.join("tests")) ihook_b = session.gethookproxy(pytester.path / "tests")
assert ihook_a is not ihook_b assert ihook_a is not ihook_b
def test_hook_with_addoption(self, testdir): def test_hook_with_addoption(self, testdir):