Skip to content

Commit 96066f6

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 062d91a commit 96066f6

File tree

4 files changed

+16
-11
lines changed

4 files changed

+16
-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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@
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
3839
from _pytest.python import Metafunc
3940
from _pytest.python import Module
40-
from _pytest.python import PyCollector
4141
from _pytest.reports import CollectReport
4242
from _pytest.reports import TestReport
4343
from _pytest.runner import CallInfo
@@ -360,7 +360,7 @@ def pytest_pycollect_makemodule(
360360

361361
@hookspec(firstresult=True)
362362
def pytest_pycollect_makeitem(
363-
collector: "PyCollector", name: str, obj: object
363+
collector: Union["Module", "Class"], name: str, obj: object
364364
) -> Union[None, "Item", "Collector", List[Union["Item", "Collector"]]]:
365365
"""Return a custom item/collector for a Python object in a module, or None.
366366

src/_pytest/python.py

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

223223

224224
@hookimpl(trylast=True)
225-
def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object):
225+
def pytest_pycollect_makeitem(
226+
collector: Union["Module", "Class"], name: str, obj: object
227+
) -> Union[None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]]:
228+
assert isinstance(collector, (Class, Module)), type(collector)
226229
# Nothing was collected elsewhere, let's do it here.
227230
if safe_isclass(obj):
228231
if collector.istestclass(obj, name):
229-
return Class.from_parent(collector, name=name, obj=obj)
232+
klass: Class = Class.from_parent(collector, name=name, obj=obj)
233+
return klass
230234
elif collector.istestfunction(obj, name):
231235
# mock seems to store unbound methods (issue473), normalize it.
232236
obj = getattr(obj, "__func__", obj)
@@ -245,15 +249,16 @@ def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object):
245249
)
246250
elif getattr(obj, "__test__", True):
247251
if is_generator(obj):
248-
res = Function.from_parent(collector, name=name)
252+
res: Function = Function.from_parent(collector, name=name)
249253
reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format(
250254
name=name
251255
)
252256
res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
253257
res.warn(PytestCollectionWarning(reason))
258+
return res
254259
else:
255-
res = list(collector._genfunctions(name, obj))
256-
return res
260+
return list(collector._genfunctions(name, obj))
261+
return None
257262

258263

259264
class PyobjMixin(nodes.Node):

src/_pytest/unittest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
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 Module
3131
from _pytest.runner import CallInfo
3232
from _pytest.scope import Scope
3333

@@ -42,7 +42,7 @@
4242

4343

4444
def pytest_pycollect_makeitem(
45-
collector: PyCollector, name: str, obj: object
45+
collector: Union[Module, Class], name: str, obj: object
4646
) -> Optional["UnitTestCase"]:
4747
# Has unittest been imported and is obj a subclass of its TestCase?
4848
try:

0 commit comments

Comments
 (0)