From 4c29572c6808a9766fdc7b99fbff60967e389e79 Mon Sep 17 00:00:00 2001 From: dromanov Date: Sun, 5 Apr 2026 22:44:40 +0300 Subject: [PATCH 1/4] chore: add sphinx-tabs to dependency --- constraints.txt | 1 + pyproject.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/constraints.txt b/constraints.txt index 05a6ebf4..dd905854 100644 --- a/constraints.txt +++ b/constraints.txt @@ -49,6 +49,7 @@ setuptools-scm==9.2.2 snowballstemmer==3.0.1 sortedcontainers==2.4.0 Sphinx==8.1.3 +sphinx-tabs==3.5.0 sphinx-rtd-theme==3.1.0 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-devhelp==2.0.0 diff --git a/pyproject.toml b/pyproject.toml index 2d629589..f52bc823 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,7 @@ dependencies = [ optional-dependencies.docs = [ "sphinx>=5.3", "sphinx-rtd-theme>=1", + "sphinx-tabs>=3.5", ] optional-dependencies.testing = [ "coverage>=6.2", From d88c61a0f22a6ece2690f47247e6b2d98c7c42c7 Mon Sep 17 00:00:00 2001 From: dromanov Date: Sun, 5 Apr 2026 22:44:59 +0300 Subject: [PATCH 2/4] docs: add changelog file --- changelog.d/641.added.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/641.added.rst diff --git a/changelog.d/641.added.rst b/changelog.d/641.added.rst new file mode 100644 index 00000000..c14a381e --- /dev/null +++ b/changelog.d/641.added.rst @@ -0,0 +1 @@ +Added ``sphinx-tabs`` to organize documentation examples into tabs. From 0a48269ec98278c6bf42645e2fc62d0e45e1b476 Mon Sep 17 00:00:00 2001 From: dromanov Date: Sun, 5 Apr 2026 22:46:30 +0300 Subject: [PATCH 3/4] feat(docs): add tabs and group tabs to improve documentation examples --- docs/concepts.rst | 48 +++++++++++++------ docs/conf.py | 2 +- .../change_default_fixture_loop.rst | 29 ++++++----- .../change_default_test_loop.rst | 32 ++++++++----- docs/reference/markers/index.rst | 25 ++++++---- 5 files changed, 87 insertions(+), 49 deletions(-) diff --git a/docs/concepts.rst b/docs/concepts.rst index 591059a8..3ac57069 100644 --- a/docs/concepts.rst +++ b/docs/concepts.rst @@ -47,30 +47,50 @@ Assigning neighboring tests to different event loop scopes is discouraged as it Test discovery modes ==================== -Pytest-asyncio provides two modes for test discovery, *strict* and *auto*. This can be set through Pytest's ``--asyncio-mode`` command line flag, or through the configuration file: +Pytest-asyncio provides two modes for test discovery, *strict* and *auto*. +This can be set through Pytest's ``--asyncio-mode`` command line flag, +or through the configuration file. -.. code-block:: toml +.. tabs:: - [tool.pytest.ini_options] - asyncio_mode = "auto" # or "strict" + .. group-tab:: Strict mode -Strict mode ------------ + .. code-block:: toml -In strict mode pytest-asyncio will only run tests that have the *asyncio* marker and will only evaluate async fixtures decorated with ``@pytest_asyncio.fixture``. Test functions and fixtures without these markers and decorators will not be handled by pytest-asyncio. + [tool.pytest.ini_options] + asyncio_mode = "strict" -This mode is intended for projects that want so support multiple asynchronous programming libraries as it allows pytest-asyncio to coexist with other async testing plugins in the same codebase. + In strict mode pytest-asyncio will only run tests that have the *asyncio* marker + and will only evaluate async fixtures decorated with ``@pytest_asyncio.fixture``. + Test functions and fixtures without these markers and decorators will not be + handled by pytest-asyncio. -Pytest automatically enables installed plugins. As a result pytest plugins need to coexist peacefully in their default configuration. This is why strict mode is the default mode. + This mode is intended for projects that want to support multiple asynchronous + programming libraries as it allows pytest-asyncio to coexist with other async + testing plugins in the same codebase. -Auto mode ---------- + Pytest automatically enables installed plugins. As a result pytest plugins + need to coexist peacefully in their default configuration. This is why strict + mode is the default mode. -In *auto* mode pytest-asyncio automatically adds the *asyncio* marker to all asynchronous test functions. It will also take ownership of all async fixtures, regardless of whether they are decorated with ``@pytest.fixture`` or ``@pytest_asyncio.fixture``. + .. group-tab:: Auto mode -This mode is intended for projects that use *asyncio* as their only asynchronous programming library. Auto mode makes for the simplest test and fixture configuration and is the recommended default. + .. code-block:: toml -If you intend to support multiple asynchronous programming libraries, e.g. *asyncio* and *trio*, strict mode will be the preferred option. + [tool.pytest.ini_options] + asyncio_mode = "auto" + + In *auto* mode pytest-asyncio automatically adds the *asyncio* marker to all + asynchronous test functions. It will also take ownership of all async fixtures, + regardless of whether they are decorated with ``@pytest.fixture`` or + ``@pytest_asyncio.fixture``. + + This mode is intended for projects that use *asyncio* as their only asynchronous + programming library. Auto mode makes for the simplest test and fixture + configuration and is the recommended default. + + If you intend to support multiple asynchronous programming libraries, + e.g. *asyncio* and *trio*, strict mode will be the preferred option. .. _concepts/concurrent_execution: diff --git a/docs/conf.py b/docs/conf.py index 62a48a45..4718d217 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -extensions = [] +extensions = ["sphinx_tabs.tabs"] templates_path = ["_templates"] exclude_patterns = [] diff --git a/docs/how-to-guides/change_default_fixture_loop.rst b/docs/how-to-guides/change_default_fixture_loop.rst index b54fef8e..0917abc7 100644 --- a/docs/how-to-guides/change_default_fixture_loop.rst +++ b/docs/how-to-guides/change_default_fixture_loop.rst @@ -3,22 +3,27 @@ How to change the default event loop scope of all fixtures ========================================================== The :ref:`configuration/asyncio_default_fixture_loop_scope` configuration option sets the default event loop scope for asynchronous fixtures. The following code snippets configure all fixtures to run in a session-scoped loop by default: -.. code-block:: ini - :caption: pytest.ini +.. tabs:: - [pytest] - asyncio_default_fixture_loop_scope = session + .. tab:: pytest.ini -.. code-block:: toml - :caption: pyproject.toml + .. code-block:: ini - [tool.pytest.ini_options] - asyncio_default_fixture_loop_scope = "session" + [pytest] + asyncio_default_fixture_loop_scope = session -.. code-block:: ini - :caption: setup.cfg + .. tab:: pyproject.toml - [tool:pytest] - asyncio_default_fixture_loop_scope = session + .. code-block:: toml + + [tool.pytest.ini_options] + asyncio_default_fixture_loop_scope = "session" + + .. tab:: setup.cfg + + .. code-block:: ini + + [tool:pytest] + asyncio_default_fixture_loop_scope = session Please refer to :ref:`configuration/asyncio_default_fixture_loop_scope` for other valid scopes. diff --git a/docs/how-to-guides/change_default_test_loop.rst b/docs/how-to-guides/change_default_test_loop.rst index c5b625d1..d008a753 100644 --- a/docs/how-to-guides/change_default_test_loop.rst +++ b/docs/how-to-guides/change_default_test_loop.rst @@ -3,22 +3,28 @@ How to change the default event loop scope of all tests ======================================================= The :ref:`configuration/asyncio_default_test_loop_scope` configuration option sets the default event loop scope for asynchronous tests. The following code snippets configure all tests to run in a session-scoped loop by default: -.. code-block:: ini - :caption: pytest.ini +.. tabs:: - [pytest] - asyncio_default_test_loop_scope = session + .. tab:: pytest.ini -.. code-block:: toml - :caption: pyproject.toml + .. code-block:: ini - [tool.pytest.ini_options] - asyncio_default_test_loop_scope = "session" + [pytest] + asyncio_default_test_loop_scope = session -.. code-block:: ini - :caption: setup.cfg + .. tab:: pyproject.toml - [tool:pytest] - asyncio_default_test_loop_scope = session + .. code-block:: toml -Please refer to :ref:`configuration/asyncio_default_test_loop_scope` for other valid scopes. + [tool.pytest.ini_options] + asyncio_default_test_loop_scope = "session" + + .. tab:: setup.cfg + + .. code-block:: ini + + [tool:pytest] + asyncio_default_test_loop_scope = session + +Please refer to :ref:`configuration/asyncio_default_test_loop_scope` +for other valid scopes. diff --git a/docs/reference/markers/index.rst b/docs/reference/markers/index.rst index 761196a8..bc0a584a 100644 --- a/docs/reference/markers/index.rst +++ b/docs/reference/markers/index.rst @@ -9,25 +9,32 @@ Markers 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. -.. include:: function_scoped_loop_strict_mode_example.py - :code: python +Multiple async tests in a single class or module can be marked in different ways: -Multiple async tests in a single class or module can be marked using |pytestmark|_. +.. tabs:: -.. include:: function_scoped_loop_pytestmark_strict_mode_example.py - :code: python + .. tab:: decorator + + .. include:: function_scoped_loop_strict_mode_example.py + :code: python + + .. tab:: pytestmark + + .. include:: function_scoped_loop_pytestmark_strict_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. -By default, each test runs in it's own asyncio event loop. +By default, each test runs in its own asyncio event loop. Multiple tests can share the same event loop by providing a *loop_scope* keyword argument to the *asyncio* mark. -The supported scopes are *function,* *class,* and *module,* *package,* and *session*. -The following code example provides a shared event loop for all tests in `TestClassScopedLoop`: +The supported scopes are *function*, *class*, *module*, *package*, and *session*. + +The following code example provides a shared event loop for all tests in ``TestClassScopedLoop``: .. include:: class_scoped_loop_strict_mode_example.py :code: python -Similar to class-scoped event loops, a module-scoped loop is provided when setting mark's scope to *module:* +Similar to class-scoped event loops, a module-scoped loop is provided when setting mark's scope to *module*: .. include:: module_scoped_loop_strict_mode_example.py :code: python From aa306b1205cc6411c706e03c637ec3cbf1bd91e2 Mon Sep 17 00:00:00 2001 From: David Dzhalaev <72649244+dzhalaevd@users.noreply.github.com> Date: Tue, 7 Apr 2026 00:32:53 +0300 Subject: [PATCH 4/4] refactor: apply suggestions from code review - change news fragment to the correct category - add more context in news fragment Co-authored-by: Michael Seifert --- changelog.d/1395.downstream.rst | 1 + changelog.d/641.added.rst | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 changelog.d/1395.downstream.rst delete mode 100644 changelog.d/641.added.rst diff --git a/changelog.d/1395.downstream.rst b/changelog.d/1395.downstream.rst new file mode 100644 index 00000000..ff7cc818 --- /dev/null +++ b/changelog.d/1395.downstream.rst @@ -0,0 +1 @@ +Added dependency on ``sphinx-tabs >= 3.5`` to organize documentation examples into tabs. diff --git a/changelog.d/641.added.rst b/changelog.d/641.added.rst deleted file mode 100644 index c14a381e..00000000 --- a/changelog.d/641.added.rst +++ /dev/null @@ -1 +0,0 @@ -Added ``sphinx-tabs`` to organize documentation examples into tabs.