Skip to content

Commit

Permalink
nodes: deprecate fspath arguments to node constructors
Browse files Browse the repository at this point in the history
This is unfortunately a dependency on `py.path` which cannot be moved to
an external plugins or eased in any way, so has to be deprecated in
order for pytest to be able to eventually remove the dependency on `py`.
  • Loading branch information
bluetech committed Oct 23, 2021
1 parent 5b4435b commit 7c11305
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 3 deletions.
2 changes: 2 additions & 0 deletions changelog/7259.deprecation.rst
@@ -1 +1,3 @@
``py.path.local`` arguments for hooks have been deprecated. See :ref:`the deprecation note <legacy-path-hooks-deprecated>` for full details.

``py.path.local`` arguments to Node constructors have been deprecated. See :ref:`the deprecation note <node-ctor-fspath-deprecation>` for full details.
19 changes: 19 additions & 0 deletions doc/en/deprecations.rst
Expand Up @@ -18,6 +18,25 @@ Deprecated Features
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
:class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.

.. _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
``py.path.local`` ``fspath`` parameters with ``pathlib.Path`` parameters, and
drop any other usage of the ``py`` library if possible.


.. _legacy-path-hooks-deprecated:

Expand Down
8 changes: 8 additions & 0 deletions src/_pytest/deprecated.py
Expand Up @@ -101,6 +101,14 @@
"#py-path-local-arguments-for-hooks-replaced-with-pathlib-path",
)

NODE_CTOR_FSPATH_ARG = UnformattedWarning(
PytestDeprecationWarning,
"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",
)

WARNS_NONE_ARG = PytestDeprecationWarning(
"Passing None to catch any warning has been deprecated, pass no arguments instead:\n"
" Replace pytest.warns(None) by simply pytest.warns()."
Expand Down
18 changes: 15 additions & 3 deletions src/_pytest/nodes.py
Expand Up @@ -28,6 +28,7 @@
from _pytest.config import Config
from _pytest.config import ConftestImportFailure
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
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
Expand Down Expand Up @@ -101,7 +102,18 @@ def _check_path(path: Path, fspath: LEGACY_PATH) -> None:
)


def _imply_path(path: Optional[Path], fspath: Optional[LEGACY_PATH]) -> Path:
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=3,
)
if path is not None:
if fspath is not None:
_check_path(path, fspath)
Expand Down Expand Up @@ -196,7 +208,7 @@ def __init__(
#: Filesystem path where this node was collected from (can be None).
if path is None and fspath is None:
path = getattr(parent, "path", None)
self.path = _imply_path(path, fspath=fspath)
self.path = _imply_path(type(self), path, fspath=fspath)

# The explicit annotation is to avoid publicly exposing NodeKeywords.
#: Keywords/markers collected from all scopes.
Expand Down Expand Up @@ -573,7 +585,7 @@ def __init__(
assert path is None
path = path_or_parent

path = _imply_path(path, fspath=fspath)
path = _imply_path(type(self), path, fspath=fspath)
if name is None:
name = path.name
if parent is not None and parent.path != path:
Expand Down
13 changes: 13 additions & 0 deletions testing/deprecated_test.py
Expand Up @@ -215,3 +215,16 @@ def pytest_cmdline_preparse(config, args):
"*Please use pytest_load_initial_conftests hook instead.*",
]
)


def test_node_ctor_fspath_argument_is_deprecated(pytester: Pytester) -> None:
mod = pytester.getmodulecol("")

with pytest.warns(
pytest.PytestDeprecationWarning,
match=re.escape("The (fspath: py.path.local) argument to File is deprecated."),
):
pytest.File.from_parent(
parent=mod.parent,
fspath=legacy_path("bla"),
)

0 comments on commit 7c11305

Please sign in to comment.