From 172b6e15c54bdbfa7de18f5981797c1737bc5f1a Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 20 Aug 2020 22:59:15 +0300 Subject: [PATCH] hookspec: improve collection phase documentation a bit Make it a bit more accurate and use the same format that pytest_runtest_protocol uses. --- src/_pytest/hookspec.py | 35 +++++++++++++++++++++++++---------- src/_pytest/main.py | 14 ++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index e60bfe9f9..2a105191a 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -205,20 +205,32 @@ def pytest_load_initial_conftests( @hookspec(firstresult=True) def pytest_collection(session: "Session") -> Optional[object]: - """Perform the collection protocol for the given session. + """Perform the collection phase for the given session. Stops at first non-None result, see :ref:`firstresult`. The return value is not used, but only stops further processing. - The hook is meant to set `session.items` to a sequence of items at least, - but normally should follow this procedure: + The default collection phase is this (see individual hooks for full details): - 1. Call the pytest_collectstart hook. - 2. Call the pytest_collectreport hook. - 3. Call the pytest_collection_modifyitems hook. - 4. Call the pytest_collection_finish hook. - 5. Set session.testscollected to the amount of collect items. - 6. Set `session.items` to a list of items. + 1. Starting from ``session`` as the initial collector: + + 1. ``pytest_collectstart(collector)`` + 2. ``report = pytest_make_collect_report(collector)`` + 3. ``pytest_exception_interact(collector, call, report)`` if an interactive exception occurred + 4. For each collected node: + + 1. If an item, ``pytest_itemcollected(item)`` + 2. If a collector, recurse into it. + + 5. ``pytest_collectreport(report)`` + + 2. ``pytest_collection_modifyitems(session, config, items)`` + + 1. ``pytest_deselected(items)`` for any deselected items (may be called multiple times) + + 3. ``pytest_collection_finish(session)`` + 4. Set ``session.items`` to the list of collected items + 5. Set ``session.testscollected`` to the number of collected items You can implement this hook to only perform some action before collection, for example the terminal plugin uses it to start displaying the collection @@ -286,7 +298,10 @@ def pytest_collectreport(report: "CollectReport") -> None: def pytest_deselected(items: Sequence["Item"]) -> None: - """Called for deselected test items, e.g. by keyword.""" + """Called for deselected test items, e.g. by keyword. + + May be called multiple times. + """ @hookspec(firstresult=True) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 2abfffba0..d5c198e6c 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -538,6 +538,20 @@ class Session(nodes.FSCollector): def perform_collect( # noqa: F811 self, args: Optional[Sequence[str]] = None, genitems: bool = True ) -> Sequence[Union[nodes.Item, nodes.Collector]]: + """Perform the collection phase for this session. + + This is called by the default + :func:`pytest_collection <_pytest.hookspec.pytest_collection>` hook + implementation; see the documentation of this hook for more details. + For testing purposes, it may also be called directly on a fresh + ``Session``. + + This function normally recursively expands any collectors collected + from the session to their items, and only items are returned. For + testing purposes, this may be suppressed by passing ``genitems=False``, + in which case the return value contains these collectors unexpanded, + and ``session.items`` is empty. + """ hook = self.config.hook try: items = self._perform_collect(args, genitems)