Skip to content

Commit 8a4aa2d

Browse files
committed
Make PyCollector an implementation detail - don't use in hook type annotation
The `pytest_pycollector_makeitem` argument `collector` is currently annotated with type `PyCollector`. As part of #7469, that would have required us to expose it in the public API. But really it's an implementation detail, not something we want to expose. So replace the annotation with the concrete python collector types that are passed. Strictly speaking, `pytest_pycollector_makeitem` is called from `PyCollector.collect()`, so the new type annotation is incorrect if another type subclasses `PyCollector`. But the set of python collectors is closed (mapping to language constructs), and the type is private, so there shouldn't be any other deriving classes, and we can consider it effectively sealed (unfortunately Python does not provide a way to express this - yet?).
1 parent 842814c commit 8a4aa2d

File tree

4 files changed

+18
-11
lines changed

4 files changed

+18
-11
lines changed

changelog/7469.feature.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ The newly-exported types are:
66
- ``pytest.Mark`` for :class:`marks <pytest.Mark>`.
77
- ``pytest.MarkDecorator`` for :class:`mark decorators <pytest.MarkDecorator>`.
88
- ``pytest.MarkGenerator`` for the :class:`pytest.mark <pytest.MarkGenerator>` singleton.
9-
- ``pytest.Metafunc`` for the :class:`metafunc <pytest.MarkGenerator>` argument to the :func:`pytest_generate_tests <pytest.hookspec.pytest_generate_tests>` hook.
9+
- ``pytest.Metafunc`` for the :class:`metafunc <pytest.MarkGenerator>` argument to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook.
1010
- ``pytest.CallInfo`` for the :class:`CallInfo <pytest.CallInfo>` type passed to various hooks.
1111
- ``pytest.PytestPluginManager`` for :class:`PytestPluginManager <pytest.PytestPluginManager>`.
1212
- ``pytest.ExceptionInfo`` for the :class:`ExceptionInfo <pytest.ExceptionInfo>` type returned from :func:`pytest.raises` and passed to various hooks.
13-
- ``pytest.Parser`` for the :class:`Parser <pytest.Parser>` type passed to the :func:`pytest_addoption <pytest.hookspec.pytest_addoption>` hook.
13+
- ``pytest.Parser`` for the :class:`Parser <pytest.Parser>` type passed to the :func:`pytest_addoption <_pytest.hookspec.pytest_addoption>` hook.
1414
- ``pytest.OptionGroup`` for the :class:`OptionGroup <pytest.OptionGroup>` type returned from the :func:`parser.addgroup <pytest.Parser.getgroup>` method.
1515
- ``pytest.HookRecorder`` for the :class:`HookRecorder <pytest.HookRecorder>` type returned from :class:`~pytest.Pytester`.
1616
- ``pytest.RecordedHookCall`` for the :class:`RecordedHookCall <pytest.HookRecorder>` type returned from :class:`~pytest.HookRecorder`.

src/_pytest/hookspec.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@
3434
from _pytest.nodes import Collector
3535
from _pytest.nodes import Item
3636
from _pytest.outcomes import Exit
37+
from _pytest.python import Class
3738
from _pytest.python import Function
39+
from _pytest.python import Instance
3840
from _pytest.python import Metafunc
3941
from _pytest.python import Module
40-
from _pytest.python import PyCollector
4142
from _pytest.reports import CollectReport
4243
from _pytest.reports import TestReport
4344
from _pytest.runner import CallInfo
@@ -360,7 +361,7 @@ def pytest_pycollect_makemodule(
360361

361362
@hookspec(firstresult=True)
362363
def pytest_pycollect_makeitem(
363-
collector: "PyCollector", name: str, obj: object
364+
collector: Union["Module", "Class", "Instance"], name: str, obj: object
364365
) -> Union[None, "Item", "Collector", List[Union["Item", "Collector"]]]:
365366
"""Return a custom item/collector for a Python object in a module, or None.
366367

src/_pytest/python.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,15 @@ def pytest_pycollect_makemodule(fspath: Path, parent) -> "Module":
218218

219219

220220
@hookimpl(trylast=True)
221-
def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object):
221+
def pytest_pycollect_makeitem(
222+
collector: Union["Module", "Class", "Instance"], name: str, obj: object
223+
) -> Union[None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]]:
224+
assert isinstance(collector, (Class, Module, Instance)), type(collector)
222225
# Nothing was collected elsewhere, let's do it here.
223226
if safe_isclass(obj):
224227
if collector.istestclass(obj, name):
225-
return Class.from_parent(collector, name=name, obj=obj)
228+
klass: Class = Class.from_parent(collector, name=name, obj=obj)
229+
return klass
226230
elif collector.istestfunction(obj, name):
227231
# mock seems to store unbound methods (issue473), normalize it.
228232
obj = getattr(obj, "__func__", obj)
@@ -241,15 +245,16 @@ def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object):
241245
)
242246
elif getattr(obj, "__test__", True):
243247
if is_generator(obj):
244-
res = Function.from_parent(collector, name=name)
248+
res: Function = Function.from_parent(collector, name=name)
245249
reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format(
246250
name=name
247251
)
248252
res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
249253
res.warn(PytestCollectionWarning(reason))
254+
return res
250255
else:
251-
res = list(collector._genfunctions(name, obj))
252-
return res
256+
return list(collector._genfunctions(name, obj))
257+
return None
253258

254259

255260
class PyobjMixin(nodes.Node):

src/_pytest/unittest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
from _pytest.outcomes import xfail
2828
from _pytest.python import Class
2929
from _pytest.python import Function
30-
from _pytest.python import PyCollector
30+
from _pytest.python import Instance
31+
from _pytest.python import Module
3132
from _pytest.runner import CallInfo
3233
from _pytest.scope import Scope
3334

@@ -42,7 +43,7 @@
4243

4344

4445
def pytest_pycollect_makeitem(
45-
collector: PyCollector, name: str, obj: object
46+
collector: Union[Module, Class, Instance], name: str, obj: object
4647
) -> Optional["UnitTestCase"]:
4748
# Has unittest been imported and is obj a subclass of its TestCase?
4849
try:

0 commit comments

Comments
 (0)