Merge pull request #8144 from bluetech/py-to-pathlib-4

hookspec: add pathlib.Path alternatives to py.path.local parameters in hooks
This commit is contained in:
Ran Benita 2020-12-15 13:06:06 +02:00 committed by GitHub
commit 6c899a0afa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 160 additions and 119 deletions

View File

@ -0,0 +1,7 @@
The following hooks now receive an additional ``pathlib.Path`` argument, equivalent to an existing ``py.path.local`` argument:
- :func:`pytest_ignore_collect <_pytest.hookspec.pytest_ignore_collect>` - The ``fspath`` parameter (equivalent to existing ``path`` parameter).
- :func:`pytest_collect_file <_pytest.hookspec.pytest_collect_file>` - The ``fspath`` parameter (equivalent to existing ``path`` parameter).
- :func:`pytest_pycollect_makemodule <_pytest.hookspec.pytest_pycollect_makemodule>` - The ``fspath`` parameter (equivalent to existing ``path`` parameter).
- :func:`pytest_report_header <_pytest.hookspec.pytest_report_header>` - The ``startpath`` parameter (equivalent to existing ``startdir`` parameter).
- :func:`pytest_report_collectionfinish <_pytest.hookspec.pytest_report_collectionfinish>` - The ``startpath`` parameter (equivalent to existing ``startdir`` parameter).

View File

@ -27,8 +27,6 @@ from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
import py
from _pytest._io.saferepr import saferepr
from _pytest._version import version
from _pytest.assertion import util
@ -37,6 +35,7 @@ from _pytest.assertion.util import ( # noqa: F401
)
from _pytest.config import Config
from _pytest.main import Session
from _pytest.pathlib import absolutepath
from _pytest.pathlib import fnmatch_ex
from _pytest.store import StoreKey
@ -215,7 +214,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
return True
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}")
return True

View File

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

View File

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

View File

@ -1,5 +1,6 @@
"""Hook specifications for pytest plugins which are invoked by pytest itself
and by builtin plugins."""
from pathlib import Path
from typing import Any
from typing import Dict
from typing import List
@ -261,7 +262,9 @@ def pytest_collection_finish(session: "Session") -> None:
@hookspec(firstresult=True)
def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[bool]:
def pytest_ignore_collect(
fspath: Path, path: py.path.local, config: "Config"
) -> Optional[bool]:
"""Return True to prevent considering this path for collection.
This hook is consulted for all files and directories prior to calling
@ -269,19 +272,29 @@ def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[boo
Stops at first non-None result, see :ref:`firstresult`.
:param pathlib.Path fspath: The path to analyze.
:param py.path.local path: The path to analyze.
:param _pytest.config.Config config: The pytest config object.
.. versionchanged:: 6.3.0
The ``fspath`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``path`` parameter.
"""
def pytest_collect_file(
path: py.path.local, parent: "Collector"
fspath: Path, path: py.path.local, parent: "Collector"
) -> "Optional[Collector]":
"""Create a Collector for the given path, or None if not relevant.
The new node needs to have the specified ``parent`` as a parent.
:param pathlib.Path fspath: The path to analyze.
:param py.path.local path: The path to collect.
.. versionchanged:: 6.3.0
The ``fspath`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``path`` parameter.
"""
@ -321,7 +334,9 @@ def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectRepor
@hookspec(firstresult=True)
def pytest_pycollect_makemodule(path: py.path.local, parent) -> Optional["Module"]:
def pytest_pycollect_makemodule(
fspath: Path, path: py.path.local, parent
) -> Optional["Module"]:
"""Return a Module collector or None for the given path.
This hook will be called for each matching test module path.
@ -330,7 +345,12 @@ def pytest_pycollect_makemodule(path: py.path.local, parent) -> Optional["Module
Stops at first non-None result, see :ref:`firstresult`.
:param py.path.local path: The path of module to collect.
:param pathlib.Path fspath: The path of the module to collect.
:param py.path.local path: The path of the module to collect.
.. versionchanged:: 6.3.0
The ``fspath`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``path`` parameter.
"""
@ -653,11 +673,12 @@ def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> No
def pytest_report_header(
config: "Config", startdir: py.path.local
config: "Config", startpath: Path, startdir: py.path.local
) -> Union[str, List[str]]:
"""Return a string or list of strings to be displayed as header info for terminal reporting.
:param _pytest.config.Config config: The pytest config object.
:param Path startpath: The starting dir.
:param py.path.local startdir: The starting dir.
.. note::
@ -672,11 +693,15 @@ def pytest_report_header(
This function should be implemented only in plugins or ``conftest.py``
files situated at the tests root directory due to how pytest
:ref:`discovers plugins during startup <pluginorder>`.
.. versionchanged:: 6.3.0
The ``startpath`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``startdir`` parameter.
"""
def pytest_report_collectionfinish(
config: "Config", startdir: py.path.local, items: Sequence["Item"],
config: "Config", startpath: Path, startdir: py.path.local, items: Sequence["Item"],
) -> Union[str, List[str]]:
"""Return a string or list of strings to be displayed after collection
has finished successfully.
@ -686,6 +711,7 @@ def pytest_report_collectionfinish(
.. versionadded:: 3.2
:param _pytest.config.Config config: The pytest config object.
:param Path startpath: The starting path.
:param py.path.local startdir: The starting dir.
:param items: List of pytest items that are going to be executed; this list should not be modified.
@ -695,6 +721,10 @@ def pytest_report_collectionfinish(
ran before it.
If you want to have your line(s) displayed first, use
:ref:`trylast=True <plugin-hookorder>`.
.. versionchanged:: 6.3.0
The ``startpath`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``startdir`` parameter.
"""

View File

@ -467,7 +467,7 @@ class Session(nodes.FSCollector):
self.shouldfail: Union[bool, str] = False
self.trace = config.trace.root.get("collection")
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)
@ -510,8 +510,8 @@ class Session(nodes.FSCollector):
pytest_collectreport = pytest_runtest_logreport
def isinitpath(self, path: py.path.local) -> bool:
return path in self._initialpaths
def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool:
return Path(path) in self._initialpaths
def gethookproxy(self, fspath: "os.PathLike[str]"):
# Check if we have the common case of running
@ -532,9 +532,10 @@ class Session(nodes.FSCollector):
def _recurse(self, direntry: "os.DirEntry[str]") -> bool:
if direntry.name == "__pycache__":
return False
path = py.path.local(direntry.path)
ihook = self.gethookproxy(path.dirpath())
if ihook.pytest_ignore_collect(path=path, config=self.config):
fspath = Path(direntry.path)
path = py.path.local(fspath)
ihook = self.gethookproxy(fspath.parent)
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):
@ -544,6 +545,7 @@ class Session(nodes.FSCollector):
def _collectfile(
self, path: py.path.local, handle_dupes: bool = True
) -> Sequence[nodes.Collector]:
fspath = Path(path)
assert (
path.isfile()
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
@ -551,7 +553,9 @@ class Session(nodes.FSCollector):
)
ihook = self.gethookproxy(path)
if not self.isinitpath(path):
if ihook.pytest_ignore_collect(path=path, config=self.config):
if ihook.pytest_ignore_collect(
fspath=fspath, path=path, config=self.config
):
return ()
if handle_dupes:
@ -563,7 +567,7 @@ class Session(nodes.FSCollector):
else:
duplicate_paths.add(path)
return ihook.pytest_collect_file(path=path, parent=self) # type: ignore[no-any-return]
return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return]
@overload
def perform_collect(
@ -601,14 +605,14 @@ class Session(nodes.FSCollector):
self.trace.root.indent += 1
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] = []
hook = self.config.hook
items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items
try:
initialpaths: List[py.path.local] = []
initialpaths: List[Path] = []
for arg in args:
fspath, parts = resolve_collection_argument(
self.config.invocation_params.dir,
@ -669,13 +673,13 @@ class Session(nodes.FSCollector):
# No point in finding packages when collecting doctests.
if not self.config.getoption("doctestmodules", False):
pm = self.config.pluginmanager
confcutdir = py.path.local(pm._confcutdir) if pm._confcutdir else None
for parent in reversed(argpath.parts()):
if confcutdir and confcutdir.relto(parent):
confcutdir = pm._confcutdir
for parent in (argpath, *argpath.parents):
if confcutdir and parent in confcutdir.parents:
break
if parent.isdir():
pkginit = parent.join("__init__.py")
if parent.is_dir():
pkginit = py.path.local(parent / "__init__.py")
if pkginit.isfile() and pkginit not in node_cache1:
col = self._collectfile(pkginit, handle_dupes=False)
if col:
@ -685,7 +689,7 @@ class Session(nodes.FSCollector):
# 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.check(dir=1):
if argpath.is_dir():
assert not names, "invalid arg {!r}".format((argpath, names))
seen_dirs: Set[py.path.local] = set()
@ -717,15 +721,16 @@ class Session(nodes.FSCollector):
node_cache2[key] = x
yield x
else:
assert argpath.check(file=1)
assert argpath.is_file()
if argpath in node_cache1:
col = node_cache1[argpath]
argpath_ = py.path.local(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(argpath_.dirname, self)
col = collect_root._collectfile(argpath_, handle_dupes=False)
if col:
node_cache1[argpath] = col
node_cache1[argpath_] = col
matching = []
work: List[
@ -782,9 +787,7 @@ class Session(nodes.FSCollector):
# first yielded item will be the __init__ Module itself, so
# just use that. If this special case isn't taken, then all the
# files in the package will be yielded.
if argpath.basename == "__init__.py" and isinstance(
matching[0], Package
):
if argpath.name == "__init__.py" and isinstance(matching[0], Package):
try:
yield next(iter(matching[0].collect()))
except StopIteration:
@ -833,7 +836,7 @@ def search_pypath(module_name: str) -> str:
def resolve_collection_argument(
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).
Command-line arguments can point to files and/or directories, and optionally contain
@ -875,4 +878,4 @@ def resolve_collection_argument(
else "directory argument cannot contain :: selection parts: {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 warnings
from contextlib import contextmanager
from pathlib import Path
from typing import Any
from typing import Generator
from typing import List
@ -325,20 +324,14 @@ class MonkeyPatch:
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.
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:
self._cwd = os.getcwd()
if hasattr(path, "chdir"):
path.chdir()
elif isinstance(path, Path):
# Modern python uses the fspath protocol here LEGACY
os.chdir(str(path))
else:
os.chdir(path)
os.chdir(path)
def undo(self) -> None:
"""Undo previous changes.

View File

@ -480,10 +480,14 @@ class Collector(Node):
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:
if fspath.common(initial_path) == initial_path:
return fspath.relto(initial_path)
initial_path_ = py.path.local(initial_path)
if fspath.common(initial_path_) == initial_path_:
return fspath.relto(initial_path_)
return None
class FSCollector(Collector):

View File

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

View File

@ -10,6 +10,7 @@ import warnings
from collections import Counter
from collections import defaultdict
from functools import partial
from pathlib import Path
from typing import Any
from typing import Callable
from typing import Dict
@ -187,17 +188,19 @@ def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]:
def pytest_collect_file(
path: py.path.local, parent: nodes.Collector
fspath: Path, path: py.path.local, parent: nodes.Collector
) -> Optional["Module"]:
ext = path.ext
if ext == ".py":
if not parent.session.isinitpath(path):
if not parent.session.isinitpath(fspath):
if not path_matches_patterns(
path, parent.config.getini("python_files") + ["__init__.py"]
):
return None
ihook = parent.session.gethookproxy(path)
module: Module = ihook.pytest_pycollect_makemodule(path=path, parent=parent)
ihook = parent.session.gethookproxy(fspath)
module: Module = ihook.pytest_pycollect_makemodule(
fspath=fspath, path=path, parent=parent
)
return module
return None
@ -664,9 +667,10 @@ class Package(Module):
def _recurse(self, direntry: "os.DirEntry[str]") -> bool:
if direntry.name == "__pycache__":
return False
path = py.path.local(direntry.path)
ihook = self.session.gethookproxy(path.dirpath())
if ihook.pytest_ignore_collect(path=path, config=self.config):
fspath = Path(direntry.path)
path = py.path.local(fspath)
ihook = self.session.gethookproxy(fspath.parent)
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):
@ -676,6 +680,7 @@ class Package(Module):
def _collectfile(
self, path: py.path.local, handle_dupes: bool = True
) -> Sequence[nodes.Collector]:
fspath = Path(path)
assert (
path.isfile()
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
@ -683,7 +688,9 @@ class Package(Module):
)
ihook = self.session.gethookproxy(path)
if not self.session.isinitpath(path):
if ihook.pytest_ignore_collect(path=path, config=self.config):
if ihook.pytest_ignore_collect(
fspath=fspath, path=path, config=self.config
):
return ()
if handle_dupes:
@ -695,7 +702,7 @@ class Package(Module):
else:
duplicate_paths.add(path)
return ihook.pytest_collect_file(path=path, parent=self) # type: ignore[no-any-return]
return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return]
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
this_path = self.fspath.dirpath()

View File

@ -710,7 +710,7 @@ class TerminalReporter:
msg += " -- " + str(sys.executable)
self.write_line(msg)
lines = self.config.hook.pytest_report_header(
config=self.config, startdir=self.startdir
config=self.config, startpath=self.startpath, startdir=self.startdir
)
self._write_report_lines_from_hooks(lines)
@ -745,7 +745,10 @@ class TerminalReporter:
self.report_collect(True)
lines = self.config.hook.pytest_report_collectionfinish(
config=self.config, startdir=self.startdir, items=session.items
config=self.config,
startpath=self.startpath,
startdir=self.startdir,
items=session.items,
)
self._write_report_lines_from_hooks(lines)

View File

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

View File

@ -4,13 +4,12 @@ import re
from pathlib import Path
from typing import Optional
import py.path
import pytest
from _pytest.config import ExitCode
from _pytest.config import UsageError
from _pytest.main import resolve_collection_argument
from _pytest.main import validate_basetemp
from _pytest.pytester import Pytester
from _pytest.pytester import Testdir
@ -109,40 +108,37 @@ def test_validate_basetemp_integration(testdir):
class TestResolveCollectionArgument:
@pytest.fixture
def invocation_dir(self, testdir: Testdir) -> py.path.local:
testdir.syspathinsert(str(testdir.tmpdir / "src"))
testdir.chdir()
def invocation_path(self, pytester: Pytester) -> Path:
pytester.syspathinsert(pytester.path / "src")
pytester.chdir()
pkg = testdir.tmpdir.join("src/pkg").ensure_dir()
pkg.join("__init__.py").ensure()
pkg.join("test.py").ensure()
return testdir.tmpdir
pkg = pytester.path.joinpath("src/pkg")
pkg.mkdir(parents=True)
pkg.joinpath("__init__.py").touch()
pkg.joinpath("test.py").touch()
return pytester.path
@pytest.fixture
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:
def test_file(self, invocation_path: Path) -> None:
"""File and parts."""
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::") == (
invocation_dir / "src/pkg/test.py",
invocation_path / "src/pkg/test.py",
[""],
)
assert resolve_collection_argument(
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(
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."""
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")
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."""
assert resolve_collection_argument(
invocation_path, "pkg.test", as_pypath=True
) == (invocation_dir / "src/pkg/test.py", [])
) == (invocation_path / "src/pkg/test.py", [])
assert resolve_collection_argument(
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) == (
invocation_dir / "src/pkg",
invocation_path / "src/pkg",
[],
)
@ -191,13 +187,11 @@ class TestResolveCollectionArgument:
):
resolve_collection_argument(invocation_path, "foobar", as_pypath=True)
def test_absolute_paths_are_resolved_correctly(
self, invocation_dir: py.path.local, invocation_path: Path
) -> None:
def test_absolute_paths_are_resolved_correctly(self, invocation_path: Path) -> None:
"""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) == (
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)
assert resolve_collection_argument(
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):

View File

@ -1,3 +1,4 @@
from typing import cast
from typing import List
from typing import Type
@ -73,17 +74,21 @@ def test__check_initialpaths_for_relpath() -> None:
class FakeSession1:
_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")
class FakeSession2:
_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")
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:

View File

@ -1010,7 +1010,7 @@ class TestTerminalFunctional:
def test_report_collectionfinish_hook(self, pytester: Pytester, params) -> None:
pytester.makeconftest(
"""
def pytest_report_collectionfinish(config, startdir, items):
def pytest_report_collectionfinish(config, startpath, startdir, items):
return ['hello from hook: {0} items'.format(len(items))]
"""
)
@ -1436,8 +1436,8 @@ class TestGenericReporting:
)
pytester.mkdir("a").joinpath("conftest.py").write_text(
"""
def pytest_report_header(config, startdir):
return ["line1", str(startdir)]
def pytest_report_header(config, startdir, startpath):
return ["line1", str(startpath)]
"""
)
result = pytester.runpytest("a")