Skip to content

Commit d6e5a25

Browse files
tjkusonseifertm
authored andcommitted
Implement event loop factory hook
1 parent 38c82ef commit d6e5a25

File tree

11 files changed

+859
-25
lines changed

11 files changed

+859
-25
lines changed

changelog.d/1164.added.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Added the ``pytest_asyncio_loop_factories`` hook to parametrize asyncio tests with custom event loop factories.
2+
3+
The hook now returns a mapping of factory names to loop factories, and ``pytest.mark.asyncio(loop_factories=[...])`` can be used to select a subset of configured factories per test.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
================================================
2+
How to use custom event loop factories for tests
3+
================================================
4+
5+
pytest-asyncio can run asynchronous tests with custom event loop factories by implementing ``pytest_asyncio_loop_factories`` in ``conftest.py``. The hook returns a mapping from factory names to loop factory callables:
6+
7+
.. code-block:: python
8+
9+
import asyncio
10+
11+
import pytest
12+
13+
14+
class CustomEventLoop(asyncio.SelectorEventLoop):
15+
pass
16+
17+
18+
def pytest_asyncio_loop_factories(config, item):
19+
return {
20+
"stdlib": asyncio.new_event_loop,
21+
"custom": CustomEventLoop,
22+
}
23+
24+
See :doc:`run_test_with_specific_loop_factories` for running tests with only a subset of configured factories.
25+
26+
See :doc:`../reference/hooks` and :doc:`../reference/markers/index` for the hook and marker reference.

docs/how-to-guides/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ How-To Guides
1010
change_fixture_loop
1111
change_default_fixture_loop
1212
change_default_test_loop
13+
custom_loop_factory
14+
run_test_with_specific_loop_factories
1315
run_class_tests_in_same_loop
1416
run_module_tests_in_same_loop
1517
run_package_tests_in_same_loop
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=========================================================
2+
How to run a test with specific event loop factories only
3+
=========================================================
4+
5+
To run a test with only a subset of configured factories, use the ``loop_factories`` argument of ``pytest.mark.asyncio``:
6+
7+
.. code-block:: python
8+
9+
import pytest
10+
11+
12+
@pytest.mark.asyncio(loop_factories=["custom"])
13+
async def test_only_with_custom_event_loop():
14+
pass

docs/how-to-guides/uvloop.rst

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,31 @@
22
How to test with uvloop
33
=======================
44

5-
Redefining the *event_loop_policy* fixture will parametrize all async tests. The following example causes all async tests to run multiple times, once for each event loop in the fixture parameters:
6-
Replace the default event loop policy in your *conftest.py:*
5+
Define a ``pytest_asyncio_loop_factories`` hook in your *conftest.py* that maps factory names to loop factories:
6+
7+
.. code-block:: python
8+
9+
import uvloop
10+
11+
12+
def pytest_asyncio_loop_factories(config, item):
13+
return {
14+
"uvloop": uvloop.new_event_loop,
15+
}
16+
17+
.. seealso::
18+
19+
:doc:`custom_loop_factory`
20+
More details on the ``pytest_asyncio_loop_factories`` hook, including per-test factory selection and multiple factory parametrization.
21+
22+
Using the event_loop_policy fixture
23+
-----------------------------------
24+
25+
.. note::
26+
27+
``asyncio.AbstractEventLoopPolicy`` is deprecated as of Python 3.14 (removal planned for 3.16), and ``uvloop.EventLoopPolicy`` will be removed alongside it. Prefer the hook approach above.
28+
29+
For older versions of Python and uvloop, you can override the *event_loop_policy* fixture in your *conftest.py:*
730

831
.. code-block:: python
932

docs/reference/hooks.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=====
2+
Hooks
3+
=====
4+
5+
``pytest_asyncio_loop_factories``
6+
=================================
7+
8+
This hook returns a mapping from factory name strings to event loop factory callables for the current test item.
9+
10+
By default, each pytest-asyncio test is run once per configured factory. Tests managed by other async plugins are unaffected. Synchronous tests are not parametrized. The configured loop scope still determines how long each event loop instance is kept alive.
11+
12+
Factories should be callables without required parameters and should return an ``asyncio.AbstractEventLoop`` instance. The effective hook result must be a non-empty mapping of non-empty string names to callables.
13+
14+
When multiple ``pytest_asyncio_loop_factories`` implementations are present, pytest-asyncio uses the first non-``None`` result in pytest's hook dispatch order.
15+
16+
When the hook is defined, async tests are parametrized via ``pytest.metafunc.parametrize``, and mapping keys are used as test IDs. For example, a test ``test_example`` with an event loop factory key ``foo`` will appear as ``test_example[foo]`` in test output.

docs/reference/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Reference
88
configuration
99
fixtures/index
1010
functions
11+
hooks
1112
markers/index
1213
decorators/index
1314
changelog

docs/reference/markers/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Subpackages do not share the loop with their parent package.
3636

3737
Tests marked with *session* scope share the same event loop, even if the tests exist in different packages.
3838

39+
The ``pytest.mark.asyncio`` marker also accepts a ``loop_factories`` keyword argument to select a subset of configured event loop factories for a test. If ``loop_factories`` contains unknown names, pytest-asyncio raises a ``pytest.UsageError`` during collection.
40+
3941
.. |auto mode| replace:: *auto mode*
4042
.. _auto mode: ../../concepts.html#auto-mode
4143
.. |pytestmark| replace:: ``pytestmark``

0 commit comments

Comments
 (0)