Skip to content

Commit e05e696

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 7ae23ff commit e05e696

File tree

3 files changed

+14
-9
lines changed

3 files changed

+14
-9
lines changed

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
@@ -224,11 +224,15 @@ def pytest_pycollect_makemodule(module_path: Path, parent) -> "Module":
224224

225225

226226
@hookimpl(trylast=True)
227-
def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object):
227+
def pytest_pycollect_makeitem(
228+
collector: Union["Module", "Class"], name: str, obj: object
229+
) -> Union[None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]]:
230+
assert isinstance(collector, (Class, Module)), type(collector)
228231
# Nothing was collected elsewhere, let's do it here.
229232
if safe_isclass(obj):
230233
if collector.istestclass(obj, name):
231-
return Class.from_parent(collector, name=name, obj=obj)
234+
klass: Class = Class.from_parent(collector, name=name, obj=obj)
235+
return klass
232236
elif collector.istestfunction(obj, name):
233237
# mock seems to store unbound methods (issue473), normalize it.
234238
obj = getattr(obj, "__func__", obj)
@@ -247,15 +251,16 @@ def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object):
247251
)
248252
elif getattr(obj, "__test__", True):
249253
if is_generator(obj):
250-
res = Function.from_parent(collector, name=name)
254+
res: Function = Function.from_parent(collector, name=name)
251255
reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format(
252256
name=name
253257
)
254258
res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
255259
res.warn(PytestCollectionWarning(reason))
260+
return res
256261
else:
257-
res = list(collector._genfunctions(name, obj))
258-
return res
262+
return list(collector._genfunctions(name, obj))
263+
return None
259264

260265

261266
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)