Add typing to `from_parent` return values (#11916)
Up to now the return values of `from_parent` were untyped, this is an attempt to make it work with `typing.Self`.
This commit is contained in:
parent
1640f2e454
commit
010ce2ab0f
|
@ -47,6 +47,7 @@ from _pytest.warning_types import PytestWarning
|
|||
|
||||
if TYPE_CHECKING:
|
||||
import doctest
|
||||
from typing import Self
|
||||
|
||||
DOCTEST_REPORT_CHOICE_NONE = "none"
|
||||
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
|
||||
|
@ -133,11 +134,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
|
||||
|
||||
|
||||
|
@ -272,14 +271,14 @@ class DoctestItem(Item):
|
|||
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)
|
||||
|
|
|
@ -21,6 +21,7 @@ from typing import Optional
|
|||
from typing import overload
|
||||
from typing import Sequence
|
||||
from typing import Tuple
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import Union
|
||||
import warnings
|
||||
|
||||
|
@ -49,6 +50,10 @@ from _pytest.runner import SetupState
|
|||
from _pytest.warning_types import PytestWarning
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Self
|
||||
|
||||
|
||||
def pytest_addoption(parser: Parser) -> None:
|
||||
parser.addini(
|
||||
"norecursedirs",
|
||||
|
@ -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
|
||||
|
|
|
@ -11,6 +11,7 @@ from typing import Iterable
|
|||
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
|
||||
|
@ -41,6 +42,8 @@ from _pytest.warning_types import PytestWarning
|
|||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Self
|
||||
|
||||
# Imported here due to circular import.
|
||||
from _pytest._code.code import _TracebackStyle
|
||||
from _pytest.main import Session
|
||||
|
@ -51,6 +54,7 @@ SEP = "/"
|
|||
tracebackcutdir = Path(_pytest.__file__).parent
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_NodeType = TypeVar("_NodeType", bound="Node")
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -181,7 +185,7 @@ class Node(abc.ABC, metaclass=NodeMeta):
|
|||
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
|
||||
|
@ -583,7 +587,7 @@ class FSCollector(Collector, abc.ABC):
|
|||
*,
|
||||
path: Optional[Path] = None,
|
||||
**kw,
|
||||
):
|
||||
) -> "Self":
|
||||
"""The public constructor."""
|
||||
return super().from_parent(parent=parent, path=path, **kw)
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ from typing import Pattern
|
|||
from typing import Sequence
|
||||
from typing import Set
|
||||
from typing import Tuple
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import Union
|
||||
import warnings
|
||||
|
||||
|
@ -81,6 +82,10 @@ from _pytest.warning_types import PytestReturnNotNoneWarning
|
|||
from _pytest.warning_types import PytestUnhandledCoroutineWarning
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Self
|
||||
|
||||
|
||||
_PYTEST_DIR = Path(_pytest.__file__).parent
|
||||
|
||||
|
||||
|
@ -204,8 +209,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
|
||||
|
||||
|
||||
|
@ -230,8 +234,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)
|
||||
|
@ -242,8 +245,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)
|
||||
|
@ -262,7 +264,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"
|
||||
)
|
||||
|
@ -465,9 +467,7 @@ class PyCollector(PyobjMixin, nodes.Collector, abc.ABC):
|
|||
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
|
||||
|
@ -751,7 +751,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)
|
||||
|
||||
|
@ -1730,8 +1730,9 @@ class Function(PyobjMixin, nodes.Item):
|
|||
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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -1613,7 +1613,7 @@ def test_fscollector_from_parent(pytester: Pytester, request: FixtureRequest) ->
|
|||
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):
|
||||
|
|
Loading…
Reference in New Issue