Some py.path.local -> pathlib.Path

This commit is contained in:
Ran Benita 2020-12-14 15:54:59 +02:00
parent cb8142b8ec
commit 2cb34a99cb
10 changed files with 84 additions and 94 deletions

View File

@ -27,8 +27,6 @@ from typing import Tuple
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Union from typing import Union
import py
from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr
from _pytest._version import version from _pytest._version import version
from _pytest.assertion import util from _pytest.assertion import util
@ -37,6 +35,7 @@ from _pytest.assertion.util import ( # noqa: F401
) )
from _pytest.config import Config from _pytest.config import Config
from _pytest.main import Session from _pytest.main import Session
from _pytest.pathlib import absolutepath
from _pytest.pathlib import fnmatch_ex from _pytest.pathlib import fnmatch_ex
from _pytest.store import StoreKey from _pytest.store import StoreKey
@ -215,7 +214,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
return True return True
if self.session is not None: if self.session is not None:
if self.session.isinitpath(py.path.local(fn)): if self.session.isinitpath(absolutepath(fn)):
state.trace(f"matched test file (was specified on cmdline): {fn!r}") state.trace(f"matched test file (was specified on cmdline): {fn!r}")
return True return True

View File

@ -1,4 +1,5 @@
import argparse import argparse
import os
import sys import sys
import warnings import warnings
from gettext import gettext from gettext import gettext
@ -14,8 +15,6 @@ from typing import Tuple
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Union from typing import Union
import py
import _pytest._io import _pytest._io
from _pytest.compat import final from _pytest.compat import final
from _pytest.config.exceptions import UsageError from _pytest.config.exceptions import UsageError
@ -97,14 +96,14 @@ class Parser:
def parse( def parse(
self, self,
args: Sequence[Union[str, py.path.local]], args: Sequence[Union[str, "os.PathLike[str]"]],
namespace: Optional[argparse.Namespace] = None, namespace: Optional[argparse.Namespace] = None,
) -> argparse.Namespace: ) -> argparse.Namespace:
from _pytest._argcomplete import try_argcomplete from _pytest._argcomplete import try_argcomplete
self.optparser = self._getparser() self.optparser = self._getparser()
try_argcomplete(self.optparser) try_argcomplete(self.optparser)
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args] strargs = [os.fspath(x) for x in args]
return self.optparser.parse_args(strargs, namespace=namespace) return self.optparser.parse_args(strargs, namespace=namespace)
def _getparser(self) -> "MyOptionParser": def _getparser(self) -> "MyOptionParser":
@ -128,7 +127,7 @@ class Parser:
def parse_setoption( def parse_setoption(
self, self,
args: Sequence[Union[str, py.path.local]], args: Sequence[Union[str, "os.PathLike[str]"]],
option: argparse.Namespace, option: argparse.Namespace,
namespace: Optional[argparse.Namespace] = None, namespace: Optional[argparse.Namespace] = None,
) -> List[str]: ) -> List[str]:
@ -139,7 +138,7 @@ class Parser:
def parse_known_args( def parse_known_args(
self, self,
args: Sequence[Union[str, py.path.local]], args: Sequence[Union[str, "os.PathLike[str]"]],
namespace: Optional[argparse.Namespace] = None, namespace: Optional[argparse.Namespace] = None,
) -> argparse.Namespace: ) -> argparse.Namespace:
"""Parse and return a namespace object with known arguments at this point.""" """Parse and return a namespace object with known arguments at this point."""
@ -147,13 +146,13 @@ class Parser:
def parse_known_and_unknown_args( def parse_known_and_unknown_args(
self, self,
args: Sequence[Union[str, py.path.local]], args: Sequence[Union[str, "os.PathLike[str]"]],
namespace: Optional[argparse.Namespace] = None, namespace: Optional[argparse.Namespace] = None,
) -> Tuple[argparse.Namespace, List[str]]: ) -> Tuple[argparse.Namespace, List[str]]:
"""Parse and return a namespace object with known arguments, and """Parse and return a namespace object with known arguments, and
the remaining arguments unknown at this point.""" the remaining arguments unknown at this point."""
optparser = self._getparser() optparser = self._getparser()
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args] strargs = [os.fspath(x) for x in args]
return optparser.parse_known_args(strargs, namespace=namespace) return optparser.parse_known_args(strargs, namespace=namespace)
def addini( def addini(

View File

@ -648,12 +648,13 @@ class FixtureRequest:
if has_params: if has_params:
frame = inspect.stack()[3] frame = inspect.stack()[3]
frameinfo = inspect.getframeinfo(frame[0]) frameinfo = inspect.getframeinfo(frame[0])
source_path = py.path.local(frameinfo.filename) source_path = absolutepath(frameinfo.filename)
source_lineno = frameinfo.lineno source_lineno = frameinfo.lineno
rel_source_path = source_path.relto(funcitem.config.rootdir) try:
if rel_source_path: source_path_str = str(
source_path_str = rel_source_path source_path.relative_to(funcitem.config.rootpath)
else: )
except ValueError:
source_path_str = str(source_path) source_path_str = str(source_path)
msg = ( msg = (
"The requested fixture has no parameter defined for test:\n" "The requested fixture has no parameter defined for test:\n"
@ -876,7 +877,7 @@ class FixtureLookupError(LookupError):
class FixtureLookupErrorRepr(TerminalRepr): class FixtureLookupErrorRepr(TerminalRepr):
def __init__( def __init__(
self, self,
filename: Union[str, py.path.local], filename: Union[str, "os.PathLike[str]"],
firstlineno: int, firstlineno: int,
tblines: Sequence[str], tblines: Sequence[str],
errorstring: str, errorstring: str,
@ -903,7 +904,7 @@ class FixtureLookupErrorRepr(TerminalRepr):
f"{FormattedExcinfo.flow_marker} {line.strip()}", red=True, f"{FormattedExcinfo.flow_marker} {line.strip()}", red=True,
) )
tw.line() tw.line()
tw.line("%s:%d" % (self.filename, self.firstlineno + 1)) tw.line("%s:%d" % (os.fspath(self.filename), self.firstlineno + 1))
def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn": def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn":

View File

@ -467,7 +467,7 @@ class Session(nodes.FSCollector):
self.shouldfail: Union[bool, str] = False self.shouldfail: Union[bool, str] = False
self.trace = config.trace.root.get("collection") self.trace = config.trace.root.get("collection")
self.startdir = config.invocation_dir self.startdir = config.invocation_dir
self._initialpaths: FrozenSet[py.path.local] = frozenset() self._initialpaths: FrozenSet[Path] = frozenset()
self._bestrelpathcache: Dict[Path, str] = _bestrelpath_cache(config.rootpath) self._bestrelpathcache: Dict[Path, str] = _bestrelpath_cache(config.rootpath)
@ -510,8 +510,8 @@ class Session(nodes.FSCollector):
pytest_collectreport = pytest_runtest_logreport pytest_collectreport = pytest_runtest_logreport
def isinitpath(self, path: py.path.local) -> bool: def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool:
return path in self._initialpaths return Path(path) in self._initialpaths
def gethookproxy(self, fspath: "os.PathLike[str]"): def gethookproxy(self, fspath: "os.PathLike[str]"):
# Check if we have the common case of running # Check if we have the common case of running
@ -601,14 +601,14 @@ class Session(nodes.FSCollector):
self.trace.root.indent += 1 self.trace.root.indent += 1
self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = [] self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = []
self._initial_parts: List[Tuple[py.path.local, List[str]]] = [] self._initial_parts: List[Tuple[Path, List[str]]] = []
self.items: List[nodes.Item] = [] self.items: List[nodes.Item] = []
hook = self.config.hook hook = self.config.hook
items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items
try: try:
initialpaths: List[py.path.local] = [] initialpaths: List[Path] = []
for arg in args: for arg in args:
fspath, parts = resolve_collection_argument( fspath, parts = resolve_collection_argument(
self.config.invocation_params.dir, self.config.invocation_params.dir,
@ -669,13 +669,13 @@ class Session(nodes.FSCollector):
# No point in finding packages when collecting doctests. # No point in finding packages when collecting doctests.
if not self.config.getoption("doctestmodules", False): if not self.config.getoption("doctestmodules", False):
pm = self.config.pluginmanager pm = self.config.pluginmanager
confcutdir = py.path.local(pm._confcutdir) if pm._confcutdir else None confcutdir = pm._confcutdir
for parent in reversed(argpath.parts()): for parent in (argpath, *argpath.parents):
if confcutdir and confcutdir.relto(parent): if confcutdir and parent in confcutdir.parents:
break break
if parent.isdir(): if parent.is_dir():
pkginit = parent.join("__init__.py") pkginit = py.path.local(parent / "__init__.py")
if pkginit.isfile() and pkginit not in node_cache1: if pkginit.isfile() and pkginit not in node_cache1:
col = self._collectfile(pkginit, handle_dupes=False) col = self._collectfile(pkginit, handle_dupes=False)
if col: if col:
@ -685,7 +685,7 @@ class Session(nodes.FSCollector):
# If it's a directory argument, recurse and look for any Subpackages. # If it's a directory argument, recurse and look for any Subpackages.
# Let the Package collector deal with subnodes, don't collect here. # Let the Package collector deal with subnodes, don't collect here.
if argpath.check(dir=1): if argpath.is_dir():
assert not names, "invalid arg {!r}".format((argpath, names)) assert not names, "invalid arg {!r}".format((argpath, names))
seen_dirs: Set[py.path.local] = set() seen_dirs: Set[py.path.local] = set()
@ -717,15 +717,16 @@ class Session(nodes.FSCollector):
node_cache2[key] = x node_cache2[key] = x
yield x yield x
else: else:
assert argpath.check(file=1) assert argpath.is_file()
if argpath in node_cache1: argpath_ = py.path.local(argpath)
col = node_cache1[argpath] if argpath_ in node_cache1:
col = node_cache1[argpath_]
else: else:
collect_root = pkg_roots.get(argpath.dirname, self) collect_root = pkg_roots.get(argpath_.dirname, self)
col = collect_root._collectfile(argpath, handle_dupes=False) col = collect_root._collectfile(argpath_, handle_dupes=False)
if col: if col:
node_cache1[argpath] = col node_cache1[argpath_] = col
matching = [] matching = []
work: List[ work: List[
@ -782,9 +783,7 @@ class Session(nodes.FSCollector):
# first yielded item will be the __init__ Module itself, so # first yielded item will be the __init__ Module itself, so
# just use that. If this special case isn't taken, then all the # just use that. If this special case isn't taken, then all the
# files in the package will be yielded. # files in the package will be yielded.
if argpath.basename == "__init__.py" and isinstance( if argpath.name == "__init__.py" and isinstance(matching[0], Package):
matching[0], Package
):
try: try:
yield next(iter(matching[0].collect())) yield next(iter(matching[0].collect()))
except StopIteration: except StopIteration:
@ -833,7 +832,7 @@ def search_pypath(module_name: str) -> str:
def resolve_collection_argument( def resolve_collection_argument(
invocation_path: Path, arg: str, *, as_pypath: bool = False invocation_path: Path, arg: str, *, as_pypath: bool = False
) -> Tuple[py.path.local, List[str]]: ) -> Tuple[Path, List[str]]:
"""Parse path arguments optionally containing selection parts and return (fspath, names). """Parse path arguments optionally containing selection parts and return (fspath, names).
Command-line arguments can point to files and/or directories, and optionally contain Command-line arguments can point to files and/or directories, and optionally contain
@ -875,4 +874,4 @@ def resolve_collection_argument(
else "directory argument cannot contain :: selection parts: {arg}" else "directory argument cannot contain :: selection parts: {arg}"
) )
raise UsageError(msg.format(arg=arg)) raise UsageError(msg.format(arg=arg))
return py.path.local(str(fspath)), parts return fspath, parts

View File

@ -4,7 +4,6 @@ import re
import sys import sys
import warnings import warnings
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path
from typing import Any from typing import Any
from typing import Generator from typing import Generator
from typing import List from typing import List
@ -325,20 +324,14 @@ class MonkeyPatch:
invalidate_caches() invalidate_caches()
def chdir(self, path) -> None: def chdir(self, path: Union[str, "os.PathLike[str]"]) -> None:
"""Change the current working directory to the specified path. """Change the current working directory to the specified path.
Path can be a string or a py.path.local object. Path can be a string or a path object.
""" """
if self._cwd is None: if self._cwd is None:
self._cwd = os.getcwd() self._cwd = os.getcwd()
if hasattr(path, "chdir"): os.chdir(path)
path.chdir()
elif isinstance(path, Path):
# Modern python uses the fspath protocol here LEGACY
os.chdir(str(path))
else:
os.chdir(path)
def undo(self) -> None: def undo(self) -> None:
"""Undo previous changes. """Undo previous changes.

View File

@ -480,10 +480,14 @@ class Collector(Node):
excinfo.traceback = ntraceback.filter() excinfo.traceback = ntraceback.filter()
def _check_initialpaths_for_relpath(session, fspath): def _check_initialpaths_for_relpath(
session: "Session", fspath: py.path.local
) -> Optional[str]:
for initial_path in session._initialpaths: for initial_path in session._initialpaths:
if fspath.common(initial_path) == initial_path: initial_path_ = py.path.local(initial_path)
return fspath.relto(initial_path) if fspath.common(initial_path_) == initial_path_:
return fspath.relto(initial_path_)
return None
class FSCollector(Collector): class FSCollector(Collector):

View File

@ -30,8 +30,6 @@ from typing import Set
from typing import TypeVar from typing import TypeVar
from typing import Union from typing import Union
import py
from _pytest.compat import assert_never from _pytest.compat import assert_never
from _pytest.outcomes import skip from _pytest.outcomes import skip
from _pytest.warning_types import PytestWarning from _pytest.warning_types import PytestWarning
@ -456,7 +454,7 @@ class ImportPathMismatchError(ImportError):
def import_path( def import_path(
p: Union[str, py.path.local, Path], p: Union[str, "os.PathLike[str]"],
*, *,
mode: Union[str, ImportMode] = ImportMode.prepend, mode: Union[str, ImportMode] = ImportMode.prepend,
) -> ModuleType: ) -> ModuleType:
@ -482,7 +480,7 @@ def import_path(
""" """
mode = ImportMode(mode) mode = ImportMode(mode)
path = Path(str(p)) path = Path(p)
if not path.exists(): if not path.exists():
raise ImportError(path) raise ImportError(path)

View File

@ -17,8 +17,6 @@ from typing import Mapping
from typing import Optional from typing import Optional
from typing import Set from typing import Set
import py
import _pytest._code import _pytest._code
import pytest import pytest
from _pytest.assertion import util from _pytest.assertion import util
@ -1311,7 +1309,7 @@ class TestEarlyRewriteBailout:
import importlib.machinery import importlib.machinery
self.find_spec_calls: List[str] = [] self.find_spec_calls: List[str] = []
self.initial_paths: Set[py.path.local] = set() self.initial_paths: Set[Path] = set()
class StubSession: class StubSession:
_initialpaths = self.initial_paths _initialpaths = self.initial_paths
@ -1346,7 +1344,7 @@ class TestEarlyRewriteBailout:
pytester.makepyfile(test_foo="def test_foo(): pass") pytester.makepyfile(test_foo="def test_foo(): pass")
pytester.makepyfile(bar="def bar(): pass") pytester.makepyfile(bar="def bar(): pass")
foobar_path = pytester.makepyfile(foobar="def foobar(): pass") foobar_path = pytester.makepyfile(foobar="def foobar(): pass")
self.initial_paths.add(py.path.local(foobar_path)) self.initial_paths.add(foobar_path)
# conftest files should always be rewritten # conftest files should always be rewritten
assert hook.find_spec("conftest") is not None assert hook.find_spec("conftest") is not None

View File

@ -4,13 +4,12 @@ import re
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
import py.path
import pytest import pytest
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.config import UsageError from _pytest.config import UsageError
from _pytest.main import resolve_collection_argument from _pytest.main import resolve_collection_argument
from _pytest.main import validate_basetemp from _pytest.main import validate_basetemp
from _pytest.pytester import Pytester
from _pytest.pytester import Testdir from _pytest.pytester import Testdir
@ -109,40 +108,37 @@ def test_validate_basetemp_integration(testdir):
class TestResolveCollectionArgument: class TestResolveCollectionArgument:
@pytest.fixture @pytest.fixture
def invocation_dir(self, testdir: Testdir) -> py.path.local: def invocation_path(self, pytester: Pytester) -> Path:
testdir.syspathinsert(str(testdir.tmpdir / "src")) pytester.syspathinsert(pytester.path / "src")
testdir.chdir() pytester.chdir()
pkg = testdir.tmpdir.join("src/pkg").ensure_dir() pkg = pytester.path.joinpath("src/pkg")
pkg.join("__init__.py").ensure() pkg.mkdir(parents=True)
pkg.join("test.py").ensure() pkg.joinpath("__init__.py").touch()
return testdir.tmpdir pkg.joinpath("test.py").touch()
return pytester.path
@pytest.fixture def test_file(self, invocation_path: Path) -> None:
def invocation_path(self, invocation_dir: py.path.local) -> Path:
return Path(str(invocation_dir))
def test_file(self, invocation_dir: py.path.local, invocation_path: Path) -> None:
"""File and parts.""" """File and parts."""
assert resolve_collection_argument(invocation_path, "src/pkg/test.py") == ( assert resolve_collection_argument(invocation_path, "src/pkg/test.py") == (
invocation_dir / "src/pkg/test.py", invocation_path / "src/pkg/test.py",
[], [],
) )
assert resolve_collection_argument(invocation_path, "src/pkg/test.py::") == ( assert resolve_collection_argument(invocation_path, "src/pkg/test.py::") == (
invocation_dir / "src/pkg/test.py", invocation_path / "src/pkg/test.py",
[""], [""],
) )
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, "src/pkg/test.py::foo::bar" invocation_path, "src/pkg/test.py::foo::bar"
) == (invocation_dir / "src/pkg/test.py", ["foo", "bar"]) ) == (invocation_path / "src/pkg/test.py", ["foo", "bar"])
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, "src/pkg/test.py::foo::bar::" invocation_path, "src/pkg/test.py::foo::bar::"
) == (invocation_dir / "src/pkg/test.py", ["foo", "bar", ""]) ) == (invocation_path / "src/pkg/test.py", ["foo", "bar", ""])
def test_dir(self, invocation_dir: py.path.local, invocation_path: Path) -> None: def test_dir(self, invocation_path: Path) -> None:
"""Directory and parts.""" """Directory and parts."""
assert resolve_collection_argument(invocation_path, "src/pkg") == ( assert resolve_collection_argument(invocation_path, "src/pkg") == (
invocation_dir / "src/pkg", invocation_path / "src/pkg",
[], [],
) )
@ -156,16 +152,16 @@ class TestResolveCollectionArgument:
): ):
resolve_collection_argument(invocation_path, "src/pkg::foo::bar") resolve_collection_argument(invocation_path, "src/pkg::foo::bar")
def test_pypath(self, invocation_dir: py.path.local, invocation_path: Path) -> None: def test_pypath(self, invocation_path: Path) -> None:
"""Dotted name and parts.""" """Dotted name and parts."""
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, "pkg.test", as_pypath=True invocation_path, "pkg.test", as_pypath=True
) == (invocation_dir / "src/pkg/test.py", []) ) == (invocation_path / "src/pkg/test.py", [])
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, "pkg.test::foo::bar", as_pypath=True invocation_path, "pkg.test::foo::bar", as_pypath=True
) == (invocation_dir / "src/pkg/test.py", ["foo", "bar"]) ) == (invocation_path / "src/pkg/test.py", ["foo", "bar"])
assert resolve_collection_argument(invocation_path, "pkg", as_pypath=True) == ( assert resolve_collection_argument(invocation_path, "pkg", as_pypath=True) == (
invocation_dir / "src/pkg", invocation_path / "src/pkg",
[], [],
) )
@ -191,13 +187,11 @@ class TestResolveCollectionArgument:
): ):
resolve_collection_argument(invocation_path, "foobar", as_pypath=True) resolve_collection_argument(invocation_path, "foobar", as_pypath=True)
def test_absolute_paths_are_resolved_correctly( def test_absolute_paths_are_resolved_correctly(self, invocation_path: Path) -> None:
self, invocation_dir: py.path.local, invocation_path: Path
) -> None:
"""Absolute paths resolve back to absolute paths.""" """Absolute paths resolve back to absolute paths."""
full_path = str(invocation_dir / "src") full_path = str(invocation_path / "src")
assert resolve_collection_argument(invocation_path, full_path) == ( assert resolve_collection_argument(invocation_path, full_path) == (
py.path.local(os.path.abspath("src")), Path(os.path.abspath("src")),
[], [],
) )
@ -206,7 +200,7 @@ class TestResolveCollectionArgument:
drive, full_path_without_drive = os.path.splitdrive(full_path) drive, full_path_without_drive = os.path.splitdrive(full_path)
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, full_path_without_drive invocation_path, full_path_without_drive
) == (py.path.local(os.path.abspath("src")), []) ) == (Path(os.path.abspath("src")), [])
def test_module_full_path_without_drive(testdir): def test_module_full_path_without_drive(testdir):

View File

@ -1,3 +1,4 @@
from typing import cast
from typing import List from typing import List
from typing import Type from typing import Type
@ -73,17 +74,21 @@ def test__check_initialpaths_for_relpath() -> None:
class FakeSession1: class FakeSession1:
_initialpaths = [cwd] _initialpaths = [cwd]
assert nodes._check_initialpaths_for_relpath(FakeSession1, cwd) == "" session = cast(pytest.Session, FakeSession1)
assert nodes._check_initialpaths_for_relpath(session, cwd) == ""
sub = cwd.join("file") sub = cwd.join("file")
class FakeSession2: class FakeSession2:
_initialpaths = [cwd] _initialpaths = [cwd]
assert nodes._check_initialpaths_for_relpath(FakeSession2, sub) == "file" session = cast(pytest.Session, FakeSession2)
assert nodes._check_initialpaths_for_relpath(session, sub) == "file"
outside = py.path.local("/outside") outside = py.path.local("/outside")
assert nodes._check_initialpaths_for_relpath(FakeSession2, outside) is None assert nodes._check_initialpaths_for_relpath(session, outside) is None
def test_failure_with_changed_cwd(pytester: Pytester) -> None: def test_failure_with_changed_cwd(pytester: Pytester) -> None: