Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronize async fixtures dynamically #669

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 12 additions & 1 deletion docs/source/reference/changelog.rst
Expand Up @@ -2,13 +2,24 @@
Changelog
=========

1.0.0 (UNRELEASED)
==================
- BREAKING: Asynchronous fixtures can no longer request the *event_loop* fixture
- BREAKING: Parametrizations and custom implementations of the *event_loop* fixture no longer have any effect on async fixtures

0.23.0 (UNRELEASED)
===================
- Removes pytest-trio from the test dependencies `#620 <https://github.com/pytest-dev/pytest-asyncio/pull/620>`_
This release is backwards-compatible with v0.21.
Changes are non-breaking, unless you upgrade from v0.22.

- BREAKING: The *asyncio_event_loop* mark has been removed. Event loops with class, module, package, and session scopes can be requested via the *scope* keyword argument to the _asyncio_ mark.
- Introduces the *event_loop_policy* fixture which allows testing with non-default or multiple event loops `#662 <https://github.com/pytest-dev/pytest-asyncio/pull/662>`_
- Removes pytest-trio from the test dependencies `#620 <https://github.com/pytest-dev/pytest-asyncio/pull/620>`_

0.22.0 (2023-10-31)
===================
This release has been yanked from PyPI due to fundamental issues with the _asyncio_event_loop_ mark.

- Class-scoped and module-scoped event loops can be requested
via the _asyncio_event_loop_ mark. `#620 <https://github.com/pytest-dev/pytest-asyncio/pull/620>`_
- Deprecate redefinition of the `event_loop` fixture. `#587 <https://github.com/pytest-dev/pytest-asyncio/issues/531>`_
Expand Down

This file was deleted.

This file was deleted.

Expand Up @@ -3,14 +3,12 @@
import pytest


@pytest.mark.asyncio_event_loop
@pytest.mark.asyncio(scope="class")
class TestClassScopedLoop:
loop: asyncio.AbstractEventLoop

@pytest.mark.asyncio
async def test_remember_loop(self):
TestClassScopedLoop.loop = asyncio.get_running_loop()

@pytest.mark.asyncio
async def test_this_runs_in_same_loop(self):
assert asyncio.get_running_loop() is TestClassScopedLoop.loop
Expand Up @@ -5,14 +5,13 @@
import pytest_asyncio


@pytest.mark.asyncio_event_loop
@pytest.mark.asyncio(scope="class")
class TestClassScopedLoop:
loop: asyncio.AbstractEventLoop

@pytest_asyncio.fixture
async def my_fixture(self):
TestClassScopedLoop.loop = asyncio.get_running_loop()

@pytest.mark.asyncio
async def test_runs_is_same_loop_as_fixture(self, my_fixture):
assert asyncio.get_running_loop() is TestClassScopedLoop.loop
@@ -0,0 +1,10 @@
import asyncio

import pytest

# Marks all test coroutines in this module
pytestmark = pytest.mark.asyncio


async def test_runs_in_asyncio_event_loop():
assert asyncio.get_running_loop()
@@ -0,0 +1,8 @@
import asyncio

import pytest


@pytest.mark.asyncio
async def test_runs_in_asyncio_event_loop():
assert asyncio.get_running_loop()
63 changes: 21 additions & 42 deletions docs/source/reference/markers/index.rst
Expand Up @@ -4,62 +4,41 @@ Markers

``pytest.mark.asyncio``
=======================
A coroutine or async generator with this marker will be treated as a test function by pytest. The marked function will be executed as an
asyncio task in the event loop provided by the ``event_loop`` fixture.
A coroutine or async generator with this marker is treated as a test function by pytest.
The marked function is executed as an asyncio task in the event loop provided by pytest-asyncio.

In order to make your test code a little more concise, the pytest |pytestmark|_
feature can be used to mark entire modules or classes with this marker.
Only test coroutines will be affected (by default, coroutines prefixed by
``test_``), so, for example, fixtures are safe to define.

.. include:: pytestmark_asyncio_strict_mode_example.py
.. include:: function_scoped_loop_strict_mode_example.py
:code: python

In *auto* mode, the ``pytest.mark.asyncio`` marker can be omitted, the marker is added
automatically to *async* test functions.


``pytest.mark.asyncio_event_loop``
==================================
Test classes or modules with this mark provide a class-scoped or module-scoped asyncio event loop.

This functionality is orthogonal to the `asyncio` mark.
That means the presence of this mark does not imply that async test functions inside the class or module are collected by pytest-asyncio.
The collection happens automatically in `auto` mode.
However, if you're using strict mode, you still have to apply the `asyncio` mark to your async test functions.
Multiple async tests in a single class or module can be marked using |pytestmark|_.

The following code example uses the `asyncio_event_loop` mark to provide a shared event loop for all tests in `TestClassScopedLoop`:

.. include:: class_scoped_loop_strict_mode_example.py
.. include:: function_scoped_loop_pytestmark_strict_mode_example.py
:code: python

In *auto* mode, the ``pytest.mark.asyncio`` marker can be omitted:

.. include:: class_scoped_loop_auto_mode_example.py
:code: python
The ``pytest.mark.asyncio`` marker can be omitted entirely in |auto mode|_ where the *asyncio* marker is added automatically to *async* test functions.

Similarly, a module-scoped loop is provided when adding the `asyncio_event_loop` mark to the module:
By default, each test runs in it's own asyncio event loop.
Multiple tests can share the same event loop by providing a *scope* keyword argument to the *asyncio* mark.
The supported scopes are *class,* and *module,* and *package*.
The following code example provides a shared event loop for all tests in `TestClassScopedLoop`:

.. include:: module_scoped_loop_auto_mode_example.py
.. include:: class_scoped_loop_strict_mode_example.py
:code: python

The `asyncio_event_loop` mark supports an optional `policy` keyword argument to set the asyncio event loop policy.
Requesting class scope with the test being part of a class will give a *UsageError*.
Similar to class-scoped event loops, a module-scoped loop is provided when setting mark's scope to *module:*

.. include:: class_scoped_loop_custom_policy_strict_mode_example.py
.. include:: module_scoped_loop_strict_mode_example.py
:code: python

Package-scoped loops only work with `regular Python packages. <https://docs.python.org/3/glossary.html#term-regular-package>`__
That means they require an *__init__.py* to be present.
Package-scoped loops do not work in `namespace packages. <https://docs.python.org/3/glossary.html#term-namespace-package>`__
Subpackages do not share the loop with their parent package.

The ``policy`` keyword argument may also take an iterable of event loop policies. This causes tests under by the `asyncio_event_loop` mark to be parametrized with different policies:

.. include:: class_scoped_loop_custom_policies_strict_mode_example.py
:code: python

If no explicit policy is provided, the mark will use the loop policy returned by ``asyncio.get_event_loop_policy()``.

Fixtures and tests sharing the same `asyncio_event_loop` mark are executed in the same event loop:

.. include:: class_scoped_loop_with_fixture_strict_mode_example.py
:code: python
Tests marked with *session* scope share the same event loop, even if the tests exist in different packages.

.. |auto mode| replace:: *auto mode*
.. _auto mode: ../../concepts.html#auto-mode
.. |pytestmark| replace:: ``pytestmark``
.. _pytestmark: http://doc.pytest.org/en/latest/example/markers.html#marking-whole-classes-or-modules
Expand Up @@ -2,7 +2,7 @@

import pytest

pytestmark = pytest.mark.asyncio_event_loop
pytestmark = pytest.mark.asyncio(scope="module")

loop: asyncio.AbstractEventLoop

Expand Down

This file was deleted.