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?).
This commit is contained in:
parent
7ae23ff8ae
commit
e05e696fda
|
@ -34,10 +34,10 @@ if TYPE_CHECKING:
|
||||||
from _pytest.nodes import Collector
|
from _pytest.nodes import Collector
|
||||||
from _pytest.nodes import Item
|
from _pytest.nodes import Item
|
||||||
from _pytest.outcomes import Exit
|
from _pytest.outcomes import Exit
|
||||||
|
from _pytest.python import Class
|
||||||
from _pytest.python import Function
|
from _pytest.python import Function
|
||||||
from _pytest.python import Metafunc
|
from _pytest.python import Metafunc
|
||||||
from _pytest.python import Module
|
from _pytest.python import Module
|
||||||
from _pytest.python import PyCollector
|
|
||||||
from _pytest.reports import CollectReport
|
from _pytest.reports import CollectReport
|
||||||
from _pytest.reports import TestReport
|
from _pytest.reports import TestReport
|
||||||
from _pytest.runner import CallInfo
|
from _pytest.runner import CallInfo
|
||||||
|
@ -360,7 +360,7 @@ def pytest_pycollect_makemodule(
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@hookspec(firstresult=True)
|
||||||
def pytest_pycollect_makeitem(
|
def pytest_pycollect_makeitem(
|
||||||
collector: "PyCollector", name: str, obj: object
|
collector: Union["Module", "Class"], name: str, obj: object
|
||||||
) -> Union[None, "Item", "Collector", List[Union["Item", "Collector"]]]:
|
) -> Union[None, "Item", "Collector", List[Union["Item", "Collector"]]]:
|
||||||
"""Return a custom item/collector for a Python object in a module, or None.
|
"""Return a custom item/collector for a Python object in a module, or None.
|
||||||
|
|
||||||
|
|
|
@ -224,11 +224,15 @@ def pytest_pycollect_makemodule(module_path: Path, parent) -> "Module":
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(trylast=True)
|
@hookimpl(trylast=True)
|
||||||
def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object):
|
def pytest_pycollect_makeitem(
|
||||||
|
collector: Union["Module", "Class"], name: str, obj: object
|
||||||
|
) -> Union[None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]]:
|
||||||
|
assert isinstance(collector, (Class, Module)), type(collector)
|
||||||
# Nothing was collected elsewhere, let's do it here.
|
# Nothing was collected elsewhere, let's do it here.
|
||||||
if safe_isclass(obj):
|
if safe_isclass(obj):
|
||||||
if collector.istestclass(obj, name):
|
if collector.istestclass(obj, name):
|
||||||
return Class.from_parent(collector, name=name, obj=obj)
|
klass: Class = Class.from_parent(collector, name=name, obj=obj)
|
||||||
|
return klass
|
||||||
elif collector.istestfunction(obj, name):
|
elif collector.istestfunction(obj, name):
|
||||||
# mock seems to store unbound methods (issue473), normalize it.
|
# mock seems to store unbound methods (issue473), normalize it.
|
||||||
obj = getattr(obj, "__func__", obj)
|
obj = getattr(obj, "__func__", obj)
|
||||||
|
@ -247,15 +251,16 @@ def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object):
|
||||||
)
|
)
|
||||||
elif getattr(obj, "__test__", True):
|
elif getattr(obj, "__test__", True):
|
||||||
if is_generator(obj):
|
if is_generator(obj):
|
||||||
res = Function.from_parent(collector, name=name)
|
res: Function = Function.from_parent(collector, name=name)
|
||||||
reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format(
|
reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format(
|
||||||
name=name
|
name=name
|
||||||
)
|
)
|
||||||
res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
|
res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
|
||||||
res.warn(PytestCollectionWarning(reason))
|
res.warn(PytestCollectionWarning(reason))
|
||||||
else:
|
|
||||||
res = list(collector._genfunctions(name, obj))
|
|
||||||
return res
|
return res
|
||||||
|
else:
|
||||||
|
return list(collector._genfunctions(name, obj))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class PyobjMixin(nodes.Node):
|
class PyobjMixin(nodes.Node):
|
||||||
|
|
|
@ -27,7 +27,7 @@ from _pytest.outcomes import skip
|
||||||
from _pytest.outcomes import xfail
|
from _pytest.outcomes import xfail
|
||||||
from _pytest.python import Class
|
from _pytest.python import Class
|
||||||
from _pytest.python import Function
|
from _pytest.python import Function
|
||||||
from _pytest.python import PyCollector
|
from _pytest.python import Module
|
||||||
from _pytest.runner import CallInfo
|
from _pytest.runner import CallInfo
|
||||||
from _pytest.scope import Scope
|
from _pytest.scope import Scope
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
def pytest_pycollect_makeitem(
|
def pytest_pycollect_makeitem(
|
||||||
collector: PyCollector, name: str, obj: object
|
collector: Union[Module, Class], name: str, obj: object
|
||||||
) -> Optional["UnitTestCase"]:
|
) -> Optional["UnitTestCase"]:
|
||||||
# Has unittest been imported and is obj a subclass of its TestCase?
|
# Has unittest been imported and is obj a subclass of its TestCase?
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue