Skip to content
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
11 changes: 5 additions & 6 deletions src/_pytest/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

if TYPE_CHECKING:
import doctest
from typing import Self

DOCTEST_REPORT_CHOICE_NONE = "none"
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
Expand Down Expand Up @@ -134,11 +135,9 @@ def pytest_collect_file(
if config.option.doctestmodules and not any(
(_is_setup_py(file_path), _is_main_py(file_path))
):
mod: DoctestModule = DoctestModule.from_parent(parent, path=file_path)
return mod
return DoctestModule.from_parent(parent, path=file_path)
elif _is_doctest(config, file_path, parent):
txt: DoctestTextfile = DoctestTextfile.from_parent(parent, path=file_path)
return txt
return DoctestTextfile.from_parent(parent, path=file_path)
return None


Expand Down Expand Up @@ -273,14 +272,14 @@ def __init__(
self._initrequest()

@classmethod
def from_parent( # type: ignore
def from_parent( # type: ignore[override]
cls,
parent: "Union[DoctestTextfile, DoctestModule]",
*,
name: str,
runner: "doctest.DocTestRunner",
dtest: "doctest.DocTest",
):
) -> "Self":
# incompatible signature due to imposed limits on subclass
"""The public named constructor."""
return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest)
Expand Down
11 changes: 8 additions & 3 deletions src/_pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from typing import overload
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
import warnings

Expand Down Expand Up @@ -49,6 +50,10 @@
from _pytest.warning_types import PytestWarning


if TYPE_CHECKING:
from typing import Self


def pytest_addoption(parser: Parser) -> None:
parser.addini(
"norecursedirs",
Expand Down Expand Up @@ -491,16 +496,16 @@ class Dir(nodes.Directory):
@classmethod
def from_parent( # type: ignore[override]
cls,
parent: nodes.Collector, # type: ignore[override]
parent: nodes.Collector,
*,
path: Path,
) -> "Dir":
) -> "Self":
"""The public constructor.

:param parent: The parent collector of this Dir.
:param path: The directory's path.
"""
return super().from_parent(parent=parent, path=path) # type: ignore[no-any-return]
return super().from_parent(parent=parent, path=path)

def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
config = self.config
Expand Down
22 changes: 13 additions & 9 deletions src/_pytest/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from typing import Iterator
from typing import List
from typing import MutableMapping
from typing import NoReturn
from typing import Optional
from typing import overload
from typing import Set
Expand Down Expand Up @@ -41,6 +42,8 @@


if TYPE_CHECKING:
from typing import Self

# Imported here due to circular import.
from _pytest._code.code import _TracebackStyle
from _pytest.main import Session
Expand All @@ -51,6 +54,7 @@
tracebackcutdir = Path(_pytest.__file__).parent


_T = TypeVar("_T")
_NodeType = TypeVar("_NodeType", bound="Node")


Expand All @@ -69,33 +73,33 @@ class NodeMeta(abc.ABCMeta):
progress on detangling the :class:`Node` classes.
"""

def __call__(self, *k, **kw):
def __call__(cls, *k, **kw) -> NoReturn:
msg = (
"Direct construction of {name} has been deprecated, please use {name}.from_parent.\n"
"See "
"https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent"
" for more details."
).format(name=f"{self.__module__}.{self.__name__}")
).format(name=f"{cls.__module__}.{cls.__name__}")
fail(msg, pytrace=False)

def _create(self, *k, **kw):
def _create(cls: Type[_T], *k, **kw) -> _T:
try:
return super().__call__(*k, **kw)
return super().__call__(*k, **kw) # type: ignore[no-any-return,misc]
except TypeError:
sig = signature(getattr(self, "__init__"))
sig = signature(getattr(cls, "__init__"))
known_kw = {k: v for k, v in kw.items() if k in sig.parameters}
from .warning_types import PytestDeprecationWarning

warnings.warn(
PytestDeprecationWarning(
f"{self} is not using a cooperative constructor and only takes {set(known_kw)}.\n"
f"{cls} is not using a cooperative constructor and only takes {set(known_kw)}.\n"
"See https://docs.pytest.org/en/stable/deprecations.html"
"#constructors-of-custom-pytest-node-subclasses-should-take-kwargs "
"for more details."
)
)

return super().__call__(*k, **known_kw)
return super().__call__(*k, **known_kw) # type: ignore[no-any-return,misc]


class Node(abc.ABC, metaclass=NodeMeta):
Expand Down Expand Up @@ -181,7 +185,7 @@ def __init__(
self._store = self.stash

@classmethod
def from_parent(cls, parent: "Node", **kw):
def from_parent(cls, parent: "Node", **kw) -> "Self":
"""Public constructor for Nodes.

This indirection got introduced in order to enable removing
Expand Down Expand Up @@ -583,7 +587,7 @@ def from_parent(
*,
path: Optional[Path] = None,
**kw,
):
) -> "Self":
"""The public constructor."""
return super().from_parent(parent=parent, path=path, **kw)

Expand Down
25 changes: 13 additions & 12 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from typing import Sequence
from typing import Set
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
import warnings

Expand Down Expand Up @@ -82,6 +83,10 @@
from _pytest.warning_types import PytestUnhandledCoroutineWarning


if TYPE_CHECKING:
from typing import Self


_PYTEST_DIR = Path(_pytest.__file__).parent


Expand Down Expand Up @@ -205,8 +210,7 @@ def pytest_collect_directory(
) -> Optional[nodes.Collector]:
pkginit = path / "__init__.py"
if pkginit.is_file():
pkg: Package = Package.from_parent(parent, path=path)
return pkg
return Package.from_parent(parent, path=path)
return None


Expand All @@ -231,8 +235,7 @@ def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool:


def pytest_pycollect_makemodule(module_path: Path, parent) -> "Module":
mod: Module = Module.from_parent(parent, path=module_path)
return mod
return Module.from_parent(parent, path=module_path)


@hookimpl(trylast=True)
Expand All @@ -243,8 +246,7 @@ def pytest_pycollect_makeitem(
# Nothing was collected elsewhere, let's do it here.
if safe_isclass(obj):
if collector.istestclass(obj, name):
klass: Class = Class.from_parent(collector, name=name, obj=obj)
return klass
return Class.from_parent(collector, name=name, obj=obj)
elif collector.istestfunction(obj, name):
# mock seems to store unbound methods (issue473), normalize it.
obj = getattr(obj, "__func__", obj)
Expand All @@ -263,7 +265,7 @@ def pytest_pycollect_makeitem(
)
elif getattr(obj, "__test__", True):
if is_generator(obj):
res: Function = Function.from_parent(collector, name=name)
res = Function.from_parent(collector, name=name)
reason = (
f"yield tests were removed in pytest 4.0 - {name} will be ignored"
)
Expand Down Expand Up @@ -466,9 +468,7 @@ def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]:
clscol = self.getparent(Class)
cls = clscol and clscol.obj or None

definition: FunctionDefinition = FunctionDefinition.from_parent(
self, name=name, callobj=funcobj
)
definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj)
fixtureinfo = definition._fixtureinfo

# pytest_generate_tests impls call metafunc.parametrize() which fills
Expand Down Expand Up @@ -752,7 +752,7 @@ class Class(PyCollector):
"""Collector for test methods (and nested classes) in a Python class."""

@classmethod
def from_parent(cls, parent, *, name, obj=None, **kw):
def from_parent(cls, parent, *, name, obj=None, **kw) -> "Self": # type: ignore[override]
"""The public constructor."""
return super().from_parent(name=name, parent=parent, **kw)

Expand Down Expand Up @@ -1732,8 +1732,9 @@ def __init__(
self.fixturenames = fixtureinfo.names_closure
self._initrequest()

# todo: determine sound type limitations
@classmethod
def from_parent(cls, parent, **kw): # todo: determine sound type limitations
def from_parent(cls, parent, **kw) -> "Self":
"""The public constructor."""
return super().from_parent(parent=parent, **kw)

Expand Down
3 changes: 1 addition & 2 deletions src/_pytest/unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ def pytest_pycollect_makeitem(
except Exception:
return None
# Yes, so let's collect it.
item: UnitTestCase = UnitTestCase.from_parent(collector, name=name, obj=obj)
return item
return UnitTestCase.from_parent(collector, name=name, obj=obj)


class UnitTestCase(Class):
Expand Down
2 changes: 1 addition & 1 deletion testing/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,7 @@ def collect(self):
assert collector.x == 10


def test_class_from_parent(pytester: Pytester, request: FixtureRequest) -> None:
def test_class_from_parent(request: FixtureRequest) -> None:
"""Ensure Class.from_parent can forward custom arguments to the constructor."""

class MyCollector(pytest.Class):
Expand Down