Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hookspec: add pathlib.Path alternatives to py.path.local parameters in hooks #8144

Merged
merged 2 commits into from
Dec 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog/8144.feature.rst
Original file line number Diff line number Diff line change
@@ -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).
5 changes: 2 additions & 3 deletions src/_pytest/assertion/rewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
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
Expand All @@ -37,6 +35,7 @@
)
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

Expand Down Expand Up @@ -215,7 +214,7 @@ def _should_rewrite(self, name: str, fn: str, state: "AssertionState") -> bool:
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

Expand Down
15 changes: 7 additions & 8 deletions src/_pytest/config/argparsing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import argparse
import os
import sys
import warnings
from gettext import gettext
Expand All @@ -14,8 +15,6 @@
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
Expand Down Expand Up @@ -97,14 +96,14 @@ def addoption(self, *opts: str, **attrs: Any) -> None:

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":
Expand All @@ -128,7 +127,7 @@ def _getparser(self) -> "MyOptionParser":

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]:
Expand All @@ -139,21 +138,21 @@ def parse_setoption(

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."""
return self.parse_known_and_unknown_args(args, namespace=namespace)[0]

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(
Expand Down
15 changes: 8 additions & 7 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,12 +648,13 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None:
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"
Expand Down Expand Up @@ -876,7 +877,7 @@ def formatrepr(self) -> "FixtureLookupErrorRepr":
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,
Expand All @@ -903,7 +904,7 @@ def toterminal(self, tw: TerminalWriter) -> None:
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":
Expand Down
42 changes: 36 additions & 6 deletions src/_pytest/hookspec.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -261,27 +262,39 @@ 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
more specific hooks.

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.
"""


Expand Down Expand Up @@ -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.
Expand All @@ -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.
"""


Expand Down Expand Up @@ -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::
Expand All @@ -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.
Expand All @@ -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.

Expand All @@ -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.
"""


Expand Down
Loading