Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.d/1164.added.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Added the ``pytest_asyncio_loop_factories`` hook to parametrize asyncio tests with custom event loop factories.

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.
26 changes: 26 additions & 0 deletions docs/how-to-guides/custom_loop_factory.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
================================================
How to use custom event loop factories for tests
================================================

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:

.. code-block:: python

import asyncio

import pytest


class CustomEventLoop(asyncio.SelectorEventLoop):
pass


def pytest_asyncio_loop_factories(config, item):
return {
"stdlib": asyncio.new_event_loop,
"custom": CustomEventLoop,
}

See :doc:`run_test_with_specific_loop_factories` for running tests with only a subset of configured factories.

See :doc:`../reference/hooks` and :doc:`../reference/markers/index` for the hook and marker reference.
2 changes: 2 additions & 0 deletions docs/how-to-guides/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ How-To Guides
change_fixture_loop
change_default_fixture_loop
change_default_test_loop
custom_loop_factory
run_test_with_specific_loop_factories
run_class_tests_in_same_loop
run_module_tests_in_same_loop
run_package_tests_in_same_loop
Expand Down
14 changes: 14 additions & 0 deletions docs/how-to-guides/run_test_with_specific_loop_factories.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
=========================================================
How to run a test with specific event loop factories only
=========================================================

To run a test with only a subset of configured factories, use the ``loop_factories`` argument of ``pytest.mark.asyncio``:

.. code-block:: python

import pytest


@pytest.mark.asyncio(loop_factories=["custom"])
async def test_only_with_custom_event_loop():
pass
27 changes: 25 additions & 2 deletions docs/how-to-guides/uvloop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,31 @@
How to test with uvloop
=======================

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:
Replace the default event loop policy in your *conftest.py:*
Define a ``pytest_asyncio_loop_factories`` hook in your *conftest.py* that maps factory names to loop factories:

.. code-block:: python

import uvloop


def pytest_asyncio_loop_factories(config, item):
return {
"uvloop": uvloop.new_event_loop,
}

.. seealso::

:doc:`custom_loop_factory`
More details on the ``pytest_asyncio_loop_factories`` hook, including per-test factory selection and multiple factory parametrization.

Using the event_loop_policy fixture
-----------------------------------

.. note::

``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.

For older versions of Python and uvloop, you can override the *event_loop_policy* fixture in your *conftest.py:*

.. code-block:: python

Expand Down
16 changes: 16 additions & 0 deletions docs/reference/hooks.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
=====
Hooks
=====

``pytest_asyncio_loop_factories``
=================================

This hook returns a mapping from factory name strings to event loop factory callables for the current test item.

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.

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.

When multiple ``pytest_asyncio_loop_factories`` implementations are present, pytest-asyncio uses the first non-``None`` result in pytest's hook dispatch order.

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.
1 change: 1 addition & 0 deletions docs/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Reference
configuration
fixtures/index
functions
hooks
markers/index
decorators/index
changelog
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/markers/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Subpackages do not share the loop with their parent package.

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

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.

.. |auto mode| replace:: *auto mode*
.. _auto mode: ../../concepts.html#auto-mode
.. |pytestmark| replace:: ``pytestmark``
Expand Down
Loading
Loading