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