Skip to content
1 change: 1 addition & 0 deletions changelog.d/+event_loop_policy_removed.removed.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Removed the *event_loop_policy* fixture. Defining a fixture with this name now raises a usage error at collection time instead of silently having no effect. Use the ``pytest_asyncio_loop_factories`` hook to customize event loop creation instead. See the :doc:`migration guide </how-to-guides/migrate_from_1_x>`.
1 change: 1 addition & 0 deletions changelog.d/+hook_based_rewrite.changed.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reimplemented test collection and dispatch using plain pytest hooks (``pytest_pycollect_makeitem``, ``pytest_generate_tests``, ``pytest_runtest_setup``, ``pytest_pyfunc_call``) instead of a custom ``pytest.Item`` subclass hierarchy. ``pytest_asyncio.is_async_test`` now returns a plain ``bool`` instead of a ``TypeIs``-narrowed type, since there is no longer a subclass to narrow to. Event loop lifecycle management (one ``asyncio.Runner`` per active loop scope) is unchanged internally.
1 change: 1 addition & 0 deletions changelog.d/+scope_kwarg_removed.removed.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Removed the deprecated *scope* keyword argument to the ``asyncio`` marker. Use ``pytest.mark.asyncio(loop_scope="…")`` instead.
1 change: 1 addition & 0 deletions changelog.d/+version_floor_unchanged.downstream.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The minimum supported Python (3.10) and pytest (8.4) versions are unchanged despite this being a major version bump; nothing in the v2 rewrite requires newer versions of either.
1 change: 1 addition & 0 deletions changelog.d/1514.added.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ``PytestAsyncioLoopScopeMismatchWarning``, raised when a test or fixture (transitively) depends on an async fixture whose effective *loop_scope* differs from its own. Running a test and a fixture it depends on under different event loops can silently break objects (such as ``asyncio.Future``, ``asyncio.Task``, or ``asyncio.Lock``) bound to the loop they were created on. See :doc:`/reference/warnings`.
2 changes: 1 addition & 1 deletion docs/how-to-guides/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ How-To Guides

migrate_from_0_21
migrate_from_0_23
migrate_from_1_x
change_fixture_loop
change_default_fixture_loop
change_default_test_loop
Expand All @@ -16,7 +17,6 @@ How-To Guides
run_class_tests_in_same_loop
run_module_tests_in_same_loop
run_package_tests_in_same_loop
multiple_loops
parametrize_with_asyncio
uvloop
test_item_is_async
Expand Down
9 changes: 9 additions & 0 deletions docs/how-to-guides/migrate_from_1_x.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. _how_to_guides/migrate_from_1_x:

======================================
How to migrate from pytest-asyncio v1
======================================
1. If your test suite (or a plugin/conftest it uses) defines an *event_loop_policy* fixture, remove it. The fixture no longer has any effect; defining one now raises a usage error at collection time. Move the customization into a ``pytest_asyncio_loop_factories`` hook implementation instead. See :doc:`custom_loop_factory` and :doc:`uvloop`.
2. Change all occurrences of ``pytest.mark.asyncio(scope="…")`` to ``pytest.mark.asyncio(loop_scope="…")``. The deprecated *scope* keyword argument to the *asyncio* marker is no longer accepted at all.
3. Expect a new ``PytestAsyncioLoopScopeMismatchWarning`` to appear if a test or fixture (transitively) depends on an async fixture with a different effective *loop_scope*. This is new in v2 and can surface pre-existing bugs in test suites that already use *loop_scope*, sometimes in bulk, on first upgrade: a test and a fixture running on different event loops can silently break objects (such as ``asyncio.Future``, ``asyncio.Task``, or ``asyncio.Lock``) bound to the loop they were created on. See :doc:`../reference/warnings` for how to fix or, if the mismatch is intentional, silence individual instances.
4. If your code relies on the return type of ``pytest_asyncio.is_async_test``, note that it now returns a plain ``bool`` rather than a ``TypeIs`` type guard, since the check is no longer an ``isinstance`` check against a pytest-asyncio-specific ``Item`` subclass.
14 changes: 0 additions & 14 deletions docs/how-to-guides/multiple_loops.rst

This file was deleted.

29 changes: 0 additions & 29 deletions docs/how-to-guides/multiple_loops_example.py

This file was deleted.

21 changes: 0 additions & 21 deletions docs/how-to-guides/uvloop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,3 @@ Define a ``pytest_asyncio_loop_factories`` hook in your *conftest.py* that maps

: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. Overriding the *event_loop_policy* fixture is also deprecated in pytest-asyncio. 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

import pytest
import uvloop


@pytest.fixture(scope="session")
def event_loop_policy():
return uvloop.EventLoopPolicy()

You may choose to limit the scope of the fixture to *package,* *module,* or *class,* if you only want a subset of your tests to run with uvloop.
23 changes: 0 additions & 23 deletions docs/reference/fixtures/event_loop_policy_example.py

This file was deleted.

This file was deleted.

23 changes: 0 additions & 23 deletions docs/reference/fixtures/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,6 @@
Fixtures
========

event_loop_policy
=================

.. warning::

Overriding the *event_loop_policy* fixture is deprecated and will be removed in a future version of pytest-asyncio. Use the ``pytest_asyncio_loop_factories`` hook instead. See :doc:`../hooks` for details.

Returns the event loop policy used to create asyncio event loops.
The default return value is *asyncio.get_event_loop_policy().*

This fixture can be overridden when a different event loop policy should be used.

.. include:: event_loop_policy_example.py
:code: python

Multiple policies can be provided via fixture parameters.
The fixture is automatically applied to all pytest-asyncio tests.
Therefore, all tests managed by pytest-asyncio are run once for each fixture parameter.
The following example runs the test with different event loop policies.

.. include:: event_loop_policy_parametrized_example.py
:code: python

unused_tcp_port
===============
Finds and yields a single unused TCP port on the localhost interface. Useful for
Expand Down
1 change: 1 addition & 0 deletions docs/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Reference
hooks
markers/index
decorators/index
warnings
changelog

This section of the documentation provides descriptions of the individual parts provided by pytest-asyncio.
Expand Down
36 changes: 36 additions & 0 deletions docs/reference/warnings.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
========
Warnings
========

PytestAsyncioLoopScopeMismatchWarning
======================================
Warns that a test or fixture requests an async fixture whose effective *loop_scope* differs from its own.

Every async test and every async fixture runs on an event loop determined by its effective *loop_scope* (its own explicit *loop_scope* argument, else the configured default, else its pytest caching scope). When a test or fixture (transitively) depends on an async fixture with a *different* effective *loop_scope*, the two run on different event loops. This can silently break objects -- such as ``asyncio.Future``, ``asyncio.Task``, or ``asyncio.Lock`` -- that are bound to the loop they were created on.

.. code-block:: python

import pytest

import pytest_asyncio


@pytest_asyncio.fixture(loop_scope="module")
async def async_fixture(): ...


@pytest.mark.asyncio(loop_scope="function")
async def test_uses_fixture_from_a_different_loop(async_fixture):
# async_fixture ran on the module-scoped loop, but this test
# runs on its own function-scoped loop: PytestAsyncioLoopScopeMismatchWarning
...

If the mismatch is intentional, silence the warning for a specific test with the standard *filterwarnings* marker:

.. code-block:: python

@pytest.mark.asyncio(loop_scope="function")
@pytest.mark.filterwarnings(
"ignore::pytest_asyncio.PytestAsyncioLoopScopeMismatchWarning"
)
async def test_uses_fixture_from_a_different_loop(async_fixture): ...
4 changes: 2 additions & 2 deletions pytest_asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from importlib.metadata import version

from .plugin import fixture, is_async_test
from .plugin import PytestAsyncioLoopScopeMismatchWarning, fixture, is_async_test

__version__ = version(__name__)

__all__ = ("fixture", "is_async_test")
__all__ = ("PytestAsyncioLoopScopeMismatchWarning", "fixture", "is_async_test")
Loading
Loading