summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRan Benita <ran@unusedvar.com>2020-12-26 15:01:07 +0200
committerGitHub <noreply@github.com>2020-12-26 15:01:07 +0200
commitd8d2df7e6f38e5c3681528bc1a998c904d49e5cc (patch)
treef73c1ab3078fd7049ca3acc309c43f961cfc8554
parentda01ee0a4bb0af780167ecd228ab3ad249511302 (diff)
parentca4effc8225edf7fc828a4291642c82349ed8107 (diff)
downloadpytest-d8d2df7e6f38e5c3681528bc1a998c904d49e5cc.tar.gz
Merge pull request #8174 from bluetech/py-to-pathlib-5
More py.path -> pathlib conversions
-rw-r--r--changelog/8174.trivial.rst6
-rw-r--r--src/_pytest/_code/code.py55
-rw-r--r--src/_pytest/config/__init__.py12
-rw-r--r--src/_pytest/doctest.py21
-rw-r--r--src/_pytest/fixtures.py8
-rw-r--r--src/_pytest/main.py84
-rw-r--r--src/_pytest/nodes.py8
-rw-r--r--src/_pytest/pathlib.py2
-rw-r--r--src/_pytest/python.py67
-rw-r--r--src/_pytest/terminal.py17
-rw-r--r--testing/code/test_excinfo.py71
-rw-r--r--testing/code/test_source.py11
-rw-r--r--testing/test_cacheprovider.py15
-rw-r--r--testing/test_collection.py6
-rw-r--r--testing/test_config.py12
-rw-r--r--testing/test_doctest.py17
-rw-r--r--testing/test_helpconfig.py1
-rw-r--r--testing/test_nodes.py13
-rw-r--r--testing/test_pathlib.py204
19 files changed, 336 insertions, 294 deletions
diff --git a/changelog/8174.trivial.rst b/changelog/8174.trivial.rst
new file mode 100644
index 000000000..764976461
--- /dev/null
+++ b/changelog/8174.trivial.rst
@@ -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``.
diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py
index 423069330..043a23a79 100644
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -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:
diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py
index 0df4ffa01..760b0f55c 100644
--- a/src/_pytest/config/__init__.py
+++ b/src/_pytest/config/__init__.py
@@ -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)
diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py
index d0b6b4c41..24f888257 100644
--- a/src/_pytest/doctest.py
+++ b/src/_pytest/doctest.py
@@ -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
diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py
index dbb039bf2..d3ec1296a 100644
--- a/src/_pytest/fixtures.py
+++ b/src/_pytest/fixtures.py
@@ -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
diff --git a/src/_pytest/main.py b/src/_pytest/main.py
index e7c31ecc1..79afdde61 100644
--- a/src/_pytest/main.py
+++ b/src/_pytest/main.py
@@ -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:
diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py
index 1b3ec5571..da2a0a7ea 100644
--- a/src/_pytest/nodes.py
+++ b/src/_pytest/nodes.py
@@ -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()
diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py
index 2e452eb1c..d3908a3fd 100644
--- a/src/_pytest/pathlib.py
+++ b/src/_pytest/pathlib.py
@@ -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
diff --git a/src/_pytest/python.py b/src/_pytest/python.py
index 3ff04455f..b46050920 100644
--- a/src/_pytest/python.py
+++ b/src/_pytest/python.py
@@ -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,
)
diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py
index f5d4e1f8d..d3d1a4b66 100644
--- a/src/_pytest/terminal.py
+++ b/src/_pytest/terminal.py
@@ -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
diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py
index 44d7ab549..19c888403 100644
--- a/testing/code/test_excinfo.py
+++ b/testing/code/test_excinfo.py
@@ -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",
diff --git a/testing/code/test_source.py b/testing/code/test_source.py
index 04d0ea932..6b8443fd2 100644
--- a/testing/code/test_source.py
+++ b/testing/code/test_source.py
@@ -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)
diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py
index ebd455593..2cb657efc 100644
--- a/testing/test_cacheprovider.py
+++ b/testing/test_cacheprovider.py
@@ -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"
)
diff --git a/testing/test_collection.py b/testing/test_collection.py
index 2d03fda39..3dd9283ec 100644
--- a/testing/test_collection.py
+++ b/testing/test_collection.py
@@ -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"
diff --git a/testing/test_config.py b/testing/test_config.py
index eacc9c9eb..8c1441e06 100644
--- a/testing/test_config.py
+++ b/testing/test_config.py
@@ -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
diff --git a/testing/test_doctest.py b/testing/test_doctest.py
index 6e3880330..08d0aacf6 100644
--- a/testing/test_doctest.py
+++ b/testing/test_doctest.py
@@ -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)
diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py
index c2533ef30..9a433b1b1 100644
--- a/testing/test_helpconfig.py
+++ b/testing/test_helpconfig.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__}"])
diff --git a/testing/test_nodes.py b/testing/test_nodes.py
index bae31f0a3..59d9f409e 100644
--- a/testing/test_nodes.py
+++ b/testing/test_nodes.py
@@ -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
diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py
index f60b9f263..48149084e 100644
--- a/testing/test_pathlib.py
+++ b/testing/test_pathlib.py
@@ -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)
-
- sampledir = path.ensure("sampledir", dir=1)
- sampledir.ensure("otherfile")
-
- otherdir = path.ensure("otherdir", dir=1)
- otherdir.ensure("__init__.py")
-
- 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(
+ path.joinpath("samplepickle").write_bytes(pickle.dumps(d, 1))
+
+ sampledir = path / "sampledir"
+ sampledir.mkdir()
+ sampledir.joinpath("otherfile").touch()
+
+ otherdir = path / "otherdir"
+ otherdir.mkdir()
+ otherdir.joinpath("__init__.py").touch()
+
+ 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")