Merge pull request #8174 from bluetech/py-to-pathlib-5
More py.path -> pathlib conversions
This commit is contained in:
commit
d8d2df7e6f
|
@ -0,0 +1,6 @@
|
||||||
|
The following changes have been made to internal pytest types/functions:
|
||||||
|
|
||||||
|
- The ``path`` property of ``_pytest.code.Code`` returns ``Path`` instead of ``py.path.local``.
|
||||||
|
- The ``path`` property of ``_pytest.code.TracebackEntry`` returns ``Path`` instead of ``py.path.local``.
|
||||||
|
- The ``_pytest.code.getfslineno()`` function returns ``Path`` instead of ``py.path.local``.
|
||||||
|
- The ``_pytest.python.path_matches_patterns()`` function takes ``Path`` instead of ``py.path.local``.
|
|
@ -43,6 +43,8 @@ from _pytest._io.saferepr import safeformat
|
||||||
from _pytest._io.saferepr import saferepr
|
from _pytest._io.saferepr import saferepr
|
||||||
from _pytest.compat import final
|
from _pytest.compat import final
|
||||||
from _pytest.compat import get_real_func
|
from _pytest.compat import get_real_func
|
||||||
|
from _pytest.pathlib import absolutepath
|
||||||
|
from _pytest.pathlib import bestrelpath
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
@ -78,16 +80,16 @@ class Code:
|
||||||
return self.raw.co_name
|
return self.raw.co_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self) -> Union[py.path.local, str]:
|
def path(self) -> Union[Path, str]:
|
||||||
"""Return a path object pointing to source code, or an ``str`` in
|
"""Return a path object pointing to source code, or an ``str`` in
|
||||||
case of ``OSError`` / non-existing file."""
|
case of ``OSError`` / non-existing file."""
|
||||||
if not self.raw.co_filename:
|
if not self.raw.co_filename:
|
||||||
return ""
|
return ""
|
||||||
try:
|
try:
|
||||||
p = py.path.local(self.raw.co_filename)
|
p = absolutepath(self.raw.co_filename)
|
||||||
# maybe don't try this checking
|
# maybe don't try this checking
|
||||||
if not p.check():
|
if not p.exists():
|
||||||
raise OSError("py.path check failed.")
|
raise OSError("path check failed.")
|
||||||
return p
|
return p
|
||||||
except OSError:
|
except OSError:
|
||||||
# XXX maybe try harder like the weird logic
|
# XXX maybe try harder like the weird logic
|
||||||
|
@ -223,7 +225,7 @@ class TracebackEntry:
|
||||||
return source.getstatement(self.lineno)
|
return source.getstatement(self.lineno)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self) -> Union[py.path.local, str]:
|
def path(self) -> Union[Path, str]:
|
||||||
"""Path to the source code."""
|
"""Path to the source code."""
|
||||||
return self.frame.code.path
|
return self.frame.code.path
|
||||||
|
|
||||||
|
@ -336,10 +338,10 @@ class Traceback(List[TracebackEntry]):
|
||||||
|
|
||||||
def cut(
|
def cut(
|
||||||
self,
|
self,
|
||||||
path=None,
|
path: Optional[Union[Path, str]] = None,
|
||||||
lineno: Optional[int] = None,
|
lineno: Optional[int] = None,
|
||||||
firstlineno: Optional[int] = None,
|
firstlineno: Optional[int] = None,
|
||||||
excludepath: Optional[py.path.local] = None,
|
excludepath: Optional[Path] = None,
|
||||||
) -> "Traceback":
|
) -> "Traceback":
|
||||||
"""Return a Traceback instance wrapping part of this Traceback.
|
"""Return a Traceback instance wrapping part of this Traceback.
|
||||||
|
|
||||||
|
@ -353,17 +355,19 @@ class Traceback(List[TracebackEntry]):
|
||||||
for x in self:
|
for x in self:
|
||||||
code = x.frame.code
|
code = x.frame.code
|
||||||
codepath = code.path
|
codepath = code.path
|
||||||
|
if path is not None and codepath != path:
|
||||||
|
continue
|
||||||
if (
|
if (
|
||||||
(path is None or codepath == path)
|
excludepath is not None
|
||||||
and (
|
and isinstance(codepath, Path)
|
||||||
excludepath is None
|
and excludepath in codepath.parents
|
||||||
or not isinstance(codepath, py.path.local)
|
|
||||||
or not codepath.relto(excludepath)
|
|
||||||
)
|
|
||||||
and (lineno is None or x.lineno == lineno)
|
|
||||||
and (firstlineno is None or x.frame.code.firstlineno == firstlineno)
|
|
||||||
):
|
):
|
||||||
return Traceback(x._rawentry, self._excinfo)
|
continue
|
||||||
|
if lineno is not None and x.lineno != lineno:
|
||||||
|
continue
|
||||||
|
if firstlineno is not None and x.frame.code.firstlineno != firstlineno:
|
||||||
|
continue
|
||||||
|
return Traceback(x._rawentry, self._excinfo)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
|
@ -801,7 +805,8 @@ class FormattedExcinfo:
|
||||||
message = "in %s" % (entry.name)
|
message = "in %s" % (entry.name)
|
||||||
else:
|
else:
|
||||||
message = excinfo and excinfo.typename or ""
|
message = excinfo and excinfo.typename or ""
|
||||||
path = self._makepath(entry.path)
|
entry_path = entry.path
|
||||||
|
path = self._makepath(entry_path)
|
||||||
reprfileloc = ReprFileLocation(path, entry.lineno + 1, message)
|
reprfileloc = ReprFileLocation(path, entry.lineno + 1, message)
|
||||||
localsrepr = self.repr_locals(entry.locals)
|
localsrepr = self.repr_locals(entry.locals)
|
||||||
return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
|
return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
|
||||||
|
@ -814,15 +819,15 @@ class FormattedExcinfo:
|
||||||
lines.extend(self.get_exconly(excinfo, indent=4))
|
lines.extend(self.get_exconly(excinfo, indent=4))
|
||||||
return ReprEntry(lines, None, None, None, style)
|
return ReprEntry(lines, None, None, None, style)
|
||||||
|
|
||||||
def _makepath(self, path):
|
def _makepath(self, path: Union[Path, str]) -> str:
|
||||||
if not self.abspath:
|
if not self.abspath and isinstance(path, Path):
|
||||||
try:
|
try:
|
||||||
np = py.path.local().bestrelpath(path)
|
np = bestrelpath(Path.cwd(), path)
|
||||||
except OSError:
|
except OSError:
|
||||||
return path
|
return str(path)
|
||||||
if len(np) < len(str(path)):
|
if len(np) < len(str(path)):
|
||||||
path = np
|
return np
|
||||||
return path
|
return str(path)
|
||||||
|
|
||||||
def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback":
|
def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback":
|
||||||
traceback = excinfo.traceback
|
traceback = excinfo.traceback
|
||||||
|
@ -1181,7 +1186,7 @@ class ReprFuncArgs(TerminalRepr):
|
||||||
tw.line("")
|
tw.line("")
|
||||||
|
|
||||||
|
|
||||||
def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]:
|
def getfslineno(obj: object) -> Tuple[Union[str, Path], int]:
|
||||||
"""Return source location (path, lineno) for the given object.
|
"""Return source location (path, lineno) for the given object.
|
||||||
|
|
||||||
If the source cannot be determined return ("", -1).
|
If the source cannot be determined return ("", -1).
|
||||||
|
@ -1203,7 +1208,7 @@ def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]:
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return "", -1
|
return "", -1
|
||||||
|
|
||||||
fspath = fn and py.path.local(fn) or ""
|
fspath = fn and absolutepath(fn) or ""
|
||||||
lineno = -1
|
lineno = -1
|
||||||
if fspath:
|
if fspath:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -128,7 +128,7 @@ def filter_traceback_for_conftest_import_failure(
|
||||||
|
|
||||||
|
|
||||||
def main(
|
def main(
|
||||||
args: Optional[Union[List[str], py.path.local]] = None,
|
args: Optional[Union[List[str], "os.PathLike[str]"]] = None,
|
||||||
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None,
|
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None,
|
||||||
) -> Union[int, ExitCode]:
|
) -> Union[int, ExitCode]:
|
||||||
"""Perform an in-process test run.
|
"""Perform an in-process test run.
|
||||||
|
@ -295,13 +295,15 @@ def get_plugin_manager() -> "PytestPluginManager":
|
||||||
|
|
||||||
|
|
||||||
def _prepareconfig(
|
def _prepareconfig(
|
||||||
args: Optional[Union[py.path.local, List[str]]] = None,
|
args: Optional[Union[List[str], "os.PathLike[str]"]] = None,
|
||||||
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None,
|
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None,
|
||||||
) -> "Config":
|
) -> "Config":
|
||||||
if args is None:
|
if args is None:
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
elif isinstance(args, py.path.local):
|
# TODO: Remove type-ignore after next mypy release.
|
||||||
args = [str(args)]
|
# https://github.com/python/typeshed/commit/076983eec45e739c68551cb6119fd7d85fd4afa9
|
||||||
|
elif isinstance(args, os.PathLike): # type: ignore[misc]
|
||||||
|
args = [os.fspath(args)]
|
||||||
elif not isinstance(args, list):
|
elif not isinstance(args, list):
|
||||||
msg = "`args` parameter expected to be a list of strings, got: {!r} (type: {})"
|
msg = "`args` parameter expected to be a list of strings, got: {!r} (type: {})"
|
||||||
raise TypeError(msg.format(args, type(args)))
|
raise TypeError(msg.format(args, type(args)))
|
||||||
|
@ -346,7 +348,7 @@ class PytestPluginManager(PluginManager):
|
||||||
self._conftestpath2mod: Dict[Path, types.ModuleType] = {}
|
self._conftestpath2mod: Dict[Path, types.ModuleType] = {}
|
||||||
self._confcutdir: Optional[Path] = None
|
self._confcutdir: Optional[Path] = None
|
||||||
self._noconftest = False
|
self._noconftest = False
|
||||||
self._duplicatepaths: Set[py.path.local] = set()
|
self._duplicatepaths: Set[Path] = set()
|
||||||
|
|
||||||
# plugins that were explicitly skipped with pytest.skip
|
# plugins that were explicitly skipped with pytest.skip
|
||||||
# list of (module name, skip reason)
|
# list of (module name, skip reason)
|
||||||
|
|
|
@ -36,6 +36,7 @@ from _pytest.config.argparsing import Parser
|
||||||
from _pytest.fixtures import FixtureRequest
|
from _pytest.fixtures import FixtureRequest
|
||||||
from _pytest.nodes import Collector
|
from _pytest.nodes import Collector
|
||||||
from _pytest.outcomes import OutcomeException
|
from _pytest.outcomes import OutcomeException
|
||||||
|
from _pytest.pathlib import fnmatch_ex
|
||||||
from _pytest.pathlib import import_path
|
from _pytest.pathlib import import_path
|
||||||
from _pytest.python_api import approx
|
from _pytest.python_api import approx
|
||||||
from _pytest.warning_types import PytestWarning
|
from _pytest.warning_types import PytestWarning
|
||||||
|
@ -120,32 +121,32 @@ def pytest_unconfigure() -> None:
|
||||||
|
|
||||||
|
|
||||||
def pytest_collect_file(
|
def pytest_collect_file(
|
||||||
path: py.path.local, parent: Collector,
|
fspath: Path, path: py.path.local, parent: Collector,
|
||||||
) -> Optional[Union["DoctestModule", "DoctestTextfile"]]:
|
) -> Optional[Union["DoctestModule", "DoctestTextfile"]]:
|
||||||
config = parent.config
|
config = parent.config
|
||||||
if path.ext == ".py":
|
if fspath.suffix == ".py":
|
||||||
if config.option.doctestmodules and not _is_setup_py(path):
|
if config.option.doctestmodules and not _is_setup_py(fspath):
|
||||||
mod: DoctestModule = DoctestModule.from_parent(parent, fspath=path)
|
mod: DoctestModule = DoctestModule.from_parent(parent, fspath=path)
|
||||||
return mod
|
return mod
|
||||||
elif _is_doctest(config, path, parent):
|
elif _is_doctest(config, fspath, parent):
|
||||||
txt: DoctestTextfile = DoctestTextfile.from_parent(parent, fspath=path)
|
txt: DoctestTextfile = DoctestTextfile.from_parent(parent, fspath=path)
|
||||||
return txt
|
return txt
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _is_setup_py(path: py.path.local) -> bool:
|
def _is_setup_py(path: Path) -> bool:
|
||||||
if path.basename != "setup.py":
|
if path.name != "setup.py":
|
||||||
return False
|
return False
|
||||||
contents = path.read_binary()
|
contents = path.read_bytes()
|
||||||
return b"setuptools" in contents or b"distutils" in contents
|
return b"setuptools" in contents or b"distutils" in contents
|
||||||
|
|
||||||
|
|
||||||
def _is_doctest(config: Config, path: py.path.local, parent) -> bool:
|
def _is_doctest(config: Config, path: Path, parent: Collector) -> bool:
|
||||||
if path.ext in (".txt", ".rst") and parent.session.isinitpath(path):
|
if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path):
|
||||||
return True
|
return True
|
||||||
globs = config.getoption("doctestglob") or ["test*.txt"]
|
globs = config.getoption("doctestglob") or ["test*.txt"]
|
||||||
for glob in globs:
|
for glob in globs:
|
||||||
if path.check(fnmatch=glob):
|
if fnmatch_ex(glob, path):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import sys
|
||||||
import warnings
|
import warnings
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from pathlib import Path
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
@ -58,6 +59,7 @@ from _pytest.mark.structures import MarkDecorator
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.outcomes import TEST_OUTCOME
|
from _pytest.outcomes import TEST_OUTCOME
|
||||||
from _pytest.pathlib import absolutepath
|
from _pytest.pathlib import absolutepath
|
||||||
|
from _pytest.pathlib import bestrelpath
|
||||||
from _pytest.store import StoreKey
|
from _pytest.store import StoreKey
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -718,7 +720,11 @@ class FixtureRequest:
|
||||||
for fixturedef in self._get_fixturestack():
|
for fixturedef in self._get_fixturestack():
|
||||||
factory = fixturedef.func
|
factory = fixturedef.func
|
||||||
fs, lineno = getfslineno(factory)
|
fs, lineno = getfslineno(factory)
|
||||||
p = self._pyfuncitem.session.fspath.bestrelpath(fs)
|
if isinstance(fs, Path):
|
||||||
|
session: Session = self._pyfuncitem.session
|
||||||
|
p = bestrelpath(Path(session.fspath), fs)
|
||||||
|
else:
|
||||||
|
p = fs
|
||||||
args = _format_args(factory)
|
args = _format_args(factory)
|
||||||
lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args))
|
lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args))
|
||||||
return lines
|
return lines
|
||||||
|
|
|
@ -37,6 +37,7 @@ from _pytest.fixtures import FixtureManager
|
||||||
from _pytest.outcomes import exit
|
from _pytest.outcomes import exit
|
||||||
from _pytest.pathlib import absolutepath
|
from _pytest.pathlib import absolutepath
|
||||||
from _pytest.pathlib import bestrelpath
|
from _pytest.pathlib import bestrelpath
|
||||||
|
from _pytest.pathlib import fnmatch_ex
|
||||||
from _pytest.pathlib import visit
|
from _pytest.pathlib import visit
|
||||||
from _pytest.reports import CollectReport
|
from _pytest.reports import CollectReport
|
||||||
from _pytest.reports import TestReport
|
from _pytest.reports import TestReport
|
||||||
|
@ -353,11 +354,14 @@ def pytest_runtestloop(session: "Session") -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _in_venv(path: py.path.local) -> bool:
|
def _in_venv(path: Path) -> bool:
|
||||||
"""Attempt to detect if ``path`` is the root of a Virtual Environment by
|
"""Attempt to detect if ``path`` is the root of a Virtual Environment by
|
||||||
checking for the existence of the appropriate activate script."""
|
checking for the existence of the appropriate activate script."""
|
||||||
bindir = path.join("Scripts" if sys.platform.startswith("win") else "bin")
|
bindir = path.joinpath("Scripts" if sys.platform.startswith("win") else "bin")
|
||||||
if not bindir.isdir():
|
try:
|
||||||
|
if not bindir.is_dir():
|
||||||
|
return False
|
||||||
|
except OSError:
|
||||||
return False
|
return False
|
||||||
activates = (
|
activates = (
|
||||||
"activate",
|
"activate",
|
||||||
|
@ -367,33 +371,32 @@ def _in_venv(path: py.path.local) -> bool:
|
||||||
"Activate.bat",
|
"Activate.bat",
|
||||||
"Activate.ps1",
|
"Activate.ps1",
|
||||||
)
|
)
|
||||||
return any([fname.basename in activates for fname in bindir.listdir()])
|
return any(fname.name in activates for fname in bindir.iterdir())
|
||||||
|
|
||||||
|
|
||||||
def pytest_ignore_collect(path: py.path.local, config: Config) -> Optional[bool]:
|
def pytest_ignore_collect(fspath: Path, config: Config) -> Optional[bool]:
|
||||||
path_ = Path(path)
|
ignore_paths = config._getconftest_pathlist("collect_ignore", path=fspath.parent)
|
||||||
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(absolutepath(x) for x in excludeopt)
|
ignore_paths.extend(absolutepath(x) for x in excludeopt)
|
||||||
|
|
||||||
if path_ in ignore_paths:
|
if fspath in ignore_paths:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
ignore_globs = config._getconftest_pathlist(
|
ignore_globs = config._getconftest_pathlist(
|
||||||
"collect_ignore_glob", path=path_.parent
|
"collect_ignore_glob", path=fspath.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(absolutepath(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(fspath), str(glob)) for glob in ignore_globs):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
allow_in_venv = config.getoption("collect_in_virtualenv")
|
allow_in_venv = config.getoption("collect_in_virtualenv")
|
||||||
if not allow_in_venv and _in_venv(path):
|
if not allow_in_venv and _in_venv(fspath):
|
||||||
return True
|
return True
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -538,21 +541,21 @@ class Session(nodes.FSCollector):
|
||||||
if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config):
|
if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config):
|
||||||
return False
|
return False
|
||||||
norecursepatterns = self.config.getini("norecursedirs")
|
norecursepatterns = self.config.getini("norecursedirs")
|
||||||
if any(path.check(fnmatch=pat) for pat in norecursepatterns):
|
if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _collectfile(
|
def _collectfile(
|
||||||
self, path: py.path.local, handle_dupes: bool = True
|
self, fspath: Path, handle_dupes: bool = True
|
||||||
) -> Sequence[nodes.Collector]:
|
) -> Sequence[nodes.Collector]:
|
||||||
fspath = Path(path)
|
path = py.path.local(fspath)
|
||||||
assert (
|
assert (
|
||||||
path.isfile()
|
fspath.is_file()
|
||||||
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
|
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
|
||||||
path, path.isdir(), path.exists(), path.islink()
|
fspath, fspath.is_dir(), fspath.exists(), fspath.is_symlink()
|
||||||
)
|
)
|
||||||
ihook = self.gethookproxy(path)
|
ihook = self.gethookproxy(fspath)
|
||||||
if not self.isinitpath(path):
|
if not self.isinitpath(fspath):
|
||||||
if ihook.pytest_ignore_collect(
|
if ihook.pytest_ignore_collect(
|
||||||
fspath=fspath, path=path, config=self.config
|
fspath=fspath, path=path, config=self.config
|
||||||
):
|
):
|
||||||
|
@ -562,10 +565,10 @@ class Session(nodes.FSCollector):
|
||||||
keepduplicates = self.config.getoption("keepduplicates")
|
keepduplicates = self.config.getoption("keepduplicates")
|
||||||
if not keepduplicates:
|
if not keepduplicates:
|
||||||
duplicate_paths = self.config.pluginmanager._duplicatepaths
|
duplicate_paths = self.config.pluginmanager._duplicatepaths
|
||||||
if path in duplicate_paths:
|
if fspath in duplicate_paths:
|
||||||
return ()
|
return ()
|
||||||
else:
|
else:
|
||||||
duplicate_paths.add(path)
|
duplicate_paths.add(fspath)
|
||||||
|
|
||||||
return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return]
|
return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return]
|
||||||
|
|
||||||
|
@ -652,10 +655,8 @@ class Session(nodes.FSCollector):
|
||||||
from _pytest.python import Package
|
from _pytest.python import Package
|
||||||
|
|
||||||
# Keep track of any collected nodes in here, so we don't duplicate fixtures.
|
# Keep track of any collected nodes in here, so we don't duplicate fixtures.
|
||||||
node_cache1: Dict[py.path.local, Sequence[nodes.Collector]] = {}
|
node_cache1: Dict[Path, Sequence[nodes.Collector]] = {}
|
||||||
node_cache2: Dict[
|
node_cache2: Dict[Tuple[Type[nodes.Collector], Path], nodes.Collector] = ({})
|
||||||
Tuple[Type[nodes.Collector], py.path.local], nodes.Collector
|
|
||||||
] = ({})
|
|
||||||
|
|
||||||
# Keep track of any collected collectors in matchnodes paths, so they
|
# Keep track of any collected collectors in matchnodes paths, so they
|
||||||
# are not collected more than once.
|
# are not collected more than once.
|
||||||
|
@ -679,31 +680,31 @@ class Session(nodes.FSCollector):
|
||||||
break
|
break
|
||||||
|
|
||||||
if parent.is_dir():
|
if parent.is_dir():
|
||||||
pkginit = py.path.local(parent / "__init__.py")
|
pkginit = parent / "__init__.py"
|
||||||
if pkginit.isfile() and pkginit not in node_cache1:
|
if pkginit.is_file() and pkginit not in node_cache1:
|
||||||
col = self._collectfile(pkginit, handle_dupes=False)
|
col = self._collectfile(pkginit, handle_dupes=False)
|
||||||
if col:
|
if col:
|
||||||
if isinstance(col[0], Package):
|
if isinstance(col[0], Package):
|
||||||
pkg_roots[str(parent)] = col[0]
|
pkg_roots[str(parent)] = col[0]
|
||||||
node_cache1[col[0].fspath] = [col[0]]
|
node_cache1[Path(col[0].fspath)] = [col[0]]
|
||||||
|
|
||||||
# If it's a directory argument, recurse and look for any Subpackages.
|
# If it's a directory argument, recurse and look for any Subpackages.
|
||||||
# Let the Package collector deal with subnodes, don't collect here.
|
# Let the Package collector deal with subnodes, don't collect here.
|
||||||
if argpath.is_dir():
|
if argpath.is_dir():
|
||||||
assert not names, "invalid arg {!r}".format((argpath, names))
|
assert not names, "invalid arg {!r}".format((argpath, names))
|
||||||
|
|
||||||
seen_dirs: Set[py.path.local] = set()
|
seen_dirs: Set[Path] = set()
|
||||||
for direntry in visit(str(argpath), self._recurse):
|
for direntry in visit(str(argpath), self._recurse):
|
||||||
if not direntry.is_file():
|
if not direntry.is_file():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
path = py.path.local(direntry.path)
|
path = Path(direntry.path)
|
||||||
dirpath = path.dirpath()
|
dirpath = path.parent
|
||||||
|
|
||||||
if dirpath not in seen_dirs:
|
if dirpath not in seen_dirs:
|
||||||
# Collect packages first.
|
# Collect packages first.
|
||||||
seen_dirs.add(dirpath)
|
seen_dirs.add(dirpath)
|
||||||
pkginit = dirpath.join("__init__.py")
|
pkginit = dirpath / "__init__.py"
|
||||||
if pkginit.exists():
|
if pkginit.exists():
|
||||||
for x in self._collectfile(pkginit):
|
for x in self._collectfile(pkginit):
|
||||||
yield x
|
yield x
|
||||||
|
@ -714,23 +715,22 @@ class Session(nodes.FSCollector):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for x in self._collectfile(path):
|
for x in self._collectfile(path):
|
||||||
key = (type(x), x.fspath)
|
key2 = (type(x), Path(x.fspath))
|
||||||
if key in node_cache2:
|
if key2 in node_cache2:
|
||||||
yield node_cache2[key]
|
yield node_cache2[key2]
|
||||||
else:
|
else:
|
||||||
node_cache2[key] = x
|
node_cache2[key2] = x
|
||||||
yield x
|
yield x
|
||||||
else:
|
else:
|
||||||
assert argpath.is_file()
|
assert argpath.is_file()
|
||||||
|
|
||||||
argpath_ = py.path.local(argpath)
|
if argpath in node_cache1:
|
||||||
if argpath_ in node_cache1:
|
col = node_cache1[argpath]
|
||||||
col = node_cache1[argpath_]
|
|
||||||
else:
|
else:
|
||||||
collect_root = pkg_roots.get(argpath_.dirname, self)
|
collect_root = pkg_roots.get(str(argpath.parent), self)
|
||||||
col = collect_root._collectfile(argpath_, handle_dupes=False)
|
col = collect_root._collectfile(argpath, handle_dupes=False)
|
||||||
if col:
|
if col:
|
||||||
node_cache1[argpath_] = col
|
node_cache1[argpath] = col
|
||||||
|
|
||||||
matching = []
|
matching = []
|
||||||
work: List[
|
work: List[
|
||||||
|
@ -846,7 +846,7 @@ def resolve_collection_argument(
|
||||||
|
|
||||||
This function ensures the path exists, and returns a tuple:
|
This function ensures the path exists, and returns a tuple:
|
||||||
|
|
||||||
(py.path.path("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"])
|
(Path("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"])
|
||||||
|
|
||||||
When as_pypath is True, expects that the command-line argument actually contains
|
When as_pypath is True, expects that the command-line argument actually contains
|
||||||
module paths instead of file-system paths:
|
module paths instead of file-system paths:
|
||||||
|
|
|
@ -39,7 +39,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
SEP = "/"
|
SEP = "/"
|
||||||
|
|
||||||
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
|
tracebackcutdir = Path(_pytest.__file__).parent
|
||||||
|
|
||||||
|
|
||||||
def iterparentnodeids(nodeid: str) -> Iterator[str]:
|
def iterparentnodeids(nodeid: str) -> Iterator[str]:
|
||||||
|
@ -416,9 +416,7 @@ class Node(metaclass=NodeMeta):
|
||||||
return self._repr_failure_py(excinfo, style)
|
return self._repr_failure_py(excinfo, style)
|
||||||
|
|
||||||
|
|
||||||
def get_fslocation_from_item(
|
def get_fslocation_from_item(node: "Node") -> Tuple[Union[str, Path], Optional[int]]:
|
||||||
node: "Node",
|
|
||||||
) -> Tuple[Union[str, py.path.local], Optional[int]]:
|
|
||||||
"""Try to extract the actual location from a node, depending on available attributes:
|
"""Try to extract the actual location from a node, depending on available attributes:
|
||||||
|
|
||||||
* "location": a pair (path, lineno)
|
* "location": a pair (path, lineno)
|
||||||
|
@ -474,7 +472,7 @@ class Collector(Node):
|
||||||
def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None:
|
def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None:
|
||||||
if hasattr(self, "fspath"):
|
if hasattr(self, "fspath"):
|
||||||
traceback = excinfo.traceback
|
traceback = excinfo.traceback
|
||||||
ntraceback = traceback.cut(path=self.fspath)
|
ntraceback = traceback.cut(path=Path(self.fspath))
|
||||||
if ntraceback == traceback:
|
if ntraceback == traceback:
|
||||||
ntraceback = ntraceback.cut(excludepath=tracebackcutdir)
|
ntraceback = ntraceback.cut(excludepath=tracebackcutdir)
|
||||||
excinfo.traceback = ntraceback.filter()
|
excinfo.traceback = ntraceback.filter()
|
||||||
|
|
|
@ -387,7 +387,7 @@ def resolve_from_str(input: str, rootpath: Path) -> Path:
|
||||||
return rootpath.joinpath(input)
|
return rootpath.joinpath(input)
|
||||||
|
|
||||||
|
|
||||||
def fnmatch_ex(pattern: str, path) -> bool:
|
def fnmatch_ex(pattern: str, path: Union[str, "os.PathLike[str]"]) -> bool:
|
||||||
"""A port of FNMatcher from py.path.common which works with PurePath() instances.
|
"""A port of FNMatcher from py.path.common which works with PurePath() instances.
|
||||||
|
|
||||||
The difference between this algorithm and PurePath.match() is that the
|
The difference between this algorithm and PurePath.match() is that the
|
||||||
|
|
|
@ -66,6 +66,8 @@ from _pytest.mark.structures import MarkDecorator
|
||||||
from _pytest.mark.structures import normalize_mark_list
|
from _pytest.mark.structures import normalize_mark_list
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.outcomes import skip
|
from _pytest.outcomes import skip
|
||||||
|
from _pytest.pathlib import bestrelpath
|
||||||
|
from _pytest.pathlib import fnmatch_ex
|
||||||
from _pytest.pathlib import import_path
|
from _pytest.pathlib import import_path
|
||||||
from _pytest.pathlib import ImportPathMismatchError
|
from _pytest.pathlib import ImportPathMismatchError
|
||||||
from _pytest.pathlib import parts
|
from _pytest.pathlib import parts
|
||||||
|
@ -190,11 +192,10 @@ def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]:
|
||||||
def pytest_collect_file(
|
def pytest_collect_file(
|
||||||
fspath: Path, path: py.path.local, parent: nodes.Collector
|
fspath: Path, path: py.path.local, parent: nodes.Collector
|
||||||
) -> Optional["Module"]:
|
) -> Optional["Module"]:
|
||||||
ext = path.ext
|
if fspath.suffix == ".py":
|
||||||
if ext == ".py":
|
|
||||||
if not parent.session.isinitpath(fspath):
|
if not parent.session.isinitpath(fspath):
|
||||||
if not path_matches_patterns(
|
if not path_matches_patterns(
|
||||||
path, parent.config.getini("python_files") + ["__init__.py"]
|
fspath, parent.config.getini("python_files") + ["__init__.py"]
|
||||||
):
|
):
|
||||||
return None
|
return None
|
||||||
ihook = parent.session.gethookproxy(fspath)
|
ihook = parent.session.gethookproxy(fspath)
|
||||||
|
@ -205,13 +206,13 @@ def pytest_collect_file(
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def path_matches_patterns(path: py.path.local, patterns: Iterable[str]) -> bool:
|
def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool:
|
||||||
"""Return whether path matches any of the patterns in the list of globs given."""
|
"""Return whether path matches any of the patterns in the list of globs given."""
|
||||||
return any(path.fnmatch(pattern) for pattern in patterns)
|
return any(fnmatch_ex(pattern, path) for pattern in patterns)
|
||||||
|
|
||||||
|
|
||||||
def pytest_pycollect_makemodule(path: py.path.local, parent) -> "Module":
|
def pytest_pycollect_makemodule(fspath: Path, path: py.path.local, parent) -> "Module":
|
||||||
if path.basename == "__init__.py":
|
if fspath.name == "__init__.py":
|
||||||
pkg: Package = Package.from_parent(parent, fspath=path)
|
pkg: Package = Package.from_parent(parent, fspath=path)
|
||||||
return pkg
|
return pkg
|
||||||
mod: Module = Module.from_parent(parent, fspath=path)
|
mod: Module = Module.from_parent(parent, fspath=path)
|
||||||
|
@ -340,7 +341,11 @@ class PyobjMixin:
|
||||||
fspath: Union[py.path.local, str] = file_path
|
fspath: Union[py.path.local, str] = file_path
|
||||||
lineno = compat_co_firstlineno
|
lineno = compat_co_firstlineno
|
||||||
else:
|
else:
|
||||||
fspath, lineno = getfslineno(obj)
|
path, lineno = getfslineno(obj)
|
||||||
|
if isinstance(path, Path):
|
||||||
|
fspath = py.path.local(path)
|
||||||
|
else:
|
||||||
|
fspath = path
|
||||||
modpath = self.getmodpath()
|
modpath = self.getmodpath()
|
||||||
assert isinstance(lineno, int)
|
assert isinstance(lineno, int)
|
||||||
return fspath, lineno, modpath
|
return fspath, lineno, modpath
|
||||||
|
@ -673,21 +678,21 @@ class Package(Module):
|
||||||
if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config):
|
if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config):
|
||||||
return False
|
return False
|
||||||
norecursepatterns = self.config.getini("norecursedirs")
|
norecursepatterns = self.config.getini("norecursedirs")
|
||||||
if any(path.check(fnmatch=pat) for pat in norecursepatterns):
|
if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _collectfile(
|
def _collectfile(
|
||||||
self, path: py.path.local, handle_dupes: bool = True
|
self, fspath: Path, handle_dupes: bool = True
|
||||||
) -> Sequence[nodes.Collector]:
|
) -> Sequence[nodes.Collector]:
|
||||||
fspath = Path(path)
|
path = py.path.local(fspath)
|
||||||
assert (
|
assert (
|
||||||
path.isfile()
|
fspath.is_file()
|
||||||
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
|
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
|
||||||
path, path.isdir(), path.exists(), path.islink()
|
path, fspath.is_dir(), fspath.exists(), fspath.is_symlink()
|
||||||
)
|
)
|
||||||
ihook = self.session.gethookproxy(path)
|
ihook = self.session.gethookproxy(fspath)
|
||||||
if not self.session.isinitpath(path):
|
if not self.session.isinitpath(fspath):
|
||||||
if ihook.pytest_ignore_collect(
|
if ihook.pytest_ignore_collect(
|
||||||
fspath=fspath, path=path, config=self.config
|
fspath=fspath, path=path, config=self.config
|
||||||
):
|
):
|
||||||
|
@ -697,32 +702,32 @@ class Package(Module):
|
||||||
keepduplicates = self.config.getoption("keepduplicates")
|
keepduplicates = self.config.getoption("keepduplicates")
|
||||||
if not keepduplicates:
|
if not keepduplicates:
|
||||||
duplicate_paths = self.config.pluginmanager._duplicatepaths
|
duplicate_paths = self.config.pluginmanager._duplicatepaths
|
||||||
if path in duplicate_paths:
|
if fspath in duplicate_paths:
|
||||||
return ()
|
return ()
|
||||||
else:
|
else:
|
||||||
duplicate_paths.add(path)
|
duplicate_paths.add(fspath)
|
||||||
|
|
||||||
return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return]
|
return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return]
|
||||||
|
|
||||||
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
|
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
|
||||||
this_path = self.fspath.dirpath()
|
this_path = Path(self.fspath).parent
|
||||||
init_module = this_path.join("__init__.py")
|
init_module = this_path / "__init__.py"
|
||||||
if init_module.check(file=1) and path_matches_patterns(
|
if init_module.is_file() and path_matches_patterns(
|
||||||
init_module, self.config.getini("python_files")
|
init_module, self.config.getini("python_files")
|
||||||
):
|
):
|
||||||
yield Module.from_parent(self, fspath=init_module)
|
yield Module.from_parent(self, fspath=py.path.local(init_module))
|
||||||
pkg_prefixes: Set[py.path.local] = set()
|
pkg_prefixes: Set[Path] = set()
|
||||||
for direntry in visit(str(this_path), recurse=self._recurse):
|
for direntry in visit(str(this_path), recurse=self._recurse):
|
||||||
path = py.path.local(direntry.path)
|
path = Path(direntry.path)
|
||||||
|
|
||||||
# We will visit our own __init__.py file, in which case we skip it.
|
# We will visit our own __init__.py file, in which case we skip it.
|
||||||
if direntry.is_file():
|
if direntry.is_file():
|
||||||
if direntry.name == "__init__.py" and path.dirpath() == this_path:
|
if direntry.name == "__init__.py" and path.parent == this_path:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
parts_ = parts(direntry.path)
|
parts_ = parts(direntry.path)
|
||||||
if any(
|
if any(
|
||||||
str(pkg_prefix) in parts_ and pkg_prefix.join("__init__.py") != path
|
str(pkg_prefix) in parts_ and pkg_prefix / "__init__.py" != path
|
||||||
for pkg_prefix in pkg_prefixes
|
for pkg_prefix in pkg_prefixes
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
@ -732,7 +737,7 @@ class Package(Module):
|
||||||
elif not direntry.is_dir():
|
elif not direntry.is_dir():
|
||||||
# Broken symlink or invalid/missing file.
|
# Broken symlink or invalid/missing file.
|
||||||
continue
|
continue
|
||||||
elif path.join("__init__.py").check(file=1):
|
elif path.joinpath("__init__.py").is_file():
|
||||||
pkg_prefixes.add(path)
|
pkg_prefixes.add(path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1412,13 +1417,13 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None:
|
||||||
import _pytest.config
|
import _pytest.config
|
||||||
|
|
||||||
session.perform_collect()
|
session.perform_collect()
|
||||||
curdir = py.path.local()
|
curdir = Path.cwd()
|
||||||
tw = _pytest.config.create_terminal_writer(config)
|
tw = _pytest.config.create_terminal_writer(config)
|
||||||
verbose = config.getvalue("verbose")
|
verbose = config.getvalue("verbose")
|
||||||
|
|
||||||
def get_best_relpath(func):
|
def get_best_relpath(func) -> str:
|
||||||
loc = getlocation(func, str(curdir))
|
loc = getlocation(func, str(curdir))
|
||||||
return curdir.bestrelpath(py.path.local(loc))
|
return bestrelpath(curdir, Path(loc))
|
||||||
|
|
||||||
def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None:
|
def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None:
|
||||||
argname = fixture_def.argname
|
argname = fixture_def.argname
|
||||||
|
@ -1468,7 +1473,7 @@ def _showfixtures_main(config: Config, session: Session) -> None:
|
||||||
import _pytest.config
|
import _pytest.config
|
||||||
|
|
||||||
session.perform_collect()
|
session.perform_collect()
|
||||||
curdir = py.path.local()
|
curdir = Path.cwd()
|
||||||
tw = _pytest.config.create_terminal_writer(config)
|
tw = _pytest.config.create_terminal_writer(config)
|
||||||
verbose = config.getvalue("verbose")
|
verbose = config.getvalue("verbose")
|
||||||
|
|
||||||
|
@ -1490,7 +1495,7 @@ def _showfixtures_main(config: Config, session: Session) -> None:
|
||||||
(
|
(
|
||||||
len(fixturedef.baseid),
|
len(fixturedef.baseid),
|
||||||
fixturedef.func.__module__,
|
fixturedef.func.__module__,
|
||||||
curdir.bestrelpath(py.path.local(loc)),
|
bestrelpath(curdir, Path(loc)),
|
||||||
fixturedef.argname,
|
fixturedef.argname,
|
||||||
fixturedef,
|
fixturedef,
|
||||||
)
|
)
|
||||||
|
|
|
@ -285,15 +285,13 @@ class WarningReport:
|
||||||
User friendly message about the warning.
|
User friendly message about the warning.
|
||||||
:ivar str|None nodeid:
|
:ivar str|None nodeid:
|
||||||
nodeid that generated the warning (see ``get_location``).
|
nodeid that generated the warning (see ``get_location``).
|
||||||
:ivar tuple|py.path.local fslocation:
|
:ivar tuple fslocation:
|
||||||
File system location of the source of the warning (see ``get_location``).
|
File system location of the source of the warning (see ``get_location``).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message = attr.ib(type=str)
|
message = attr.ib(type=str)
|
||||||
nodeid = attr.ib(type=Optional[str], default=None)
|
nodeid = attr.ib(type=Optional[str], default=None)
|
||||||
fslocation = attr.ib(
|
fslocation = attr.ib(type=Optional[Tuple[str, int]], default=None)
|
||||||
type=Optional[Union[Tuple[str, int], py.path.local]], default=None
|
|
||||||
)
|
|
||||||
count_towards_summary = True
|
count_towards_summary = True
|
||||||
|
|
||||||
def get_location(self, config: Config) -> Optional[str]:
|
def get_location(self, config: Config) -> Optional[str]:
|
||||||
|
@ -301,14 +299,9 @@ class WarningReport:
|
||||||
if self.nodeid:
|
if self.nodeid:
|
||||||
return self.nodeid
|
return self.nodeid
|
||||||
if self.fslocation:
|
if self.fslocation:
|
||||||
if isinstance(self.fslocation, tuple) and len(self.fslocation) >= 2:
|
filename, linenum = self.fslocation
|
||||||
filename, linenum = self.fslocation[:2]
|
relpath = bestrelpath(config.invocation_params.dir, absolutepath(filename))
|
||||||
relpath = bestrelpath(
|
return f"{relpath}:{linenum}"
|
||||||
config.invocation_params.dir, absolutepath(filename)
|
|
||||||
)
|
|
||||||
return f"{relpath}:{linenum}"
|
|
||||||
else:
|
|
||||||
return str(self.fslocation)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import importlib
|
import importlib
|
||||||
import io
|
import io
|
||||||
import operator
|
import operator
|
||||||
import os
|
|
||||||
import queue
|
import queue
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
@ -12,14 +11,14 @@ from typing import Tuple
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import py
|
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest._code.code import ExceptionChainRepr
|
from _pytest._code.code import ExceptionChainRepr
|
||||||
from _pytest._code.code import ExceptionInfo
|
from _pytest._code.code import ExceptionInfo
|
||||||
from _pytest._code.code import FormattedExcinfo
|
from _pytest._code.code import FormattedExcinfo
|
||||||
from _pytest._io import TerminalWriter
|
from _pytest._io import TerminalWriter
|
||||||
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
|
from _pytest.pathlib import bestrelpath
|
||||||
from _pytest.pathlib import import_path
|
from _pytest.pathlib import import_path
|
||||||
from _pytest.pytester import LineMatcher
|
from _pytest.pytester import LineMatcher
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
|
@ -150,9 +149,10 @@ class TestTraceback_f_g_h:
|
||||||
" except somenoname: # type: ignore[name-defined] # noqa: F821",
|
" except somenoname: # type: ignore[name-defined] # noqa: F821",
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_traceback_cut(self):
|
def test_traceback_cut(self) -> None:
|
||||||
co = _pytest._code.Code.from_function(f)
|
co = _pytest._code.Code.from_function(f)
|
||||||
path, firstlineno = co.path, co.firstlineno
|
path, firstlineno = co.path, co.firstlineno
|
||||||
|
assert isinstance(path, Path)
|
||||||
traceback = self.excinfo.traceback
|
traceback = self.excinfo.traceback
|
||||||
newtraceback = traceback.cut(path=path, firstlineno=firstlineno)
|
newtraceback = traceback.cut(path=path, firstlineno=firstlineno)
|
||||||
assert len(newtraceback) == 1
|
assert len(newtraceback) == 1
|
||||||
|
@ -163,11 +163,11 @@ class TestTraceback_f_g_h:
|
||||||
p = pytester.makepyfile("def f(): raise ValueError")
|
p = pytester.makepyfile("def f(): raise ValueError")
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
import_path(p).f() # type: ignore[attr-defined]
|
import_path(p).f() # type: ignore[attr-defined]
|
||||||
basedir = py.path.local(pytest.__file__).dirpath()
|
basedir = Path(pytest.__file__).parent
|
||||||
newtraceback = excinfo.traceback.cut(excludepath=basedir)
|
newtraceback = excinfo.traceback.cut(excludepath=basedir)
|
||||||
for x in newtraceback:
|
for x in newtraceback:
|
||||||
if hasattr(x, "path"):
|
assert isinstance(x.path, Path)
|
||||||
assert not py.path.local(x.path).relto(basedir)
|
assert basedir not in x.path.parents
|
||||||
assert newtraceback[-1].frame.code.path == p
|
assert newtraceback[-1].frame.code.path == p
|
||||||
|
|
||||||
def test_traceback_filter(self):
|
def test_traceback_filter(self):
|
||||||
|
@ -376,7 +376,7 @@ def test_excinfo_no_python_sourcecode(tmpdir):
|
||||||
for item in excinfo.traceback:
|
for item in excinfo.traceback:
|
||||||
print(item) # XXX: for some reason jinja.Template.render is printed in full
|
print(item) # XXX: for some reason jinja.Template.render is printed in full
|
||||||
item.source # shouldn't fail
|
item.source # shouldn't fail
|
||||||
if isinstance(item.path, py.path.local) and item.path.basename == "test.txt":
|
if isinstance(item.path, Path) and item.path.name == "test.txt":
|
||||||
assert str(item.source) == "{{ h()}}:"
|
assert str(item.source) == "{{ h()}}:"
|
||||||
|
|
||||||
|
|
||||||
|
@ -392,16 +392,16 @@ def test_entrysource_Queue_example():
|
||||||
assert s.startswith("def get")
|
assert s.startswith("def get")
|
||||||
|
|
||||||
|
|
||||||
def test_codepath_Queue_example():
|
def test_codepath_Queue_example() -> None:
|
||||||
try:
|
try:
|
||||||
queue.Queue().get(timeout=0.001)
|
queue.Queue().get(timeout=0.001)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
excinfo = _pytest._code.ExceptionInfo.from_current()
|
excinfo = _pytest._code.ExceptionInfo.from_current()
|
||||||
entry = excinfo.traceback[-1]
|
entry = excinfo.traceback[-1]
|
||||||
path = entry.path
|
path = entry.path
|
||||||
assert isinstance(path, py.path.local)
|
assert isinstance(path, Path)
|
||||||
assert path.basename.lower() == "queue.py"
|
assert path.name.lower() == "queue.py"
|
||||||
assert path.check()
|
assert path.exists()
|
||||||
|
|
||||||
|
|
||||||
def test_match_succeeds():
|
def test_match_succeeds():
|
||||||
|
@ -805,21 +805,21 @@ raise ValueError()
|
||||||
|
|
||||||
raised = 0
|
raised = 0
|
||||||
|
|
||||||
orig_getcwd = os.getcwd
|
orig_path_cwd = Path.cwd
|
||||||
|
|
||||||
def raiseos():
|
def raiseos():
|
||||||
nonlocal raised
|
nonlocal raised
|
||||||
upframe = sys._getframe().f_back
|
upframe = sys._getframe().f_back
|
||||||
assert upframe is not None
|
assert upframe is not None
|
||||||
if upframe.f_code.co_name == "checked_call":
|
if upframe.f_code.co_name == "_makepath":
|
||||||
# Only raise with expected calls, but not via e.g. inspect for
|
# Only raise with expected calls, but not via e.g. inspect for
|
||||||
# py38-windows.
|
# py38-windows.
|
||||||
raised += 1
|
raised += 1
|
||||||
raise OSError(2, "custom_oserror")
|
raise OSError(2, "custom_oserror")
|
||||||
return orig_getcwd()
|
return orig_path_cwd()
|
||||||
|
|
||||||
monkeypatch.setattr(os, "getcwd", raiseos)
|
monkeypatch.setattr(Path, "cwd", raiseos)
|
||||||
assert p._makepath(__file__) == __file__
|
assert p._makepath(Path(__file__)) == __file__
|
||||||
assert raised == 1
|
assert raised == 1
|
||||||
repr_tb = p.repr_traceback(excinfo)
|
repr_tb = p.repr_traceback(excinfo)
|
||||||
|
|
||||||
|
@ -1015,7 +1015,9 @@ raise ValueError()
|
||||||
assert line.endswith("mod.py")
|
assert line.endswith("mod.py")
|
||||||
assert tw_mock.lines[10] == ":3: ValueError"
|
assert tw_mock.lines[10] == ":3: ValueError"
|
||||||
|
|
||||||
def test_toterminal_long_filenames(self, importasmod, tw_mock):
|
def test_toterminal_long_filenames(
|
||||||
|
self, importasmod, tw_mock, monkeypatch: MonkeyPatch
|
||||||
|
) -> None:
|
||||||
mod = importasmod(
|
mod = importasmod(
|
||||||
"""
|
"""
|
||||||
def f():
|
def f():
|
||||||
|
@ -1023,25 +1025,22 @@ raise ValueError()
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
excinfo = pytest.raises(ValueError, mod.f)
|
excinfo = pytest.raises(ValueError, mod.f)
|
||||||
path = py.path.local(mod.__file__)
|
path = Path(mod.__file__)
|
||||||
old = path.dirpath().chdir()
|
monkeypatch.chdir(path.parent)
|
||||||
try:
|
repr = excinfo.getrepr(abspath=False)
|
||||||
repr = excinfo.getrepr(abspath=False)
|
repr.toterminal(tw_mock)
|
||||||
repr.toterminal(tw_mock)
|
x = bestrelpath(Path.cwd(), path)
|
||||||
x = py.path.local().bestrelpath(path)
|
if len(x) < len(str(path)):
|
||||||
if len(x) < len(str(path)):
|
|
||||||
msg = tw_mock.get_write_msg(-2)
|
|
||||||
assert msg == "mod.py"
|
|
||||||
assert tw_mock.lines[-1] == ":3: ValueError"
|
|
||||||
|
|
||||||
repr = excinfo.getrepr(abspath=True)
|
|
||||||
repr.toterminal(tw_mock)
|
|
||||||
msg = tw_mock.get_write_msg(-2)
|
msg = tw_mock.get_write_msg(-2)
|
||||||
assert msg == path
|
assert msg == "mod.py"
|
||||||
line = tw_mock.lines[-1]
|
assert tw_mock.lines[-1] == ":3: ValueError"
|
||||||
assert line == ":3: ValueError"
|
|
||||||
finally:
|
repr = excinfo.getrepr(abspath=True)
|
||||||
old.chdir()
|
repr.toterminal(tw_mock)
|
||||||
|
msg = tw_mock.get_write_msg(-2)
|
||||||
|
assert msg == str(path)
|
||||||
|
line = tw_mock.lines[-1]
|
||||||
|
assert line == ":3: ValueError"
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"reproptions",
|
"reproptions",
|
||||||
|
|
|
@ -6,13 +6,12 @@ import inspect
|
||||||
import linecache
|
import linecache
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
from pathlib import Path
|
||||||
from types import CodeType
|
from types import CodeType
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import py.path
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest._code import Code
|
from _pytest._code import Code
|
||||||
from _pytest._code import Frame
|
from _pytest._code import Frame
|
||||||
|
@ -352,8 +351,8 @@ def test_getfslineno() -> None:
|
||||||
|
|
||||||
fspath, lineno = getfslineno(f)
|
fspath, lineno = getfslineno(f)
|
||||||
|
|
||||||
assert isinstance(fspath, py.path.local)
|
assert isinstance(fspath, Path)
|
||||||
assert fspath.basename == "test_source.py"
|
assert fspath.name == "test_source.py"
|
||||||
assert lineno == f.__code__.co_firstlineno - 1 # see findsource
|
assert lineno == f.__code__.co_firstlineno - 1 # see findsource
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
|
@ -362,8 +361,8 @@ def test_getfslineno() -> None:
|
||||||
fspath, lineno = getfslineno(A)
|
fspath, lineno = getfslineno(A)
|
||||||
|
|
||||||
_, A_lineno = inspect.findsource(A)
|
_, A_lineno = inspect.findsource(A)
|
||||||
assert isinstance(fspath, py.path.local)
|
assert isinstance(fspath, Path)
|
||||||
assert fspath.basename == "test_source.py"
|
assert fspath.name == "test_source.py"
|
||||||
assert lineno == A_lineno
|
assert lineno == A_lineno
|
||||||
|
|
||||||
assert getfslineno(3) == ("", -1)
|
assert getfslineno(3) == ("", -1)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import pytest
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
from _pytest.monkeypatch import MonkeyPatch
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
|
from _pytest.tmpdir import TempPathFactory
|
||||||
|
|
||||||
pytest_plugins = ("pytester",)
|
pytest_plugins = ("pytester",)
|
||||||
|
|
||||||
|
@ -139,9 +140,11 @@ class TestNewAPI:
|
||||||
pytester.runpytest()
|
pytester.runpytest()
|
||||||
assert pytester.path.joinpath(rel_cache_dir).is_dir()
|
assert pytester.path.joinpath(rel_cache_dir).is_dir()
|
||||||
|
|
||||||
def test_custom_abs_cache_dir(self, pytester: Pytester, tmpdir_factory) -> None:
|
def test_custom_abs_cache_dir(
|
||||||
tmp = str(tmpdir_factory.mktemp("tmp"))
|
self, pytester: Pytester, tmp_path_factory: TempPathFactory
|
||||||
abs_cache_dir = os.path.join(tmp, "custom_cache_dir")
|
) -> None:
|
||||||
|
tmp = tmp_path_factory.mktemp("tmp")
|
||||||
|
abs_cache_dir = tmp / "custom_cache_dir"
|
||||||
pytester.makeini(
|
pytester.makeini(
|
||||||
"""
|
"""
|
||||||
[pytest]
|
[pytest]
|
||||||
|
@ -152,7 +155,7 @@ class TestNewAPI:
|
||||||
)
|
)
|
||||||
pytester.makepyfile(test_errored="def test_error():\n assert False")
|
pytester.makepyfile(test_errored="def test_error():\n assert False")
|
||||||
pytester.runpytest()
|
pytester.runpytest()
|
||||||
assert Path(abs_cache_dir).is_dir()
|
assert abs_cache_dir.is_dir()
|
||||||
|
|
||||||
def test_custom_cache_dir_with_env_var(
|
def test_custom_cache_dir_with_env_var(
|
||||||
self, pytester: Pytester, monkeypatch: MonkeyPatch
|
self, pytester: Pytester, monkeypatch: MonkeyPatch
|
||||||
|
@ -185,9 +188,9 @@ def test_cache_reportheader(env, pytester: Pytester, monkeypatch: MonkeyPatch) -
|
||||||
|
|
||||||
|
|
||||||
def test_cache_reportheader_external_abspath(
|
def test_cache_reportheader_external_abspath(
|
||||||
pytester: Pytester, tmpdir_factory
|
pytester: Pytester, tmp_path_factory: TempPathFactory
|
||||||
) -> None:
|
) -> None:
|
||||||
external_cache = tmpdir_factory.mktemp(
|
external_cache = tmp_path_factory.mktemp(
|
||||||
"test_cache_reportheader_external_abspath_abs"
|
"test_cache_reportheader_external_abspath_abs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -212,12 +212,12 @@ class TestCollectFS:
|
||||||
bindir = "Scripts" if sys.platform.startswith("win") else "bin"
|
bindir = "Scripts" if sys.platform.startswith("win") else "bin"
|
||||||
# no bin/activate, not a virtualenv
|
# no bin/activate, not a virtualenv
|
||||||
base_path = pytester.mkdir("venv")
|
base_path = pytester.mkdir("venv")
|
||||||
assert _in_venv(py.path.local(base_path)) is False
|
assert _in_venv(base_path) is False
|
||||||
# with bin/activate, totally a virtualenv
|
# with bin/activate, totally a virtualenv
|
||||||
bin_path = base_path.joinpath(bindir)
|
bin_path = base_path.joinpath(bindir)
|
||||||
bin_path.mkdir()
|
bin_path.mkdir()
|
||||||
bin_path.joinpath(fname).touch()
|
bin_path.joinpath(fname).touch()
|
||||||
assert _in_venv(py.path.local(base_path)) is True
|
assert _in_venv(base_path) is True
|
||||||
|
|
||||||
def test_custom_norecursedirs(self, pytester: Pytester) -> None:
|
def test_custom_norecursedirs(self, pytester: Pytester) -> None:
|
||||||
pytester.makeini(
|
pytester.makeini(
|
||||||
|
@ -277,7 +277,7 @@ class TestCollectPluginHookRelay:
|
||||||
wascalled.append(path)
|
wascalled.append(path)
|
||||||
|
|
||||||
pytester.makefile(".abc", "xyz")
|
pytester.makefile(".abc", "xyz")
|
||||||
pytest.main(py.path.local(pytester.path), plugins=[Plugin()])
|
pytest.main(pytester.path, plugins=[Plugin()])
|
||||||
assert len(wascalled) == 1
|
assert len(wascalled) == 1
|
||||||
assert wascalled[0].ext == ".abc"
|
assert wascalled[0].ext == ".abc"
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ from typing import Type
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
import py.path
|
|
||||||
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -28,6 +27,7 @@ from _pytest.config.findpaths import determine_setup
|
||||||
from _pytest.config.findpaths import get_common_ancestor
|
from _pytest.config.findpaths import get_common_ancestor
|
||||||
from _pytest.config.findpaths import locate_config
|
from _pytest.config.findpaths import locate_config
|
||||||
from _pytest.monkeypatch import MonkeyPatch
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
|
from _pytest.pathlib import absolutepath
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
|
|
||||||
|
|
||||||
|
@ -854,8 +854,8 @@ class TestConfigFromdictargs:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
inifile = "../../foo/bar.ini"
|
inifilename = "../../foo/bar.ini"
|
||||||
option_dict = {"inifilename": inifile, "capture": "no"}
|
option_dict = {"inifilename": inifilename, "capture": "no"}
|
||||||
|
|
||||||
cwd = tmp_path.joinpath("a/b")
|
cwd = tmp_path.joinpath("a/b")
|
||||||
cwd.mkdir(parents=True)
|
cwd.mkdir(parents=True)
|
||||||
|
@ -873,14 +873,14 @@ class TestConfigFromdictargs:
|
||||||
with MonkeyPatch.context() as mp:
|
with MonkeyPatch.context() as mp:
|
||||||
mp.chdir(cwd)
|
mp.chdir(cwd)
|
||||||
config = Config.fromdictargs(option_dict, ())
|
config = Config.fromdictargs(option_dict, ())
|
||||||
inipath = py.path.local(inifile)
|
inipath = absolutepath(inifilename)
|
||||||
|
|
||||||
assert config.args == [str(cwd)]
|
assert config.args == [str(cwd)]
|
||||||
assert config.option.inifilename == inifile
|
assert config.option.inifilename == inifilename
|
||||||
assert config.option.capture == "no"
|
assert config.option.capture == "no"
|
||||||
|
|
||||||
# this indicates this is the file used for getting configuration values
|
# this indicates this is the file used for getting configuration values
|
||||||
assert config.inifile == inipath
|
assert config.inipath == inipath
|
||||||
assert config.inicfg.get("name") == "value"
|
assert config.inicfg.get("name") == "value"
|
||||||
assert config.inicfg.get("should_not_be_set") is None
|
assert config.inicfg.get("should_not_be_set") is None
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import inspect
|
import inspect
|
||||||
import textwrap
|
import textwrap
|
||||||
|
from pathlib import Path
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import py
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.doctest import _get_checker
|
from _pytest.doctest import _get_checker
|
||||||
from _pytest.doctest import _is_mocked
|
from _pytest.doctest import _is_mocked
|
||||||
|
@ -1496,25 +1495,25 @@ def test_warning_on_unwrap_of_broken_object(
|
||||||
assert inspect.unwrap.__module__ == "inspect"
|
assert inspect.unwrap.__module__ == "inspect"
|
||||||
|
|
||||||
|
|
||||||
def test_is_setup_py_not_named_setup_py(tmp_path):
|
def test_is_setup_py_not_named_setup_py(tmp_path: Path) -> None:
|
||||||
not_setup_py = tmp_path.joinpath("not_setup.py")
|
not_setup_py = tmp_path.joinpath("not_setup.py")
|
||||||
not_setup_py.write_text('from setuptools import setup; setup(name="foo")')
|
not_setup_py.write_text('from setuptools import setup; setup(name="foo")')
|
||||||
assert not _is_setup_py(py.path.local(str(not_setup_py)))
|
assert not _is_setup_py(not_setup_py)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mod", ("setuptools", "distutils.core"))
|
@pytest.mark.parametrize("mod", ("setuptools", "distutils.core"))
|
||||||
def test_is_setup_py_is_a_setup_py(tmpdir, mod):
|
def test_is_setup_py_is_a_setup_py(tmp_path: Path, mod: str) -> None:
|
||||||
setup_py = tmpdir.join("setup.py")
|
setup_py = tmp_path.joinpath("setup.py")
|
||||||
setup_py.write(f'from {mod} import setup; setup(name="foo")')
|
setup_py.write_text(f'from {mod} import setup; setup(name="foo")', "utf-8")
|
||||||
assert _is_setup_py(setup_py)
|
assert _is_setup_py(setup_py)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mod", ("setuptools", "distutils.core"))
|
@pytest.mark.parametrize("mod", ("setuptools", "distutils.core"))
|
||||||
def test_is_setup_py_different_encoding(tmp_path, mod):
|
def test_is_setup_py_different_encoding(tmp_path: Path, mod: str) -> None:
|
||||||
setup_py = tmp_path.joinpath("setup.py")
|
setup_py = tmp_path.joinpath("setup.py")
|
||||||
contents = (
|
contents = (
|
||||||
"# -*- coding: cp1252 -*-\n"
|
"# -*- coding: cp1252 -*-\n"
|
||||||
'from {} import setup; setup(name="foo", description="€")\n'.format(mod)
|
'from {} import setup; setup(name="foo", description="€")\n'.format(mod)
|
||||||
)
|
)
|
||||||
setup_py.write_bytes(contents.encode("cp1252"))
|
setup_py.write_bytes(contents.encode("cp1252"))
|
||||||
assert _is_setup_py(py.path.local(str(setup_py)))
|
assert _is_setup_py(setup_py)
|
||||||
|
|
|
@ -16,7 +16,6 @@ def test_version_less_verbose(pytester: Pytester, pytestconfig, monkeypatch) ->
|
||||||
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
||||||
result = pytester.runpytest("--version")
|
result = pytester.runpytest("--version")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
# p = py.path.local(py.__file__).dirpath()
|
|
||||||
result.stderr.fnmatch_lines([f"pytest {pytest.__version__}"])
|
result.stderr.fnmatch_lines([f"pytest {pytest.__version__}"])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from pathlib import Path
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
@ -69,23 +70,23 @@ def test_node_warning_enforces_warning_types(pytester: Pytester) -> None:
|
||||||
|
|
||||||
def test__check_initialpaths_for_relpath() -> None:
|
def test__check_initialpaths_for_relpath() -> None:
|
||||||
"""Ensure that it handles dirs, and does not always use dirname."""
|
"""Ensure that it handles dirs, and does not always use dirname."""
|
||||||
cwd = py.path.local()
|
cwd = Path.cwd()
|
||||||
|
|
||||||
class FakeSession1:
|
class FakeSession1:
|
||||||
_initialpaths = [cwd]
|
_initialpaths = frozenset({cwd})
|
||||||
|
|
||||||
session = cast(pytest.Session, FakeSession1)
|
session = cast(pytest.Session, FakeSession1)
|
||||||
|
|
||||||
assert nodes._check_initialpaths_for_relpath(session, cwd) == ""
|
assert nodes._check_initialpaths_for_relpath(session, py.path.local(cwd)) == ""
|
||||||
|
|
||||||
sub = cwd.join("file")
|
sub = cwd / "file"
|
||||||
|
|
||||||
class FakeSession2:
|
class FakeSession2:
|
||||||
_initialpaths = [cwd]
|
_initialpaths = frozenset({cwd})
|
||||||
|
|
||||||
session = cast(pytest.Session, FakeSession2)
|
session = cast(pytest.Session, FakeSession2)
|
||||||
|
|
||||||
assert nodes._check_initialpaths_for_relpath(session, sub) == "file"
|
assert nodes._check_initialpaths_for_relpath(session, py.path.local(sub)) == "file"
|
||||||
|
|
||||||
outside = py.path.local("/outside")
|
outside = py.path.local("/outside")
|
||||||
assert nodes._check_initialpaths_for_relpath(session, outside) is None
|
assert nodes._check_initialpaths_for_relpath(session, outside) is None
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
import os.path
|
import os.path
|
||||||
|
import pickle
|
||||||
import sys
|
import sys
|
||||||
import unittest.mock
|
import unittest.mock
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
from types import ModuleType
|
||||||
|
from typing import Generator
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -20,6 +23,7 @@ from _pytest.pathlib import maybe_delete_a_numbered_dir
|
||||||
from _pytest.pathlib import resolve_package_path
|
from _pytest.pathlib import resolve_package_path
|
||||||
from _pytest.pathlib import symlink_or_skip
|
from _pytest.pathlib import symlink_or_skip
|
||||||
from _pytest.pathlib import visit
|
from _pytest.pathlib import visit
|
||||||
|
from _pytest.tmpdir import TempPathFactory
|
||||||
|
|
||||||
|
|
||||||
class TestFNMatcherPort:
|
class TestFNMatcherPort:
|
||||||
|
@ -96,38 +100,40 @@ class TestImportPath:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def path1(self, tmpdir_factory):
|
def path1(self, tmp_path_factory: TempPathFactory) -> Generator[Path, None, None]:
|
||||||
path = tmpdir_factory.mktemp("path")
|
path = tmp_path_factory.mktemp("path")
|
||||||
self.setuptestfs(path)
|
self.setuptestfs(path)
|
||||||
yield path
|
yield path
|
||||||
assert path.join("samplefile").check()
|
assert path.joinpath("samplefile").exists()
|
||||||
|
|
||||||
def setuptestfs(self, path):
|
def setuptestfs(self, path: Path) -> None:
|
||||||
# print "setting up test fs for", repr(path)
|
# print "setting up test fs for", repr(path)
|
||||||
samplefile = path.ensure("samplefile")
|
samplefile = path / "samplefile"
|
||||||
samplefile.write("samplefile\n")
|
samplefile.write_text("samplefile\n")
|
||||||
|
|
||||||
execfile = path.ensure("execfile")
|
execfile = path / "execfile"
|
||||||
execfile.write("x=42")
|
execfile.write_text("x=42")
|
||||||
|
|
||||||
execfilepy = path.ensure("execfile.py")
|
execfilepy = path / "execfile.py"
|
||||||
execfilepy.write("x=42")
|
execfilepy.write_text("x=42")
|
||||||
|
|
||||||
d = {1: 2, "hello": "world", "answer": 42}
|
d = {1: 2, "hello": "world", "answer": 42}
|
||||||
path.ensure("samplepickle").dump(d)
|
path.joinpath("samplepickle").write_bytes(pickle.dumps(d, 1))
|
||||||
|
|
||||||
sampledir = path.ensure("sampledir", dir=1)
|
sampledir = path / "sampledir"
|
||||||
sampledir.ensure("otherfile")
|
sampledir.mkdir()
|
||||||
|
sampledir.joinpath("otherfile").touch()
|
||||||
|
|
||||||
otherdir = path.ensure("otherdir", dir=1)
|
otherdir = path / "otherdir"
|
||||||
otherdir.ensure("__init__.py")
|
otherdir.mkdir()
|
||||||
|
otherdir.joinpath("__init__.py").touch()
|
||||||
|
|
||||||
module_a = otherdir.ensure("a.py")
|
module_a = otherdir / "a.py"
|
||||||
module_a.write("from .b import stuff as result\n")
|
module_a.write_text("from .b import stuff as result\n")
|
||||||
module_b = otherdir.ensure("b.py")
|
module_b = otherdir / "b.py"
|
||||||
module_b.write('stuff="got it"\n')
|
module_b.write_text('stuff="got it"\n')
|
||||||
module_c = otherdir.ensure("c.py")
|
module_c = otherdir / "c.py"
|
||||||
module_c.write(
|
module_c.write_text(
|
||||||
dedent(
|
dedent(
|
||||||
"""
|
"""
|
||||||
import py;
|
import py;
|
||||||
|
@ -136,8 +142,8 @@ class TestImportPath:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
module_d = otherdir.ensure("d.py")
|
module_d = otherdir / "d.py"
|
||||||
module_d.write(
|
module_d.write_text(
|
||||||
dedent(
|
dedent(
|
||||||
"""
|
"""
|
||||||
import py;
|
import py;
|
||||||
|
@ -147,122 +153,141 @@ class TestImportPath:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_smoke_test(self, path1):
|
def test_smoke_test(self, path1: Path) -> None:
|
||||||
obj = import_path(path1.join("execfile.py"))
|
obj = import_path(path1 / "execfile.py")
|
||||||
assert obj.x == 42 # type: ignore[attr-defined]
|
assert obj.x == 42 # type: ignore[attr-defined]
|
||||||
assert obj.__name__ == "execfile"
|
assert obj.__name__ == "execfile"
|
||||||
|
|
||||||
def test_renamed_dir_creates_mismatch(self, tmpdir, monkeypatch):
|
def test_renamed_dir_creates_mismatch(
|
||||||
p = tmpdir.ensure("a", "test_x123.py")
|
self, tmp_path: Path, monkeypatch: MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
tmp_path.joinpath("a").mkdir()
|
||||||
|
p = tmp_path.joinpath("a", "test_x123.py")
|
||||||
|
p.touch()
|
||||||
import_path(p)
|
import_path(p)
|
||||||
tmpdir.join("a").move(tmpdir.join("b"))
|
tmp_path.joinpath("a").rename(tmp_path.joinpath("b"))
|
||||||
with pytest.raises(ImportPathMismatchError):
|
with pytest.raises(ImportPathMismatchError):
|
||||||
import_path(tmpdir.join("b", "test_x123.py"))
|
import_path(tmp_path.joinpath("b", "test_x123.py"))
|
||||||
|
|
||||||
# Errors can be ignored.
|
# Errors can be ignored.
|
||||||
monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "1")
|
monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "1")
|
||||||
import_path(tmpdir.join("b", "test_x123.py"))
|
import_path(tmp_path.joinpath("b", "test_x123.py"))
|
||||||
|
|
||||||
# PY_IGNORE_IMPORTMISMATCH=0 does not ignore error.
|
# PY_IGNORE_IMPORTMISMATCH=0 does not ignore error.
|
||||||
monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "0")
|
monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "0")
|
||||||
with pytest.raises(ImportPathMismatchError):
|
with pytest.raises(ImportPathMismatchError):
|
||||||
import_path(tmpdir.join("b", "test_x123.py"))
|
import_path(tmp_path.joinpath("b", "test_x123.py"))
|
||||||
|
|
||||||
def test_messy_name(self, tmpdir):
|
def test_messy_name(self, tmp_path: Path) -> None:
|
||||||
# http://bitbucket.org/hpk42/py-trunk/issue/129
|
# http://bitbucket.org/hpk42/py-trunk/issue/129
|
||||||
path = tmpdir.ensure("foo__init__.py")
|
path = tmp_path / "foo__init__.py"
|
||||||
|
path.touch()
|
||||||
module = import_path(path)
|
module = import_path(path)
|
||||||
assert module.__name__ == "foo__init__"
|
assert module.__name__ == "foo__init__"
|
||||||
|
|
||||||
def test_dir(self, tmpdir):
|
def test_dir(self, tmp_path: Path) -> None:
|
||||||
p = tmpdir.join("hello_123")
|
p = tmp_path / "hello_123"
|
||||||
p_init = p.ensure("__init__.py")
|
p.mkdir()
|
||||||
|
p_init = p / "__init__.py"
|
||||||
|
p_init.touch()
|
||||||
m = import_path(p)
|
m = import_path(p)
|
||||||
assert m.__name__ == "hello_123"
|
assert m.__name__ == "hello_123"
|
||||||
m = import_path(p_init)
|
m = import_path(p_init)
|
||||||
assert m.__name__ == "hello_123"
|
assert m.__name__ == "hello_123"
|
||||||
|
|
||||||
def test_a(self, path1):
|
def test_a(self, path1: Path) -> None:
|
||||||
otherdir = path1.join("otherdir")
|
otherdir = path1 / "otherdir"
|
||||||
mod = import_path(otherdir.join("a.py"))
|
mod = import_path(otherdir / "a.py")
|
||||||
assert mod.result == "got it" # type: ignore[attr-defined]
|
assert mod.result == "got it" # type: ignore[attr-defined]
|
||||||
assert mod.__name__ == "otherdir.a"
|
assert mod.__name__ == "otherdir.a"
|
||||||
|
|
||||||
def test_b(self, path1):
|
def test_b(self, path1: Path) -> None:
|
||||||
otherdir = path1.join("otherdir")
|
otherdir = path1 / "otherdir"
|
||||||
mod = import_path(otherdir.join("b.py"))
|
mod = import_path(otherdir / "b.py")
|
||||||
assert mod.stuff == "got it" # type: ignore[attr-defined]
|
assert mod.stuff == "got it" # type: ignore[attr-defined]
|
||||||
assert mod.__name__ == "otherdir.b"
|
assert mod.__name__ == "otherdir.b"
|
||||||
|
|
||||||
def test_c(self, path1):
|
def test_c(self, path1: Path) -> None:
|
||||||
otherdir = path1.join("otherdir")
|
otherdir = path1 / "otherdir"
|
||||||
mod = import_path(otherdir.join("c.py"))
|
mod = import_path(otherdir / "c.py")
|
||||||
assert mod.value == "got it" # type: ignore[attr-defined]
|
assert mod.value == "got it" # type: ignore[attr-defined]
|
||||||
|
|
||||||
def test_d(self, path1):
|
def test_d(self, path1: Path) -> None:
|
||||||
otherdir = path1.join("otherdir")
|
otherdir = path1 / "otherdir"
|
||||||
mod = import_path(otherdir.join("d.py"))
|
mod = import_path(otherdir / "d.py")
|
||||||
assert mod.value2 == "got it" # type: ignore[attr-defined]
|
assert mod.value2 == "got it" # type: ignore[attr-defined]
|
||||||
|
|
||||||
def test_import_after(self, tmpdir):
|
def test_import_after(self, tmp_path: Path) -> None:
|
||||||
tmpdir.ensure("xxxpackage", "__init__.py")
|
tmp_path.joinpath("xxxpackage").mkdir()
|
||||||
mod1path = tmpdir.ensure("xxxpackage", "module1.py")
|
tmp_path.joinpath("xxxpackage", "__init__.py").touch()
|
||||||
|
mod1path = tmp_path.joinpath("xxxpackage", "module1.py")
|
||||||
|
mod1path.touch()
|
||||||
mod1 = import_path(mod1path)
|
mod1 = import_path(mod1path)
|
||||||
assert mod1.__name__ == "xxxpackage.module1"
|
assert mod1.__name__ == "xxxpackage.module1"
|
||||||
from xxxpackage import module1
|
from xxxpackage import module1
|
||||||
|
|
||||||
assert module1 is mod1
|
assert module1 is mod1
|
||||||
|
|
||||||
def test_check_filepath_consistency(self, monkeypatch, tmpdir):
|
def test_check_filepath_consistency(
|
||||||
|
self, monkeypatch: MonkeyPatch, tmp_path: Path
|
||||||
|
) -> None:
|
||||||
name = "pointsback123"
|
name = "pointsback123"
|
||||||
ModuleType = type(os)
|
p = tmp_path.joinpath(name + ".py")
|
||||||
p = tmpdir.ensure(name + ".py")
|
p.touch()
|
||||||
for ending in (".pyc", ".pyo"):
|
for ending in (".pyc", ".pyo"):
|
||||||
mod = ModuleType(name)
|
mod = ModuleType(name)
|
||||||
pseudopath = tmpdir.ensure(name + ending)
|
pseudopath = tmp_path.joinpath(name + ending)
|
||||||
|
pseudopath.touch()
|
||||||
mod.__file__ = str(pseudopath)
|
mod.__file__ = str(pseudopath)
|
||||||
monkeypatch.setitem(sys.modules, name, mod)
|
monkeypatch.setitem(sys.modules, name, mod)
|
||||||
newmod = import_path(p)
|
newmod = import_path(p)
|
||||||
assert mod == newmod
|
assert mod == newmod
|
||||||
monkeypatch.undo()
|
monkeypatch.undo()
|
||||||
mod = ModuleType(name)
|
mod = ModuleType(name)
|
||||||
pseudopath = tmpdir.ensure(name + "123.py")
|
pseudopath = tmp_path.joinpath(name + "123.py")
|
||||||
|
pseudopath.touch()
|
||||||
mod.__file__ = str(pseudopath)
|
mod.__file__ = str(pseudopath)
|
||||||
monkeypatch.setitem(sys.modules, name, mod)
|
monkeypatch.setitem(sys.modules, name, mod)
|
||||||
with pytest.raises(ImportPathMismatchError) as excinfo:
|
with pytest.raises(ImportPathMismatchError) as excinfo:
|
||||||
import_path(p)
|
import_path(p)
|
||||||
modname, modfile, orig = excinfo.value.args
|
modname, modfile, orig = excinfo.value.args
|
||||||
assert modname == name
|
assert modname == name
|
||||||
assert modfile == pseudopath
|
assert modfile == str(pseudopath)
|
||||||
assert orig == p
|
assert orig == p
|
||||||
assert issubclass(ImportPathMismatchError, ImportError)
|
assert issubclass(ImportPathMismatchError, ImportError)
|
||||||
|
|
||||||
def test_issue131_on__init__(self, tmpdir):
|
def test_issue131_on__init__(self, tmp_path: Path) -> None:
|
||||||
# __init__.py files may be namespace packages, and thus the
|
# __init__.py files may be namespace packages, and thus the
|
||||||
# __file__ of an imported module may not be ourselves
|
# __file__ of an imported module may not be ourselves
|
||||||
# see issue
|
# see issue
|
||||||
p1 = tmpdir.ensure("proja", "__init__.py")
|
tmp_path.joinpath("proja").mkdir()
|
||||||
p2 = tmpdir.ensure("sub", "proja", "__init__.py")
|
p1 = tmp_path.joinpath("proja", "__init__.py")
|
||||||
|
p1.touch()
|
||||||
|
tmp_path.joinpath("sub", "proja").mkdir(parents=True)
|
||||||
|
p2 = tmp_path.joinpath("sub", "proja", "__init__.py")
|
||||||
|
p2.touch()
|
||||||
m1 = import_path(p1)
|
m1 = import_path(p1)
|
||||||
m2 = import_path(p2)
|
m2 = import_path(p2)
|
||||||
assert m1 == m2
|
assert m1 == m2
|
||||||
|
|
||||||
def test_ensuresyspath_append(self, tmpdir):
|
def test_ensuresyspath_append(self, tmp_path: Path) -> None:
|
||||||
root1 = tmpdir.mkdir("root1")
|
root1 = tmp_path / "root1"
|
||||||
file1 = root1.ensure("x123.py")
|
root1.mkdir()
|
||||||
|
file1 = root1 / "x123.py"
|
||||||
|
file1.touch()
|
||||||
assert str(root1) not in sys.path
|
assert str(root1) not in sys.path
|
||||||
import_path(file1, mode="append")
|
import_path(file1, mode="append")
|
||||||
assert str(root1) == sys.path[-1]
|
assert str(root1) == sys.path[-1]
|
||||||
assert str(root1) not in sys.path[:-1]
|
assert str(root1) not in sys.path[:-1]
|
||||||
|
|
||||||
def test_invalid_path(self, tmpdir):
|
def test_invalid_path(self, tmp_path: Path) -> None:
|
||||||
with pytest.raises(ImportError):
|
with pytest.raises(ImportError):
|
||||||
import_path(tmpdir.join("invalid.py"))
|
import_path(tmp_path / "invalid.py")
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def simple_module(self, tmpdir):
|
def simple_module(self, tmp_path: Path) -> Path:
|
||||||
fn = tmpdir.join("mymod.py")
|
fn = tmp_path / "mymod.py"
|
||||||
fn.write(
|
fn.write_text(
|
||||||
dedent(
|
dedent(
|
||||||
"""
|
"""
|
||||||
def foo(x): return 40 + x
|
def foo(x): return 40 + x
|
||||||
|
@ -271,19 +296,21 @@ class TestImportPath:
|
||||||
)
|
)
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
def test_importmode_importlib(self, simple_module):
|
def test_importmode_importlib(self, simple_module: Path) -> None:
|
||||||
"""`importlib` mode does not change sys.path."""
|
"""`importlib` mode does not change sys.path."""
|
||||||
module = import_path(simple_module, mode="importlib")
|
module = import_path(simple_module, mode="importlib")
|
||||||
assert module.foo(2) == 42 # type: ignore[attr-defined]
|
assert module.foo(2) == 42 # type: ignore[attr-defined]
|
||||||
assert simple_module.dirname not in sys.path
|
assert str(simple_module.parent) not in sys.path
|
||||||
|
|
||||||
def test_importmode_twice_is_different_module(self, simple_module):
|
def test_importmode_twice_is_different_module(self, simple_module: Path) -> None:
|
||||||
"""`importlib` mode always returns a new module."""
|
"""`importlib` mode always returns a new module."""
|
||||||
module1 = import_path(simple_module, mode="importlib")
|
module1 = import_path(simple_module, mode="importlib")
|
||||||
module2 = import_path(simple_module, mode="importlib")
|
module2 = import_path(simple_module, mode="importlib")
|
||||||
assert module1 is not module2
|
assert module1 is not module2
|
||||||
|
|
||||||
def test_no_meta_path_found(self, simple_module, monkeypatch):
|
def test_no_meta_path_found(
|
||||||
|
self, simple_module: Path, monkeypatch: MonkeyPatch
|
||||||
|
) -> None:
|
||||||
"""Even without any meta_path should still import module."""
|
"""Even without any meta_path should still import module."""
|
||||||
monkeypatch.setattr(sys, "meta_path", [])
|
monkeypatch.setattr(sys, "meta_path", [])
|
||||||
module = import_path(simple_module, mode="importlib")
|
module = import_path(simple_module, mode="importlib")
|
||||||
|
@ -299,7 +326,7 @@ class TestImportPath:
|
||||||
import_path(simple_module, mode="importlib")
|
import_path(simple_module, mode="importlib")
|
||||||
|
|
||||||
|
|
||||||
def test_resolve_package_path(tmp_path):
|
def test_resolve_package_path(tmp_path: Path) -> None:
|
||||||
pkg = tmp_path / "pkg1"
|
pkg = tmp_path / "pkg1"
|
||||||
pkg.mkdir()
|
pkg.mkdir()
|
||||||
(pkg / "__init__.py").touch()
|
(pkg / "__init__.py").touch()
|
||||||
|
@ -309,7 +336,7 @@ def test_resolve_package_path(tmp_path):
|
||||||
assert resolve_package_path(pkg.joinpath("subdir", "__init__.py")) == pkg
|
assert resolve_package_path(pkg.joinpath("subdir", "__init__.py")) == pkg
|
||||||
|
|
||||||
|
|
||||||
def test_package_unimportable(tmp_path):
|
def test_package_unimportable(tmp_path: Path) -> None:
|
||||||
pkg = tmp_path / "pkg1-1"
|
pkg = tmp_path / "pkg1-1"
|
||||||
pkg.mkdir()
|
pkg.mkdir()
|
||||||
pkg.joinpath("__init__.py").touch()
|
pkg.joinpath("__init__.py").touch()
|
||||||
|
@ -323,7 +350,7 @@ def test_package_unimportable(tmp_path):
|
||||||
assert not resolve_package_path(pkg)
|
assert not resolve_package_path(pkg)
|
||||||
|
|
||||||
|
|
||||||
def test_access_denied_during_cleanup(tmp_path, monkeypatch):
|
def test_access_denied_during_cleanup(tmp_path: Path, monkeypatch: MonkeyPatch) -> None:
|
||||||
"""Ensure that deleting a numbered dir does not fail because of OSErrors (#4262)."""
|
"""Ensure that deleting a numbered dir does not fail because of OSErrors (#4262)."""
|
||||||
path = tmp_path / "temp-1"
|
path = tmp_path / "temp-1"
|
||||||
path.mkdir()
|
path.mkdir()
|
||||||
|
@ -338,7 +365,7 @@ def test_access_denied_during_cleanup(tmp_path, monkeypatch):
|
||||||
assert not lock_path.is_file()
|
assert not lock_path.is_file()
|
||||||
|
|
||||||
|
|
||||||
def test_long_path_during_cleanup(tmp_path):
|
def test_long_path_during_cleanup(tmp_path: Path) -> None:
|
||||||
"""Ensure that deleting long path works (particularly on Windows (#6775))."""
|
"""Ensure that deleting long path works (particularly on Windows (#6775))."""
|
||||||
path = (tmp_path / ("a" * 250)).resolve()
|
path = (tmp_path / ("a" * 250)).resolve()
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
|
@ -354,14 +381,14 @@ def test_long_path_during_cleanup(tmp_path):
|
||||||
assert not os.path.isdir(extended_path)
|
assert not os.path.isdir(extended_path)
|
||||||
|
|
||||||
|
|
||||||
def test_get_extended_length_path_str():
|
def test_get_extended_length_path_str() -> None:
|
||||||
assert get_extended_length_path_str(r"c:\foo") == r"\\?\c:\foo"
|
assert get_extended_length_path_str(r"c:\foo") == r"\\?\c:\foo"
|
||||||
assert get_extended_length_path_str(r"\\share\foo") == r"\\?\UNC\share\foo"
|
assert get_extended_length_path_str(r"\\share\foo") == r"\\?\UNC\share\foo"
|
||||||
assert get_extended_length_path_str(r"\\?\UNC\share\foo") == r"\\?\UNC\share\foo"
|
assert get_extended_length_path_str(r"\\?\UNC\share\foo") == r"\\?\UNC\share\foo"
|
||||||
assert get_extended_length_path_str(r"\\?\c:\foo") == r"\\?\c:\foo"
|
assert get_extended_length_path_str(r"\\?\c:\foo") == r"\\?\c:\foo"
|
||||||
|
|
||||||
|
|
||||||
def test_suppress_error_removing_lock(tmp_path):
|
def test_suppress_error_removing_lock(tmp_path: Path) -> None:
|
||||||
"""ensure_deletable should be resilient if lock file cannot be removed (#5456, #7491)"""
|
"""ensure_deletable should be resilient if lock file cannot be removed (#5456, #7491)"""
|
||||||
path = tmp_path / "dir"
|
path = tmp_path / "dir"
|
||||||
path.mkdir()
|
path.mkdir()
|
||||||
|
@ -406,15 +433,14 @@ def test_commonpath() -> None:
|
||||||
assert commonpath(path, path.parent.parent) == path.parent.parent
|
assert commonpath(path, path.parent.parent) == path.parent.parent
|
||||||
|
|
||||||
|
|
||||||
def test_visit_ignores_errors(tmpdir) -> None:
|
def test_visit_ignores_errors(tmp_path: Path) -> None:
|
||||||
symlink_or_skip("recursive", tmpdir.join("recursive"))
|
symlink_or_skip("recursive", tmp_path / "recursive")
|
||||||
tmpdir.join("foo").write_binary(b"")
|
tmp_path.joinpath("foo").write_bytes(b"")
|
||||||
tmpdir.join("bar").write_binary(b"")
|
tmp_path.joinpath("bar").write_bytes(b"")
|
||||||
|
|
||||||
assert [entry.name for entry in visit(tmpdir, recurse=lambda entry: False)] == [
|
assert [
|
||||||
"bar",
|
entry.name for entry in visit(str(tmp_path), recurse=lambda entry: False)
|
||||||
"foo",
|
] == ["bar", "foo"]
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only")
|
@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only")
|
||||||
|
|
Loading…
Reference in New Issue