[8.1.x] Revert legacy path removals (#12093)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
This commit is contained in:
		
							parent
							
								
									86c3aab005
								
							
						
					
					
						commit
						15fbe57c44
					
				| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
Delayed the deprecation of the following features to ``9.0.0``:
 | 
			
		||||
 | 
			
		||||
* :ref:`node-ctor-fspath-deprecation`.
 | 
			
		||||
* :ref:`legacy-path-hooks-deprecated`.
 | 
			
		||||
 | 
			
		||||
It was discovered after ``8.1.0`` was released that the warnings about the impeding removal were not being displayed, so the team decided to revert the removal.
 | 
			
		||||
 | 
			
		||||
This was the reason for ``8.1.0`` being yanked.
 | 
			
		||||
| 
						 | 
				
			
			@ -200,6 +200,7 @@ nitpick_ignore = [
 | 
			
		|||
    ("py:class", "_tracing.TagTracerSub"),
 | 
			
		||||
    ("py:class", "warnings.WarningMessage"),
 | 
			
		||||
    # Undocumented type aliases
 | 
			
		||||
    ("py:class", "LEGACY_PATH"),
 | 
			
		||||
    ("py:class", "_PluggyPlugin"),
 | 
			
		||||
    # TypeVars
 | 
			
		||||
    ("py:class", "_pytest._code.code.E"),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,45 @@ Below is a complete list of all pytest features which are considered deprecated.
 | 
			
		|||
:class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _legacy-path-hooks-deprecated:
 | 
			
		||||
.. _node-ctor-fspath-deprecation:
 | 
			
		||||
 | 
			
		||||
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
.. deprecated:: 7.0
 | 
			
		||||
 | 
			
		||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`,
 | 
			
		||||
the ``fspath`` argument to :class:`~_pytest.nodes.Node` constructors like
 | 
			
		||||
:func:`pytest.Function.from_parent()` and :func:`pytest.Class.from_parent()`
 | 
			
		||||
is now deprecated.
 | 
			
		||||
 | 
			
		||||
Plugins which construct nodes should pass the ``path`` argument, of type
 | 
			
		||||
:class:`pathlib.Path`, instead of the ``fspath`` argument.
 | 
			
		||||
 | 
			
		||||
Plugins which implement custom items and collectors are encouraged to replace
 | 
			
		||||
``fspath`` parameters (``py.path.local``) with ``path`` parameters
 | 
			
		||||
(``pathlib.Path``), and drop any other usage of the ``py`` library if possible.
 | 
			
		||||
 | 
			
		||||
If possible, plugins with custom items should use :ref:`cooperative
 | 
			
		||||
constructors <uncooperative-constructors-deprecated>` to avoid hardcoding
 | 
			
		||||
arguments they only pass on to the superclass.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    The name of the :class:`~_pytest.nodes.Node` arguments and attributes (the
 | 
			
		||||
    new attribute being ``path``) is **the opposite** of the situation for
 | 
			
		||||
    hooks, :ref:`outlined below <legacy-path-hooks-deprecated>` (the old
 | 
			
		||||
    argument being ``path``).
 | 
			
		||||
 | 
			
		||||
    This is an unfortunate artifact due to historical reasons, which should be
 | 
			
		||||
    resolved in future versions as we slowly get rid of the :pypi:`py`
 | 
			
		||||
    dependency (see :issue:`9283` for a longer discussion).
 | 
			
		||||
 | 
			
		||||
Due to the ongoing migration of methods like :meth:`~pytest.Item.reportinfo`
 | 
			
		||||
which still is expected to return a ``py.path.local`` object, nodes still have
 | 
			
		||||
both ``fspath`` (``py.path.local``) and ``path`` (``pathlib.Path``) attributes,
 | 
			
		||||
no matter what argument was used in the constructor. We expect to deprecate the
 | 
			
		||||
``fspath`` attribute in a future release.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Configuring hook specs/impls using markers
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +100,33 @@ Changed ``hookwrapper`` attributes:
 | 
			
		|||
* ``historic``
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _legacy-path-hooks-deprecated:
 | 
			
		||||
 | 
			
		||||
``py.path.local`` arguments for hooks replaced with ``pathlib.Path``
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
.. deprecated:: 7.0
 | 
			
		||||
 | 
			
		||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`, the following hooks now receive additional arguments:
 | 
			
		||||
 | 
			
		||||
*  :hook:`pytest_ignore_collect(collection_path: pathlib.Path) <pytest_ignore_collect>` as equivalent to ``path``
 | 
			
		||||
*  :hook:`pytest_collect_file(file_path: pathlib.Path) <pytest_collect_file>` as equivalent to ``path``
 | 
			
		||||
*  :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) <pytest_pycollect_makemodule>` as equivalent to ``path``
 | 
			
		||||
*  :hook:`pytest_report_header(start_path: pathlib.Path) <pytest_report_header>` as equivalent to ``startdir``
 | 
			
		||||
*  :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) <pytest_report_collectionfinish>` as equivalent to ``startdir``
 | 
			
		||||
 | 
			
		||||
The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    The name of the :class:`~_pytest.nodes.Node` arguments and attributes,
 | 
			
		||||
    :ref:`outlined above <node-ctor-fspath-deprecation>` (the new attribute
 | 
			
		||||
    being ``path``) is **the opposite** of the situation for hooks (the old
 | 
			
		||||
    argument being ``path``).
 | 
			
		||||
 | 
			
		||||
    This is an unfortunate artifact due to historical reasons, which should be
 | 
			
		||||
    resolved in future versions as we slowly get rid of the :pypi:`py`
 | 
			
		||||
    dependency (see :issue:`9283` for a longer discussion).
 | 
			
		||||
 | 
			
		||||
Directly constructing internal classes
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -208,73 +273,6 @@ an appropriate period of deprecation has passed.
 | 
			
		|||
 | 
			
		||||
Some breaking changes which could not be deprecated are also listed.
 | 
			
		||||
 | 
			
		||||
.. _node-ctor-fspath-deprecation:
 | 
			
		||||
 | 
			
		||||
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
.. deprecated:: 7.0
 | 
			
		||||
 | 
			
		||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`,
 | 
			
		||||
the ``fspath`` argument to :class:`~_pytest.nodes.Node` constructors like
 | 
			
		||||
:func:`pytest.Function.from_parent()` and :func:`pytest.Class.from_parent()`
 | 
			
		||||
is now deprecated.
 | 
			
		||||
 | 
			
		||||
Plugins which construct nodes should pass the ``path`` argument, of type
 | 
			
		||||
:class:`pathlib.Path`, instead of the ``fspath`` argument.
 | 
			
		||||
 | 
			
		||||
Plugins which implement custom items and collectors are encouraged to replace
 | 
			
		||||
``fspath`` parameters (``py.path.local``) with ``path`` parameters
 | 
			
		||||
(``pathlib.Path``), and drop any other usage of the ``py`` library if possible.
 | 
			
		||||
 | 
			
		||||
If possible, plugins with custom items should use :ref:`cooperative
 | 
			
		||||
constructors <uncooperative-constructors-deprecated>` to avoid hardcoding
 | 
			
		||||
arguments they only pass on to the superclass.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    The name of the :class:`~_pytest.nodes.Node` arguments and attributes (the
 | 
			
		||||
    new attribute being ``path``) is **the opposite** of the situation for
 | 
			
		||||
    hooks, :ref:`outlined below <legacy-path-hooks-deprecated>` (the old
 | 
			
		||||
    argument being ``path``).
 | 
			
		||||
 | 
			
		||||
    This is an unfortunate artifact due to historical reasons, which should be
 | 
			
		||||
    resolved in future versions as we slowly get rid of the :pypi:`py`
 | 
			
		||||
    dependency (see :issue:`9283` for a longer discussion).
 | 
			
		||||
 | 
			
		||||
Due to the ongoing migration of methods like :meth:`~pytest.Item.reportinfo`
 | 
			
		||||
which still is expected to return a ``py.path.local`` object, nodes still have
 | 
			
		||||
both ``fspath`` (``py.path.local``) and ``path`` (``pathlib.Path``) attributes,
 | 
			
		||||
no matter what argument was used in the constructor. We expect to deprecate the
 | 
			
		||||
``fspath`` attribute in a future release.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
``py.path.local`` arguments for hooks replaced with ``pathlib.Path``
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
.. deprecated:: 7.0
 | 
			
		||||
.. versionremoved:: 8.0
 | 
			
		||||
 | 
			
		||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`, the following hooks now receive additional arguments:
 | 
			
		||||
 | 
			
		||||
*  :hook:`pytest_ignore_collect(collection_path: pathlib.Path) <pytest_ignore_collect>` as equivalent to ``path``
 | 
			
		||||
*  :hook:`pytest_collect_file(file_path: pathlib.Path) <pytest_collect_file>` as equivalent to ``path``
 | 
			
		||||
*  :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) <pytest_pycollect_makemodule>` as equivalent to ``path``
 | 
			
		||||
*  :hook:`pytest_report_header(start_path: pathlib.Path) <pytest_report_header>` as equivalent to ``startdir``
 | 
			
		||||
*  :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) <pytest_report_collectionfinish>` as equivalent to ``startdir``
 | 
			
		||||
 | 
			
		||||
The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    The name of the :class:`~_pytest.nodes.Node` arguments and attributes,
 | 
			
		||||
    :ref:`outlined above <node-ctor-fspath-deprecation>` (the new attribute
 | 
			
		||||
    being ``path``) is **the opposite** of the situation for hooks (the old
 | 
			
		||||
    argument being ``path``).
 | 
			
		||||
 | 
			
		||||
    This is an unfortunate artifact due to historical reasons, which should be
 | 
			
		||||
    resolved in future versions as we slowly get rid of the :pypi:`py`
 | 
			
		||||
    dependency (see :issue:`9283` for a longer discussion).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _nose-deprecation:
 | 
			
		||||
 | 
			
		||||
Support for tests written for nose
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
# mypy: allow-untyped-defs
 | 
			
		||||
"""Python version compatibility code."""
 | 
			
		||||
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
import dataclasses
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +17,22 @@ from typing import Callable
 | 
			
		|||
from typing import Final
 | 
			
		||||
from typing import NoReturn
 | 
			
		||||
 | 
			
		||||
import py
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#: constant to prepare valuing pylib path replacements/lazy proxies later on
 | 
			
		||||
#  intended for removal in pytest 8.0 or 9.0
 | 
			
		||||
 | 
			
		||||
# fmt: off
 | 
			
		||||
# intentional space to create a fake difference for the verification
 | 
			
		||||
LEGACY_PATH = py.path. local
 | 
			
		||||
# fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def legacy_path(path: str | os.PathLike[str]) -> LEGACY_PATH:
 | 
			
		||||
    """Internal wrapper to prepare lazy proxies for legacy_path instances"""
 | 
			
		||||
    return LEGACY_PATH(path)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# fmt: off
 | 
			
		||||
# Singleton type for NOTSET, as described in:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,12 +38,14 @@ from typing import TYPE_CHECKING
 | 
			
		|||
from typing import Union
 | 
			
		||||
import warnings
 | 
			
		||||
 | 
			
		||||
import pluggy
 | 
			
		||||
from pluggy import HookimplMarker
 | 
			
		||||
from pluggy import HookimplOpts
 | 
			
		||||
from pluggy import HookspecMarker
 | 
			
		||||
from pluggy import HookspecOpts
 | 
			
		||||
from pluggy import PluginManager
 | 
			
		||||
 | 
			
		||||
from .compat import PathAwareHookProxy
 | 
			
		||||
from .exceptions import PrintHelp as PrintHelp
 | 
			
		||||
from .exceptions import UsageError as UsageError
 | 
			
		||||
from .findpaths import determine_setup
 | 
			
		||||
| 
						 | 
				
			
			@ -1068,7 +1070,7 @@ class Config:
 | 
			
		|||
        self._store = self.stash
 | 
			
		||||
 | 
			
		||||
        self.trace = self.pluginmanager.trace.root.get("config")
 | 
			
		||||
        self.hook = self.pluginmanager.hook  # type: ignore[assignment]
 | 
			
		||||
        self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook)  # type: ignore[assignment]
 | 
			
		||||
        self._inicache: Dict[str, Any] = {}
 | 
			
		||||
        self._override_ini: Sequence[str] = ()
 | 
			
		||||
        self._opt2dest: Dict[str, str] = {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
import functools
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Any
 | 
			
		||||
from typing import Mapping
 | 
			
		||||
import warnings
 | 
			
		||||
 | 
			
		||||
import pluggy
 | 
			
		||||
 | 
			
		||||
from ..compat import LEGACY_PATH
 | 
			
		||||
from ..compat import legacy_path
 | 
			
		||||
from ..deprecated import HOOK_LEGACY_PATH_ARG
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# hookname: (Path, LEGACY_PATH)
 | 
			
		||||
imply_paths_hooks: Mapping[str, tuple[str, str]] = {
 | 
			
		||||
    "pytest_ignore_collect": ("collection_path", "path"),
 | 
			
		||||
    "pytest_collect_file": ("file_path", "path"),
 | 
			
		||||
    "pytest_pycollect_makemodule": ("module_path", "path"),
 | 
			
		||||
    "pytest_report_header": ("start_path", "startdir"),
 | 
			
		||||
    "pytest_report_collectionfinish": ("start_path", "startdir"),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _check_path(path: Path, fspath: LEGACY_PATH) -> None:
 | 
			
		||||
    if Path(fspath) != path:
 | 
			
		||||
        raise ValueError(
 | 
			
		||||
            f"Path({fspath!r}) != {path!r}\n"
 | 
			
		||||
            "if both path and fspath are given they need to be equal"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PathAwareHookProxy:
 | 
			
		||||
    """
 | 
			
		||||
    this helper wraps around hook callers
 | 
			
		||||
    until pluggy supports fixingcalls, this one will do
 | 
			
		||||
 | 
			
		||||
    it currently doesn't return full hook caller proxies for fixed hooks,
 | 
			
		||||
    this may have to be changed later depending on bugs
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, hook_relay: pluggy.HookRelay) -> None:
 | 
			
		||||
        self._hook_relay = hook_relay
 | 
			
		||||
 | 
			
		||||
    def __dir__(self) -> list[str]:
 | 
			
		||||
        return dir(self._hook_relay)
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, key: str) -> pluggy.HookCaller:
 | 
			
		||||
        hook: pluggy.HookCaller = getattr(self._hook_relay, key)
 | 
			
		||||
        if key not in imply_paths_hooks:
 | 
			
		||||
            self.__dict__[key] = hook
 | 
			
		||||
            return hook
 | 
			
		||||
        else:
 | 
			
		||||
            path_var, fspath_var = imply_paths_hooks[key]
 | 
			
		||||
 | 
			
		||||
            @functools.wraps(hook)
 | 
			
		||||
            def fixed_hook(**kw: Any) -> Any:
 | 
			
		||||
                path_value: Path | None = kw.pop(path_var, None)
 | 
			
		||||
                fspath_value: LEGACY_PATH | None = kw.pop(fspath_var, None)
 | 
			
		||||
                if fspath_value is not None:
 | 
			
		||||
                    warnings.warn(
 | 
			
		||||
                        HOOK_LEGACY_PATH_ARG.format(
 | 
			
		||||
                            pylib_path_arg=fspath_var, pathlib_path_arg=path_var
 | 
			
		||||
                        ),
 | 
			
		||||
                        stacklevel=2,
 | 
			
		||||
                    )
 | 
			
		||||
                if path_value is not None:
 | 
			
		||||
                    if fspath_value is not None:
 | 
			
		||||
                        _check_path(path_value, fspath_value)
 | 
			
		||||
                    else:
 | 
			
		||||
                        fspath_value = legacy_path(path_value)
 | 
			
		||||
                else:
 | 
			
		||||
                    assert fspath_value is not None
 | 
			
		||||
                    path_value = Path(fspath_value)
 | 
			
		||||
 | 
			
		||||
                kw[path_var] = path_value
 | 
			
		||||
                kw[fspath_var] = fspath_value
 | 
			
		||||
                return hook(**kw)
 | 
			
		||||
 | 
			
		||||
            fixed_hook.name = hook.name  # type: ignore[attr-defined]
 | 
			
		||||
            fixed_hook.spec = hook.spec  # type: ignore[attr-defined]
 | 
			
		||||
            fixed_hook.__name__ = key
 | 
			
		||||
            self.__dict__[key] = fixed_hook
 | 
			
		||||
            return fixed_hook  # type: ignore[return-value]
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +36,21 @@ YIELD_FIXTURE = PytestDeprecationWarning(
 | 
			
		|||
PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
HOOK_LEGACY_PATH_ARG = UnformattedWarning(
 | 
			
		||||
    PytestRemovedIn9Warning,
 | 
			
		||||
    "The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n"
 | 
			
		||||
    "see https://docs.pytest.org/en/latest/deprecations.html"
 | 
			
		||||
    "#py-path-local-arguments-for-hooks-replaced-with-pathlib-path",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
NODE_CTOR_FSPATH_ARG = UnformattedWarning(
 | 
			
		||||
    PytestRemovedIn9Warning,
 | 
			
		||||
    "The (fspath: py.path.local) argument to {node_type_name} is deprecated. "
 | 
			
		||||
    "Please use the (path: pathlib.Path) argument instead.\n"
 | 
			
		||||
    "See https://docs.pytest.org/en/latest/deprecations.html"
 | 
			
		||||
    "#fspath-argument-for-node-constructors-replaced-with-pathlib-path",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
HOOK_LEGACY_MARKING = UnformattedWarning(
 | 
			
		||||
    PytestDeprecationWarning,
 | 
			
		||||
    "The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@ if TYPE_CHECKING:
 | 
			
		|||
 | 
			
		||||
    from _pytest._code.code import ExceptionInfo
 | 
			
		||||
    from _pytest._code.code import ExceptionRepr
 | 
			
		||||
    from _pytest.compat import LEGACY_PATH
 | 
			
		||||
    from _pytest.config import _PluggyPlugin
 | 
			
		||||
    from _pytest.config import Config
 | 
			
		||||
    from _pytest.config import ExitCode
 | 
			
		||||
| 
						 | 
				
			
			@ -296,7 +297,9 @@ def pytest_collection_finish(session: "Session") -> None:
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
@hookspec(firstresult=True)
 | 
			
		||||
def pytest_ignore_collect(collection_path: Path, config: "Config") -> Optional[bool]:
 | 
			
		||||
def pytest_ignore_collect(
 | 
			
		||||
    collection_path: Path, path: "LEGACY_PATH", 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
 | 
			
		||||
| 
						 | 
				
			
			@ -310,10 +313,8 @@ def pytest_ignore_collect(collection_path: Path, config: "Config") -> Optional[b
 | 
			
		|||
 | 
			
		||||
    .. versionchanged:: 7.0.0
 | 
			
		||||
        The ``collection_path`` parameter was added as a :class:`pathlib.Path`
 | 
			
		||||
        equivalent of the ``path`` parameter.
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 8.0.0
 | 
			
		||||
        The ``path`` parameter has been removed.
 | 
			
		||||
        equivalent of the ``path`` parameter. The ``path`` parameter
 | 
			
		||||
        has been deprecated.
 | 
			
		||||
 | 
			
		||||
    Use in conftest plugins
 | 
			
		||||
    =======================
 | 
			
		||||
| 
						 | 
				
			
			@ -354,7 +355,9 @@ def pytest_collect_directory(path: Path, parent: "Collector") -> "Optional[Colle
 | 
			
		|||
    """
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pytest_collect_file(file_path: Path, parent: "Collector") -> "Optional[Collector]":
 | 
			
		||||
def pytest_collect_file(
 | 
			
		||||
    file_path: Path, path: "LEGACY_PATH", parent: "Collector"
 | 
			
		||||
) -> "Optional[Collector]":
 | 
			
		||||
    """Create a :class:`~pytest.Collector` for the given path, or None if not relevant.
 | 
			
		||||
 | 
			
		||||
    For best results, the returned collector should be a subclass of
 | 
			
		||||
| 
						 | 
				
			
			@ -367,10 +370,8 @@ def pytest_collect_file(file_path: Path, parent: "Collector") -> "Optional[Colle
 | 
			
		|||
 | 
			
		||||
    .. versionchanged:: 7.0.0
 | 
			
		||||
        The ``file_path`` parameter was added as a :class:`pathlib.Path`
 | 
			
		||||
        equivalent of the ``path`` parameter.
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 8.0.0
 | 
			
		||||
        The ``path`` parameter was removed.
 | 
			
		||||
        equivalent of the ``path`` parameter. The ``path`` parameter
 | 
			
		||||
        has been deprecated.
 | 
			
		||||
 | 
			
		||||
    Use in conftest plugins
 | 
			
		||||
    =======================
 | 
			
		||||
| 
						 | 
				
			
			@ -467,7 +468,9 @@ def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectRepor
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
@hookspec(firstresult=True)
 | 
			
		||||
def pytest_pycollect_makemodule(module_path: Path, parent) -> Optional["Module"]:
 | 
			
		||||
def pytest_pycollect_makemodule(
 | 
			
		||||
    module_path: Path, path: "LEGACY_PATH", parent
 | 
			
		||||
) -> Optional["Module"]:
 | 
			
		||||
    """Return a :class:`pytest.Module` collector or None for the given path.
 | 
			
		||||
 | 
			
		||||
    This hook will be called for each matching test module path.
 | 
			
		||||
| 
						 | 
				
			
			@ -483,8 +486,7 @@ def pytest_pycollect_makemodule(module_path: Path, parent) -> Optional["Module"]
 | 
			
		|||
        The ``module_path`` parameter was added as a :class:`pathlib.Path`
 | 
			
		||||
        equivalent of the ``path`` parameter.
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 8.0.0
 | 
			
		||||
        The ``path`` parameter has been removed in favor of ``module_path``.
 | 
			
		||||
        The ``path`` parameter has been deprecated in favor of ``fspath``.
 | 
			
		||||
 | 
			
		||||
    Use in conftest plugins
 | 
			
		||||
    =======================
 | 
			
		||||
| 
						 | 
				
			
			@ -992,7 +994,7 @@ def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> No
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def pytest_report_header(  # type:ignore[empty-body]
 | 
			
		||||
    config: "Config", start_path: Path
 | 
			
		||||
    config: "Config", start_path: Path, startdir: "LEGACY_PATH"
 | 
			
		||||
) -> Union[str, List[str]]:
 | 
			
		||||
    """Return a string or list of strings to be displayed as header info for terminal reporting.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1009,10 +1011,8 @@ def pytest_report_header(  # type:ignore[empty-body]
 | 
			
		|||
 | 
			
		||||
    .. versionchanged:: 7.0.0
 | 
			
		||||
        The ``start_path`` parameter was added as a :class:`pathlib.Path`
 | 
			
		||||
        equivalent of the ``startdir`` parameter.
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 8.0.0
 | 
			
		||||
        The ``startdir`` parameter has been removed.
 | 
			
		||||
        equivalent of the ``startdir`` parameter. The ``startdir`` parameter
 | 
			
		||||
        has been deprecated.
 | 
			
		||||
 | 
			
		||||
    Use in conftest plugins
 | 
			
		||||
    =======================
 | 
			
		||||
| 
						 | 
				
			
			@ -1024,6 +1024,7 @@ def pytest_report_header(  # type:ignore[empty-body]
 | 
			
		|||
def pytest_report_collectionfinish(  # type:ignore[empty-body]
 | 
			
		||||
    config: "Config",
 | 
			
		||||
    start_path: Path,
 | 
			
		||||
    startdir: "LEGACY_PATH",
 | 
			
		||||
    items: Sequence["Item"],
 | 
			
		||||
) -> Union[str, List[str]]:
 | 
			
		||||
    """Return a string or list of strings to be displayed after collection
 | 
			
		||||
| 
						 | 
				
			
			@ -1047,10 +1048,8 @@ def pytest_report_collectionfinish(  # type:ignore[empty-body]
 | 
			
		|||
 | 
			
		||||
    .. versionchanged:: 7.0.0
 | 
			
		||||
        The ``start_path`` parameter was added as a :class:`pathlib.Path`
 | 
			
		||||
        equivalent of the ``startdir`` parameter.
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 8.0.0
 | 
			
		||||
        The ``startdir`` parameter has been removed.
 | 
			
		||||
        equivalent of the ``startdir`` parameter. The ``startdir`` parameter
 | 
			
		||||
        has been deprecated.
 | 
			
		||||
 | 
			
		||||
    Use in conftest plugins
 | 
			
		||||
    =======================
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
# mypy: allow-untyped-defs
 | 
			
		||||
"""Add backward compatibility support for the legacy py path type."""
 | 
			
		||||
 | 
			
		||||
import dataclasses
 | 
			
		||||
import os
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import shlex
 | 
			
		||||
import subprocess
 | 
			
		||||
| 
						 | 
				
			
			@ -14,9 +14,9 @@ from typing import Union
 | 
			
		|||
 | 
			
		||||
from iniconfig import SectionWrapper
 | 
			
		||||
 | 
			
		||||
import py
 | 
			
		||||
 | 
			
		||||
from _pytest.cacheprovider import Cache
 | 
			
		||||
from _pytest.compat import LEGACY_PATH
 | 
			
		||||
from _pytest.compat import legacy_path
 | 
			
		||||
from _pytest.config import Config
 | 
			
		||||
from _pytest.config import hookimpl
 | 
			
		||||
from _pytest.config import PytestPluginManager
 | 
			
		||||
| 
						 | 
				
			
			@ -39,20 +39,6 @@ if TYPE_CHECKING:
 | 
			
		|||
    import pexpect
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#: constant to prepare valuing pylib path replacements/lazy proxies later on
 | 
			
		||||
#  intended for removal in pytest 8.0 or 9.0
 | 
			
		||||
 | 
			
		||||
# fmt: off
 | 
			
		||||
# intentional space to create a fake difference for the verification
 | 
			
		||||
LEGACY_PATH = py.path. local
 | 
			
		||||
# fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH:
 | 
			
		||||
    """Internal wrapper to prepare lazy proxies for legacy_path instances"""
 | 
			
		||||
    return LEGACY_PATH(path)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@final
 | 
			
		||||
class Testdir:
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ from _pytest.config import hookimpl
 | 
			
		|||
from _pytest.config import PytestPluginManager
 | 
			
		||||
from _pytest.config import UsageError
 | 
			
		||||
from _pytest.config.argparsing import Parser
 | 
			
		||||
from _pytest.config.compat import PathAwareHookProxy
 | 
			
		||||
from _pytest.fixtures import FixtureManager
 | 
			
		||||
from _pytest.outcomes import exit
 | 
			
		||||
from _pytest.pathlib import absolutepath
 | 
			
		||||
| 
						 | 
				
			
			@ -557,6 +558,7 @@ class Session(nodes.Collector):
 | 
			
		|||
        super().__init__(
 | 
			
		||||
            name="",
 | 
			
		||||
            path=config.rootpath,
 | 
			
		||||
            fspath=None,
 | 
			
		||||
            parent=None,
 | 
			
		||||
            config=config,
 | 
			
		||||
            session=self,
 | 
			
		||||
| 
						 | 
				
			
			@ -694,7 +696,7 @@ class Session(nodes.Collector):
 | 
			
		|||
        proxy: pluggy.HookRelay
 | 
			
		||||
        if remove_mods:
 | 
			
		||||
            # One or more conftests are not in use at this path.
 | 
			
		||||
            proxy = FSHookProxy(pm, remove_mods)  # type: ignore[arg-type,assignment]
 | 
			
		||||
            proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods))  # type: ignore[arg-type,assignment]
 | 
			
		||||
        else:
 | 
			
		||||
            # All plugins are active for this fspath.
 | 
			
		||||
            proxy = self.config.hook
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ import abc
 | 
			
		|||
from functools import cached_property
 | 
			
		||||
from inspect import signature
 | 
			
		||||
import os
 | 
			
		||||
import pathlib
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Any
 | 
			
		||||
from typing import Callable
 | 
			
		||||
| 
						 | 
				
			
			@ -29,8 +30,11 @@ from _pytest._code import getfslineno
 | 
			
		|||
from _pytest._code.code import ExceptionInfo
 | 
			
		||||
from _pytest._code.code import TerminalRepr
 | 
			
		||||
from _pytest._code.code import Traceback
 | 
			
		||||
from _pytest.compat import LEGACY_PATH
 | 
			
		||||
from _pytest.config import Config
 | 
			
		||||
from _pytest.config import ConftestImportFailure
 | 
			
		||||
from _pytest.config.compat import _check_path
 | 
			
		||||
from _pytest.deprecated import NODE_CTOR_FSPATH_ARG
 | 
			
		||||
from _pytest.mark.structures import Mark
 | 
			
		||||
from _pytest.mark.structures import MarkDecorator
 | 
			
		||||
from _pytest.mark.structures import NodeKeywords
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +59,29 @@ tracebackcutdir = Path(_pytest.__file__).parent
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
_T = TypeVar("_T")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _imply_path(
 | 
			
		||||
    node_type: Type["Node"],
 | 
			
		||||
    path: Optional[Path],
 | 
			
		||||
    fspath: Optional[LEGACY_PATH],
 | 
			
		||||
) -> Path:
 | 
			
		||||
    if fspath is not None:
 | 
			
		||||
        warnings.warn(
 | 
			
		||||
            NODE_CTOR_FSPATH_ARG.format(
 | 
			
		||||
                node_type_name=node_type.__name__,
 | 
			
		||||
            ),
 | 
			
		||||
            stacklevel=6,
 | 
			
		||||
        )
 | 
			
		||||
    if path is not None:
 | 
			
		||||
        if fspath is not None:
 | 
			
		||||
            _check_path(path, fspath)
 | 
			
		||||
        return path
 | 
			
		||||
    else:
 | 
			
		||||
        assert fspath is not None
 | 
			
		||||
        return Path(fspath)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_NodeType = TypeVar("_NodeType", bound="Node")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +137,13 @@ class Node(abc.ABC, metaclass=NodeMeta):
 | 
			
		|||
    leaf nodes.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # Implemented in the legacypath plugin.
 | 
			
		||||
    #: A ``LEGACY_PATH`` copy of the :attr:`path` attribute. Intended for usage
 | 
			
		||||
    #: for methods not migrated to ``pathlib.Path`` yet, such as
 | 
			
		||||
    #: :meth:`Item.reportinfo <pytest.Item.reportinfo>`. Will be deprecated in
 | 
			
		||||
    #: a future release, prefer using :attr:`path` instead.
 | 
			
		||||
    fspath: LEGACY_PATH
 | 
			
		||||
 | 
			
		||||
    # Use __slots__ to make attribute access faster.
 | 
			
		||||
    # Note that __dict__ is still available.
 | 
			
		||||
    __slots__ = (
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +163,7 @@ class Node(abc.ABC, metaclass=NodeMeta):
 | 
			
		|||
        parent: "Optional[Node]" = None,
 | 
			
		||||
        config: Optional[Config] = None,
 | 
			
		||||
        session: "Optional[Session]" = None,
 | 
			
		||||
        fspath: Optional[LEGACY_PATH] = None,
 | 
			
		||||
        path: Optional[Path] = None,
 | 
			
		||||
        nodeid: Optional[str] = None,
 | 
			
		||||
    ) -> None:
 | 
			
		||||
| 
						 | 
				
			
			@ -154,11 +189,10 @@ class Node(abc.ABC, metaclass=NodeMeta):
 | 
			
		|||
                raise TypeError("session or parent must be provided")
 | 
			
		||||
            self.session = parent.session
 | 
			
		||||
 | 
			
		||||
        if path is None:
 | 
			
		||||
        if path is None and fspath is None:
 | 
			
		||||
            path = getattr(parent, "path", None)
 | 
			
		||||
        assert path is not None
 | 
			
		||||
        #: Filesystem path where this node was collected from (can be None).
 | 
			
		||||
        self.path = path
 | 
			
		||||
        self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath)
 | 
			
		||||
 | 
			
		||||
        # The explicit annotation is to avoid publicly exposing NodeKeywords.
 | 
			
		||||
        #: Keywords/markers collected from all scopes.
 | 
			
		||||
| 
						 | 
				
			
			@ -529,6 +563,7 @@ class FSCollector(Collector, abc.ABC):
 | 
			
		|||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        fspath: Optional[LEGACY_PATH] = None,
 | 
			
		||||
        path_or_parent: Optional[Union[Path, Node]] = None,
 | 
			
		||||
        path: Optional[Path] = None,
 | 
			
		||||
        name: Optional[str] = None,
 | 
			
		||||
| 
						 | 
				
			
			@ -544,8 +579,8 @@ class FSCollector(Collector, abc.ABC):
 | 
			
		|||
            elif isinstance(path_or_parent, Path):
 | 
			
		||||
                assert path is None
 | 
			
		||||
                path = path_or_parent
 | 
			
		||||
        assert path is not None
 | 
			
		||||
 | 
			
		||||
        path = _imply_path(type(self), path, fspath=fspath)
 | 
			
		||||
        if name is None:
 | 
			
		||||
            name = path.name
 | 
			
		||||
            if parent is not None and parent.path != path:
 | 
			
		||||
| 
						 | 
				
			
			@ -585,11 +620,12 @@ class FSCollector(Collector, abc.ABC):
 | 
			
		|||
        cls,
 | 
			
		||||
        parent,
 | 
			
		||||
        *,
 | 
			
		||||
        fspath: Optional[LEGACY_PATH] = None,
 | 
			
		||||
        path: Optional[Path] = None,
 | 
			
		||||
        **kw,
 | 
			
		||||
    ) -> "Self":
 | 
			
		||||
        """The public constructor."""
 | 
			
		||||
        return super().from_parent(parent=parent, path=path, **kw)
 | 
			
		||||
        return super().from_parent(parent=parent, fspath=fspath, path=path, **kw)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class File(FSCollector, abc.ABC):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,7 @@ from _pytest.compat import getimfunc
 | 
			
		|||
from _pytest.compat import getlocation
 | 
			
		||||
from _pytest.compat import is_async_function
 | 
			
		||||
from _pytest.compat import is_generator
 | 
			
		||||
from _pytest.compat import LEGACY_PATH
 | 
			
		||||
from _pytest.compat import NOTSET
 | 
			
		||||
from _pytest.compat import safe_getattr
 | 
			
		||||
from _pytest.compat import safe_isclass
 | 
			
		||||
| 
						 | 
				
			
			@ -665,6 +666,7 @@ class Package(nodes.Directory):
 | 
			
		|||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        fspath: Optional[LEGACY_PATH],
 | 
			
		||||
        parent: nodes.Collector,
 | 
			
		||||
        # NOTE: following args are unused:
 | 
			
		||||
        config=None,
 | 
			
		||||
| 
						 | 
				
			
			@ -676,6 +678,7 @@ class Package(nodes.Directory):
 | 
			
		|||
        # super().__init__(self, fspath, parent=parent)
 | 
			
		||||
        session = parent.session
 | 
			
		||||
        super().__init__(
 | 
			
		||||
            fspath=fspath,
 | 
			
		||||
            path=path,
 | 
			
		||||
            parent=parent,
 | 
			
		||||
            config=config,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,10 @@
 | 
			
		|||
# mypy: allow-untyped-defs
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from _pytest import deprecated
 | 
			
		||||
from _pytest.compat import legacy_path
 | 
			
		||||
from _pytest.pytester import Pytester
 | 
			
		||||
import pytest
 | 
			
		||||
from pytest import PytestDeprecationWarning
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +90,56 @@ def test_private_is_deprecated() -> None:
 | 
			
		|||
    PrivateInit(10, _ispytest=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize("hooktype", ["hook", "ihook"])
 | 
			
		||||
def test_hookproxy_warnings_for_pathlib(tmp_path, hooktype, request):
 | 
			
		||||
    path = legacy_path(tmp_path)
 | 
			
		||||
 | 
			
		||||
    PATH_WARN_MATCH = r".*path: py\.path\.local\) argument is deprecated, please use \(collection_path: pathlib\.Path.*"
 | 
			
		||||
    if hooktype == "ihook":
 | 
			
		||||
        hooks = request.node.ihook
 | 
			
		||||
    else:
 | 
			
		||||
        hooks = request.config.hook
 | 
			
		||||
 | 
			
		||||
    with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r:
 | 
			
		||||
        l1 = sys._getframe().f_lineno
 | 
			
		||||
        hooks.pytest_ignore_collect(
 | 
			
		||||
            config=request.config, path=path, collection_path=tmp_path
 | 
			
		||||
        )
 | 
			
		||||
        l2 = sys._getframe().f_lineno
 | 
			
		||||
 | 
			
		||||
    (record,) = r
 | 
			
		||||
    assert record.filename == __file__
 | 
			
		||||
    assert l1 < record.lineno < l2
 | 
			
		||||
 | 
			
		||||
    hooks.pytest_ignore_collect(config=request.config, collection_path=tmp_path)
 | 
			
		||||
 | 
			
		||||
    # Passing entirely *different* paths is an outright error.
 | 
			
		||||
    with pytest.raises(ValueError, match=r"path.*fspath.*need to be equal"):
 | 
			
		||||
        with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r:
 | 
			
		||||
            hooks.pytest_ignore_collect(
 | 
			
		||||
                config=request.config, path=path, collection_path=Path("/bla/bla")
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_node_ctor_fspath_argument_is_deprecated(pytester: Pytester) -> None:
 | 
			
		||||
    mod = pytester.getmodulecol("")
 | 
			
		||||
 | 
			
		||||
    class MyFile(pytest.File):
 | 
			
		||||
        def collect(self):
 | 
			
		||||
            raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    with pytest.warns(
 | 
			
		||||
        pytest.PytestDeprecationWarning,
 | 
			
		||||
        match=re.escape(
 | 
			
		||||
            "The (fspath: py.path.local) argument to MyFile is deprecated."
 | 
			
		||||
        ),
 | 
			
		||||
    ):
 | 
			
		||||
        MyFile.from_parent(
 | 
			
		||||
            parent=mod.parent,
 | 
			
		||||
            fspath=legacy_path("bla"),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_fixture_disallow_on_marked_functions():
 | 
			
		||||
    """Test that applying @pytest.fixture to a marked function warns (#3364)."""
 | 
			
		||||
    with pytest.warns(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
# mypy: allow-untyped-defs
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from _pytest.compat import LEGACY_PATH
 | 
			
		||||
from _pytest.fixtures import TopRequest
 | 
			
		||||
from _pytest.legacypath import LEGACY_PATH
 | 
			
		||||
from _pytest.legacypath import TempdirFactory
 | 
			
		||||
from _pytest.legacypath import Testdir
 | 
			
		||||
import pytest
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ def test_item_fspath(pytester: pytest.Pytester) -> None:
 | 
			
		|||
    items2, hookrec = pytester.inline_genitems(item.nodeid)
 | 
			
		||||
    (item2,) = items2
 | 
			
		||||
    assert item2.name == item.name
 | 
			
		||||
    assert item2.fspath == item.fspath  # type: ignore[attr-defined]
 | 
			
		||||
    assert item2.fspath == item.fspath
 | 
			
		||||
    assert item2.path == item.path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ from typing import Type
 | 
			
		|||
import warnings
 | 
			
		||||
 | 
			
		||||
from _pytest import nodes
 | 
			
		||||
from _pytest.compat import legacy_path
 | 
			
		||||
from _pytest.outcomes import OutcomeException
 | 
			
		||||
from _pytest.pytester import Pytester
 | 
			
		||||
from _pytest.warning_types import PytestWarning
 | 
			
		||||
| 
						 | 
				
			
			@ -44,9 +45,9 @@ def test_subclassing_both_item_and_collector_deprecated(
 | 
			
		|||
        warnings.simplefilter("error")
 | 
			
		||||
 | 
			
		||||
        class SoWrong(nodes.Item, nodes.File):
 | 
			
		||||
            def __init__(self, path, parent):
 | 
			
		||||
            def __init__(self, fspath, parent):
 | 
			
		||||
                """Legacy ctor with legacy call # don't wana see"""
 | 
			
		||||
                super().__init__(parent, path)
 | 
			
		||||
                super().__init__(fspath, parent)
 | 
			
		||||
 | 
			
		||||
            def collect(self):
 | 
			
		||||
                raise NotImplementedError()
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +56,9 @@ def test_subclassing_both_item_and_collector_deprecated(
 | 
			
		|||
                raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    with pytest.warns(PytestWarning) as rec:
 | 
			
		||||
        SoWrong.from_parent(request.session, path=tmp_path / "broken.txt", wrong=10)
 | 
			
		||||
        SoWrong.from_parent(
 | 
			
		||||
            request.session, fspath=legacy_path(tmp_path / "broken.txt")
 | 
			
		||||
        )
 | 
			
		||||
    messages = [str(x.message) for x in rec]
 | 
			
		||||
    assert any(
 | 
			
		||||
        re.search(".*SoWrong.* not using a cooperative constructor.*", x)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue