hookspec: add pathlib.Path alternatives to py.path.local parameters in hooks
As part of the ongoing migration for py.path to pathlib, make sure all hooks which take a py.path.local also take an equivalent pathlib.Path.
This commit is contained in:
parent
2cb34a99cb
commit
592b32bd69
|
@ -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).
|
|
@ -1,5 +1,6 @@
|
||||||
"""Hook specifications for pytest plugins which are invoked by pytest itself
|
"""Hook specifications for pytest plugins which are invoked by pytest itself
|
||||||
and by builtin plugins."""
|
and by builtin plugins."""
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import List
|
from typing import List
|
||||||
|
@ -261,7 +262,9 @@ def pytest_collection_finish(session: "Session") -> None:
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@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.
|
"""Return True to prevent considering this path for collection.
|
||||||
|
|
||||||
This hook is consulted for all files and directories prior to calling
|
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`.
|
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 py.path.local path: The path to analyze.
|
||||||
:param _pytest.config.Config config: The pytest config object.
|
: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(
|
def pytest_collect_file(
|
||||||
path: py.path.local, parent: "Collector"
|
fspath: Path, path: py.path.local, parent: "Collector"
|
||||||
) -> "Optional[Collector]":
|
) -> "Optional[Collector]":
|
||||||
"""Create a Collector for the given path, or None if not relevant.
|
"""Create a Collector for the given path, or None if not relevant.
|
||||||
|
|
||||||
The new node needs to have the specified ``parent`` as a parent.
|
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.
|
: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)
|
@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.
|
"""Return a Module collector or None for the given path.
|
||||||
|
|
||||||
This hook will be called for each matching test module 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`.
|
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(
|
def pytest_report_header(
|
||||||
config: "Config", startdir: py.path.local
|
config: "Config", startpath: Path, startdir: py.path.local
|
||||||
) -> Union[str, List[str]]:
|
) -> Union[str, List[str]]:
|
||||||
"""Return a string or list of strings to be displayed as header info for terminal reporting.
|
"""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 _pytest.config.Config config: The pytest config object.
|
||||||
|
:param Path startpath: The starting dir.
|
||||||
:param py.path.local startdir: The starting dir.
|
:param py.path.local startdir: The starting dir.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -672,11 +693,15 @@ def pytest_report_header(
|
||||||
This function should be implemented only in plugins or ``conftest.py``
|
This function should be implemented only in plugins or ``conftest.py``
|
||||||
files situated at the tests root directory due to how pytest
|
files situated at the tests root directory due to how pytest
|
||||||
:ref:`discovers plugins during startup <pluginorder>`.
|
: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(
|
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]]:
|
) -> Union[str, List[str]]:
|
||||||
"""Return a string or list of strings to be displayed after collection
|
"""Return a string or list of strings to be displayed after collection
|
||||||
has finished successfully.
|
has finished successfully.
|
||||||
|
@ -686,6 +711,7 @@ def pytest_report_collectionfinish(
|
||||||
.. versionadded:: 3.2
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
:param _pytest.config.Config config: The pytest config object.
|
:param _pytest.config.Config config: The pytest config object.
|
||||||
|
:param Path startpath: The starting path.
|
||||||
:param py.path.local startdir: The starting dir.
|
: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.
|
: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.
|
ran before it.
|
||||||
If you want to have your line(s) displayed first, use
|
If you want to have your line(s) displayed first, use
|
||||||
:ref:`trylast=True <plugin-hookorder>`.
|
:ref:`trylast=True <plugin-hookorder>`.
|
||||||
|
|
||||||
|
.. versionchanged:: 6.3.0
|
||||||
|
The ``startpath`` parameter was added as a :class:`pathlib.Path`
|
||||||
|
equivalent of the ``startdir`` parameter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -532,9 +532,10 @@ class Session(nodes.FSCollector):
|
||||||
def _recurse(self, direntry: "os.DirEntry[str]") -> bool:
|
def _recurse(self, direntry: "os.DirEntry[str]") -> bool:
|
||||||
if direntry.name == "__pycache__":
|
if direntry.name == "__pycache__":
|
||||||
return False
|
return False
|
||||||
path = py.path.local(direntry.path)
|
fspath = Path(direntry.path)
|
||||||
ihook = self.gethookproxy(path.dirpath())
|
path = py.path.local(fspath)
|
||||||
if ihook.pytest_ignore_collect(path=path, config=self.config):
|
ihook = self.gethookproxy(fspath.parent)
|
||||||
|
if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config):
|
||||||
return False
|
return False
|
||||||
norecursepatterns = self.config.getini("norecursedirs")
|
norecursepatterns = self.config.getini("norecursedirs")
|
||||||
if any(path.check(fnmatch=pat) for pat in norecursepatterns):
|
if any(path.check(fnmatch=pat) for pat in norecursepatterns):
|
||||||
|
@ -544,6 +545,7 @@ class Session(nodes.FSCollector):
|
||||||
def _collectfile(
|
def _collectfile(
|
||||||
self, path: py.path.local, handle_dupes: bool = True
|
self, path: py.path.local, handle_dupes: bool = True
|
||||||
) -> Sequence[nodes.Collector]:
|
) -> Sequence[nodes.Collector]:
|
||||||
|
fspath = Path(path)
|
||||||
assert (
|
assert (
|
||||||
path.isfile()
|
path.isfile()
|
||||||
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
|
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
|
||||||
|
@ -551,7 +553,9 @@ class Session(nodes.FSCollector):
|
||||||
)
|
)
|
||||||
ihook = self.gethookproxy(path)
|
ihook = self.gethookproxy(path)
|
||||||
if not self.isinitpath(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 ()
|
return ()
|
||||||
|
|
||||||
if handle_dupes:
|
if handle_dupes:
|
||||||
|
@ -563,7 +567,7 @@ class Session(nodes.FSCollector):
|
||||||
else:
|
else:
|
||||||
duplicate_paths.add(path)
|
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
|
@overload
|
||||||
def perform_collect(
|
def perform_collect(
|
||||||
|
|
|
@ -10,6 +10,7 @@ import warnings
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
@ -187,17 +188,19 @@ def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]:
|
||||||
|
|
||||||
|
|
||||||
def pytest_collect_file(
|
def pytest_collect_file(
|
||||||
path: py.path.local, parent: nodes.Collector
|
fspath: Path, path: py.path.local, parent: nodes.Collector
|
||||||
) -> Optional["Module"]:
|
) -> Optional["Module"]:
|
||||||
ext = path.ext
|
ext = path.ext
|
||||||
if ext == ".py":
|
if ext == ".py":
|
||||||
if not parent.session.isinitpath(path):
|
if not parent.session.isinitpath(fspath):
|
||||||
if not path_matches_patterns(
|
if not path_matches_patterns(
|
||||||
path, parent.config.getini("python_files") + ["__init__.py"]
|
path, parent.config.getini("python_files") + ["__init__.py"]
|
||||||
):
|
):
|
||||||
return None
|
return None
|
||||||
ihook = parent.session.gethookproxy(path)
|
ihook = parent.session.gethookproxy(fspath)
|
||||||
module: Module = ihook.pytest_pycollect_makemodule(path=path, parent=parent)
|
module: Module = ihook.pytest_pycollect_makemodule(
|
||||||
|
fspath=fspath, path=path, parent=parent
|
||||||
|
)
|
||||||
return module
|
return module
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -664,9 +667,10 @@ class Package(Module):
|
||||||
def _recurse(self, direntry: "os.DirEntry[str]") -> bool:
|
def _recurse(self, direntry: "os.DirEntry[str]") -> bool:
|
||||||
if direntry.name == "__pycache__":
|
if direntry.name == "__pycache__":
|
||||||
return False
|
return False
|
||||||
path = py.path.local(direntry.path)
|
fspath = Path(direntry.path)
|
||||||
ihook = self.session.gethookproxy(path.dirpath())
|
path = py.path.local(fspath)
|
||||||
if ihook.pytest_ignore_collect(path=path, config=self.config):
|
ihook = self.session.gethookproxy(fspath.parent)
|
||||||
|
if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config):
|
||||||
return False
|
return False
|
||||||
norecursepatterns = self.config.getini("norecursedirs")
|
norecursepatterns = self.config.getini("norecursedirs")
|
||||||
if any(path.check(fnmatch=pat) for pat in norecursepatterns):
|
if any(path.check(fnmatch=pat) for pat in norecursepatterns):
|
||||||
|
@ -676,6 +680,7 @@ class Package(Module):
|
||||||
def _collectfile(
|
def _collectfile(
|
||||||
self, path: py.path.local, handle_dupes: bool = True
|
self, path: py.path.local, handle_dupes: bool = True
|
||||||
) -> Sequence[nodes.Collector]:
|
) -> Sequence[nodes.Collector]:
|
||||||
|
fspath = Path(path)
|
||||||
assert (
|
assert (
|
||||||
path.isfile()
|
path.isfile()
|
||||||
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
|
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
|
||||||
|
@ -683,7 +688,9 @@ class Package(Module):
|
||||||
)
|
)
|
||||||
ihook = self.session.gethookproxy(path)
|
ihook = self.session.gethookproxy(path)
|
||||||
if not self.session.isinitpath(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 ()
|
return ()
|
||||||
|
|
||||||
if handle_dupes:
|
if handle_dupes:
|
||||||
|
@ -695,7 +702,7 @@ class Package(Module):
|
||||||
else:
|
else:
|
||||||
duplicate_paths.add(path)
|
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]]:
|
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
|
||||||
this_path = self.fspath.dirpath()
|
this_path = self.fspath.dirpath()
|
||||||
|
|
|
@ -710,7 +710,7 @@ class TerminalReporter:
|
||||||
msg += " -- " + str(sys.executable)
|
msg += " -- " + str(sys.executable)
|
||||||
self.write_line(msg)
|
self.write_line(msg)
|
||||||
lines = self.config.hook.pytest_report_header(
|
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)
|
self._write_report_lines_from_hooks(lines)
|
||||||
|
|
||||||
|
@ -745,7 +745,10 @@ class TerminalReporter:
|
||||||
self.report_collect(True)
|
self.report_collect(True)
|
||||||
|
|
||||||
lines = self.config.hook.pytest_report_collectionfinish(
|
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)
|
self._write_report_lines_from_hooks(lines)
|
||||||
|
|
||||||
|
|
|
@ -1010,7 +1010,7 @@ class TestTerminalFunctional:
|
||||||
def test_report_collectionfinish_hook(self, pytester: Pytester, params) -> None:
|
def test_report_collectionfinish_hook(self, pytester: Pytester, params) -> None:
|
||||||
pytester.makeconftest(
|
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))]
|
return ['hello from hook: {0} items'.format(len(items))]
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
@ -1436,8 +1436,8 @@ class TestGenericReporting:
|
||||||
)
|
)
|
||||||
pytester.mkdir("a").joinpath("conftest.py").write_text(
|
pytester.mkdir("a").joinpath("conftest.py").write_text(
|
||||||
"""
|
"""
|
||||||
def pytest_report_header(config, startdir):
|
def pytest_report_header(config, startdir, startpath):
|
||||||
return ["line1", str(startdir)]
|
return ["line1", str(startpath)]
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("a")
|
result = pytester.runpytest("a")
|
||||||
|
|
Loading…
Reference in New Issue