Skip to content

Commit 7c11305

Browse files
committed
nodes: deprecate fspath arguments to node constructors
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`.
1 parent 5b4435b commit 7c11305

File tree

5 files changed

+57
-3
lines changed

5 files changed

+57
-3
lines changed

changelog/7259.deprecation.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
``py.path.local`` arguments for hooks have been deprecated. See :ref:`the deprecation note <legacy-path-hooks-deprecated>` for full details.
2+
3+
``py.path.local`` arguments to Node constructors have been deprecated. See :ref:`the deprecation note <node-ctor-fspath-deprecation>` for full details.

doc/en/deprecations.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@ Deprecated Features
1818
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
1919
:class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
2020

21+
.. _node-ctor-fspath-deprecation:
22+
23+
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
24+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25+
26+
.. deprecated:: 7.0
27+
28+
In order to support the transition from ``py.path.local`` to :mod:`pathlib`,
29+
the ``fspath`` argument to :class:`~_pytest.nodes.Node` constructors like
30+
:func:`pytest.Function.from_parent()` and :func:`pytest.Class.from_parent()`
31+
is now deprecated.
32+
33+
Plugins which construct nodes should pass the ``path`` argument, of type
34+
:class:`pathlib.Path`, instead of the ``fspath`` argument.
35+
36+
Plugins which implement custom items and collectors are encouraged to replace
37+
``py.path.local`` ``fspath`` parameters with ``pathlib.Path`` parameters, and
38+
drop any other usage of the ``py`` library if possible.
39+
2140

2241
.. _legacy-path-hooks-deprecated:
2342

src/_pytest/deprecated.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@
101101
"#py-path-local-arguments-for-hooks-replaced-with-pathlib-path",
102102
)
103103

104+
NODE_CTOR_FSPATH_ARG = UnformattedWarning(
105+
PytestDeprecationWarning,
106+
"The (fspath: py.path.local) argument to {node_type_name} is deprecated. "
107+
"Please use the (path: pathlib.Path) argument instead.\n"
108+
"See https://docs.pytest.org/en/latest/deprecations.html"
109+
"#fspath-argument-for-node-constructors-replaced-with-pathlib-path",
110+
)
111+
104112
WARNS_NONE_ARG = PytestDeprecationWarning(
105113
"Passing None to catch any warning has been deprecated, pass no arguments instead:\n"
106114
" Replace pytest.warns(None) by simply pytest.warns()."

src/_pytest/nodes.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from _pytest.config import Config
2929
from _pytest.config import ConftestImportFailure
3030
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
31+
from _pytest.deprecated import NODE_CTOR_FSPATH_ARG
3132
from _pytest.mark.structures import Mark
3233
from _pytest.mark.structures import MarkDecorator
3334
from _pytest.mark.structures import NodeKeywords
@@ -101,7 +102,18 @@ def _check_path(path: Path, fspath: LEGACY_PATH) -> None:
101102
)
102103

103104

104-
def _imply_path(path: Optional[Path], fspath: Optional[LEGACY_PATH]) -> Path:
105+
def _imply_path(
106+
node_type: Type["Node"],
107+
path: Optional[Path],
108+
fspath: Optional[LEGACY_PATH],
109+
) -> Path:
110+
if fspath is not None:
111+
warnings.warn(
112+
NODE_CTOR_FSPATH_ARG.format(
113+
node_type_name=node_type.__name__,
114+
),
115+
stacklevel=3,
116+
)
105117
if path is not None:
106118
if fspath is not None:
107119
_check_path(path, fspath)
@@ -196,7 +208,7 @@ def __init__(
196208
#: Filesystem path where this node was collected from (can be None).
197209
if path is None and fspath is None:
198210
path = getattr(parent, "path", None)
199-
self.path = _imply_path(path, fspath=fspath)
211+
self.path = _imply_path(type(self), path, fspath=fspath)
200212

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

576-
path = _imply_path(path, fspath=fspath)
588+
path = _imply_path(type(self), path, fspath=fspath)
577589
if name is None:
578590
name = path.name
579591
if parent is not None and parent.path != path:

testing/deprecated_test.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,16 @@ def pytest_cmdline_preparse(config, args):
215215
"*Please use pytest_load_initial_conftests hook instead.*",
216216
]
217217
)
218+
219+
220+
def test_node_ctor_fspath_argument_is_deprecated(pytester: Pytester) -> None:
221+
mod = pytester.getmodulecol("")
222+
223+
with pytest.warns(
224+
pytest.PytestDeprecationWarning,
225+
match=re.escape("The (fspath: py.path.local) argument to File is deprecated."),
226+
):
227+
pytest.File.from_parent(
228+
parent=mod.parent,
229+
fspath=legacy_path("bla"),
230+
)

0 commit comments

Comments
 (0)