pytest-asyncio 0.25.2__tar.gz → 0.26.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/PKG-INFO +5 -4
  2. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/dependencies/default/constraints.txt +5 -5
  3. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/dependencies/docs/constraints.txt +3 -3
  4. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/concepts.rst +5 -1
  5. pytest_asyncio-0.26.0/docs/how-to-guides/change_default_test_loop.rst +24 -0
  6. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/index.rst +1 -1
  7. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/uvloop.rst +1 -1
  8. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/changelog.rst +13 -2
  9. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/configuration.rst +6 -0
  10. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pyproject.toml +6 -8
  11. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio/_version.py +9 -4
  12. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio/plugin.py +56 -26
  13. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio.egg-info/PKG-INFO +5 -4
  14. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio.egg-info/SOURCES.txt +1 -3
  15. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio.egg-info/requires.txt +3 -0
  16. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_asyncio_mark.py +77 -0
  17. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_event_loop_fixture.py +58 -0
  18. pytest_asyncio-0.25.2/docs/how-to-guides/run_session_tests_in_same_loop.rst +0 -10
  19. pytest_asyncio-0.25.2/docs/how-to-guides/session_scoped_loop_example.py +0 -10
  20. pytest_asyncio-0.25.2/docs/how-to-guides/test_session_scoped_loop_example.py +0 -63
  21. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/.readthedocs.yaml +0 -0
  22. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/LICENSE +0 -0
  23. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/MANIFEST.in +0 -0
  24. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/Makefile +0 -0
  25. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/README.rst +0 -0
  26. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/SECURITY.rst +0 -0
  27. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/dependencies/default/requirements.txt +0 -0
  28. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/dependencies/docs/requirements.txt +0 -0
  29. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/dependencies/pytest-min/constraints.txt +0 -0
  30. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/dependencies/pytest-min/requirements.txt +0 -0
  31. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/concepts_function_scope_example.py +0 -0
  32. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/concepts_module_scope_example.py +0 -0
  33. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/conf.py +0 -0
  34. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/change_default_fixture_loop.rst +0 -0
  35. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/change_fixture_loop.rst +0 -0
  36. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/change_fixture_loop_example.py +0 -0
  37. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/class_scoped_loop_example.py +0 -0
  38. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/migrate_from_0_21.rst +0 -0
  39. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/migrate_from_0_23.rst +0 -0
  40. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/module_scoped_loop_example.py +0 -0
  41. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/multiple_loops.rst +0 -0
  42. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/multiple_loops_example.py +0 -0
  43. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/package_scoped_loop_example.py +0 -0
  44. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/run_class_tests_in_same_loop.rst +0 -0
  45. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/run_module_tests_in_same_loop.rst +0 -0
  46. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/run_package_tests_in_same_loop.rst +0 -0
  47. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/test_item_is_async.rst +0 -0
  48. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/how-to-guides/test_item_is_async_example.py +0 -0
  49. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/index.rst +0 -0
  50. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/decorators/index.rst +0 -0
  51. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/decorators/pytest_asyncio_fixture_example.py +0 -0
  52. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/fixtures/event_loop_example.py +0 -0
  53. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/fixtures/event_loop_policy_example.py +0 -0
  54. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/fixtures/event_loop_policy_parametrized_example.py +0 -0
  55. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/fixtures/index.rst +0 -0
  56. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/functions.rst +0 -0
  57. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/index.rst +0 -0
  58. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/markers/class_scoped_loop_custom_policies_strict_mode_example.py +0 -0
  59. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/markers/class_scoped_loop_strict_mode_example.py +0 -0
  60. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/markers/class_scoped_loop_with_fixture_strict_mode_example.py +0 -0
  61. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/markers/function_scoped_loop_pytestmark_strict_mode_example.py +0 -0
  62. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/markers/function_scoped_loop_strict_mode_example.py +0 -0
  63. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/markers/index.rst +0 -0
  64. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/reference/markers/module_scoped_loop_strict_mode_example.py +0 -0
  65. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/docs/support.rst +0 -0
  66. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio/__init__.py +0 -0
  67. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio/py.typed +0 -0
  68. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio.egg-info/dependency_links.txt +0 -0
  69. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio.egg-info/entry_points.txt +0 -0
  70. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/pytest_asyncio.egg-info/top_level.txt +0 -0
  71. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/setup.cfg +0 -0
  72. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/__init__.py +0 -0
  73. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/async_fixtures/__init__.py +0 -0
  74. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/async_fixtures/test_async_fixtures.py +0 -0
  75. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/async_fixtures/test_async_fixtures_contextvars.py +0 -0
  76. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/async_fixtures/test_async_fixtures_scope.py +0 -0
  77. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/async_fixtures/test_async_fixtures_with_finalizer.py +0 -0
  78. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/async_fixtures/test_async_gen_fixtures.py +0 -0
  79. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/async_fixtures/test_nested.py +0 -0
  80. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/async_fixtures/test_parametrized_loop.py +0 -0
  81. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/async_fixtures/test_shared_module_fixture.py +0 -0
  82. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/conftest.py +0 -0
  83. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/hypothesis/__init__.py +0 -0
  84. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/hypothesis/test_base.py +0 -0
  85. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/loop_fixture_scope/__init__.py +0 -0
  86. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/loop_fixture_scope/conftest.py +0 -0
  87. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/loop_fixture_scope/test_loop_fixture_scope.py +0 -0
  88. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/markers/__init__.py +0 -0
  89. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/markers/test_class_scope.py +0 -0
  90. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/markers/test_function_scope.py +0 -0
  91. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/markers/test_invalid_arguments.py +0 -0
  92. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/markers/test_mixed_scope.py +0 -0
  93. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/markers/test_module_scope.py +0 -0
  94. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/markers/test_package_scope.py +0 -0
  95. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/markers/test_session_scope.py +0 -0
  96. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/modes/__init__.py +0 -0
  97. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/modes/test_auto_mode.py +0 -0
  98. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/modes/test_strict_mode.py +0 -0
  99. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_asyncio_fixture.py +0 -0
  100. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_dependent_fixtures.py +0 -0
  101. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_doctest.py +0 -0
  102. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_event_loop_fixture_finalizer.py +0 -0
  103. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_event_loop_fixture_override_deprecation.py +0 -0
  104. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_explicit_event_loop_fixture_request.py +0 -0
  105. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_fixture_loop_scopes.py +0 -0
  106. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_import.py +0 -0
  107. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_is_async_test.py +0 -0
  108. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_multiloop.py +0 -0
  109. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_port_factories.py +0 -0
  110. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_simple.py +0 -0
  111. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_skips.py +0 -0
  112. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tests/test_subprocess.py +0 -0
  113. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tools/get-version.py +0 -0
  114. {pytest_asyncio-0.25.2 → pytest_asyncio-0.26.0}/tox.ini +0 -0
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pytest-asyncio
3
- Version: 0.25.2
3
+ Version: 0.26.0
4
4
  Summary: Pytest support for asyncio
5
5
  Author-email: "Tin Tvrtković <tinchester@gmail.com>" <tinchester@gmail.com>
6
- License: Apache 2.0
6
+ License-Expression: Apache-2.0
7
7
  Project-URL: Bug Tracker, https://github.com/pytest-dev/pytest-asyncio/issues
8
8
  Project-URL: Changelog, https://pytest-asyncio.readthedocs.io/en/latest/reference/changelog.html
9
9
  Project-URL: Documentation, https://pytest-asyncio.readthedocs.io
@@ -13,7 +13,6 @@ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Framework :: AsyncIO
14
14
  Classifier: Framework :: Pytest
15
15
  Classifier: Intended Audience :: Developers
16
- Classifier: License :: OSI Approved :: Apache Software License
17
16
  Classifier: Programming Language :: Python :: 3 :: Only
18
17
  Classifier: Programming Language :: Python :: 3.9
19
18
  Classifier: Programming Language :: Python :: 3.10
@@ -26,12 +25,14 @@ Requires-Python: >=3.9
26
25
  Description-Content-Type: text/x-rst
27
26
  License-File: LICENSE
28
27
  Requires-Dist: pytest<9,>=8.2
28
+ Requires-Dist: typing-extensions>=4.12; python_version < "3.10"
29
29
  Provides-Extra: docs
30
30
  Requires-Dist: sphinx>=5.3; extra == "docs"
31
31
  Requires-Dist: sphinx-rtd-theme>=1; extra == "docs"
32
32
  Provides-Extra: testing
33
33
  Requires-Dist: coverage>=6.2; extra == "testing"
34
34
  Requires-Dist: hypothesis>=5.7.1; extra == "testing"
35
+ Dynamic: license-file
35
36
 
36
37
  pytest-asyncio
37
38
  ==============
@@ -1,11 +1,11 @@
1
- attrs==24.3.0
2
- coverage==7.6.10
1
+ attrs==25.3.0
2
+ coverage==7.7.1
3
3
  exceptiongroup==1.2.2
4
- hypothesis==6.123.4
5
- iniconfig==2.0.0
4
+ hypothesis==6.130.3
5
+ iniconfig==2.1.0
6
6
  packaging==24.2
7
7
  pluggy==1.5.0
8
- pytest==8.3.4
8
+ pytest==8.3.5
9
9
  sortedcontainers==2.4.0
10
10
  tomli==2.2.1
11
11
  typing_extensions==4.12.2
@@ -1,11 +1,11 @@
1
1
  alabaster==0.7.16
2
- Babel==2.16.0
3
- certifi==2024.12.14
2
+ Babel==2.17.0
3
+ certifi==2025.1.31
4
4
  charset-normalizer==3.4.1
5
5
  docutils==0.21.2
6
6
  idna==3.10
7
7
  imagesize==1.4.1
8
- Jinja2==3.1.5
8
+ Jinja2==3.1.6
9
9
  MarkupSafe==3.0.2
10
10
  packaging==24.2
11
11
  Pygments==2.19.1
@@ -47,8 +47,12 @@ Assigning neighboring tests to different event loop scopes is discouraged as it
47
47
  Test discovery modes
48
48
  ====================
49
49
 
50
- Pytest-asyncio provides two modes for test discovery, *strict* and *auto*.
50
+ 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:
51
51
 
52
+ .. code-block:: toml
53
+
54
+ [tool.pytest.ini_options]
55
+ asyncio_mode = "auto" # or "strict"
52
56
 
53
57
  Strict mode
54
58
  -----------
@@ -0,0 +1,24 @@
1
+ =======================================================
2
+ How to change the default event loop scope of all tests
3
+ =======================================================
4
+ 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:
5
+
6
+ .. code-block:: ini
7
+ :caption: pytest.ini
8
+
9
+ [pytest]
10
+ asyncio_default_test_loop_scope = session
11
+
12
+ .. code-block:: toml
13
+ :caption: pyproject.toml
14
+
15
+ [tool.pytest.ini_options]
16
+ asyncio_default_test_loop_scope = "session"
17
+
18
+ .. code-block:: ini
19
+ :caption: setup.cfg
20
+
21
+ [tool:pytest]
22
+ asyncio_default_test_loop_scope = session
23
+
24
+ Please refer to :ref:`configuration/asyncio_default_test_loop_scope` for other valid scopes.
@@ -9,10 +9,10 @@ How-To Guides
9
9
  migrate_from_0_23
10
10
  change_fixture_loop
11
11
  change_default_fixture_loop
12
+ change_default_test_loop
12
13
  run_class_tests_in_same_loop
13
14
  run_module_tests_in_same_loop
14
15
  run_package_tests_in_same_loop
15
- run_session_tests_in_same_loop
16
16
  multiple_loops
17
17
  uvloop
18
18
  test_item_is_async
@@ -2,7 +2,7 @@
2
2
  How to test with uvloop
3
3
  =======================
4
4
 
5
- Redefinig 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:
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
6
  Replace the default event loop policy in your *conftest.py:*
7
7
 
8
8
  .. code-block:: python
@@ -2,11 +2,23 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
- 0.25.2 (2025-01-08)
5
+ 0.26.0 (2025-03-25)
6
6
  ===================
7
+ - Adds configuration option that sets default event loop scope for all tests `#793 <https://github.com/pytest-dev/pytest-asyncio/issues/793>`_
8
+ - Improved type annotations for ``pytest_asyncio.fixture`` `#1045 <https://github.com/pytest-dev/pytest-asyncio/pull/1045>`_
9
+ - Added ``typing-extensions`` as additional dependency for Python ``<3.10`` `#1045 <https://github.com/pytest-dev/pytest-asyncio/pull/1045>`_
10
+
11
+
12
+ 0.25.3 (2025-01-28)
13
+ ===================
14
+ - Avoid errors in cleanup of async generators when event loop is already closed `#1040 <https://github.com/pytest-dev/pytest-asyncio/issues/1040>`_
15
+
7
16
 
17
+ 0.25.2 (2025-01-08)
18
+ ===================
8
19
  - Call ``loop.shutdown_asyncgens()`` before closing the event loop to ensure async generators are closed in the same manner as ``asyncio.run`` does `#1034 <https://github.com/pytest-dev/pytest-asyncio/pull/1034>`_
9
20
 
21
+
10
22
  0.25.1 (2025-01-02)
11
23
  ===================
12
24
  - Fixes an issue that caused a broken event loop when a function-scoped test was executed in between two tests with wider loop scope `#950 <https://github.com/pytest-dev/pytest-asyncio/issues/950>`_
@@ -22,7 +34,6 @@ Changelog
22
34
  - Propagates `contextvars` set in async fixtures to other fixtures and tests on Python 3.11 and above. `#1008 <https://github.com/pytest-dev/pytest-asyncio/pull/1008>`_
23
35
 
24
36
 
25
-
26
37
  0.24.0 (2024-08-22)
27
38
  ===================
28
39
  - BREAKING: Updated minimum supported pytest version to v8.2.0
@@ -8,6 +8,12 @@ asyncio_default_fixture_loop_scope
8
8
  ==================================
9
9
  Determines the default event loop scope of asynchronous fixtures. When this configuration option is unset, it defaults to the fixture scope. In future versions of pytest-asyncio, the value will default to ``function`` when unset. Possible values are: ``function``, ``class``, ``module``, ``package``, ``session``
10
10
 
11
+ .. _configuration/asyncio_default_test_loop_scope:
12
+
13
+ asyncio_default_test_loop_scope
14
+ ===============================
15
+ Determines the default event loop scope of asynchronous tests. When this configuration option is unset, it default to function scope. Possible values are: ``function``, ``class``, ``module``, ``package``, ``session``
16
+
11
17
  asyncio_mode
12
18
  ============
13
19
  The pytest-asyncio mode can be set by the ``asyncio_mode`` configuration option in the `configuration file
@@ -1,10 +1,8 @@
1
1
  [build-system]
2
2
  build-backend = "setuptools.build_meta"
3
-
4
3
  requires = [
5
- "setuptools>=51",
4
+ "setuptools>=77",
6
5
  "setuptools-scm[toml]>=6.2",
7
- "wheel>=0.36",
8
6
  ]
9
7
 
10
8
  [project]
@@ -12,7 +10,10 @@ name = "pytest-asyncio"
12
10
  description = "Pytest support for asyncio"
13
11
  readme.content-type = "text/x-rst"
14
12
  readme.file = "README.rst"
15
- license.text = "Apache 2.0"
13
+ license = "Apache-2.0"
14
+ license-files = [
15
+ "LICENSE",
16
+ ]
16
17
  authors = [
17
18
  { name = "Tin Tvrtković <tinchester@gmail.com>", email = "tinchester@gmail.com" },
18
19
  ]
@@ -22,7 +23,6 @@ classifiers = [
22
23
  "Framework :: AsyncIO",
23
24
  "Framework :: Pytest",
24
25
  "Intended Audience :: Developers",
25
- "License :: OSI Approved :: Apache Software License",
26
26
  "Programming Language :: Python :: 3 :: Only",
27
27
  "Programming Language :: Python :: 3.9",
28
28
  "Programming Language :: Python :: 3.10",
@@ -38,6 +38,7 @@ dynamic = [
38
38
 
39
39
  dependencies = [
40
40
  "pytest>=8.2,<9",
41
+ "typing-extensions>=4.12; python_version<'3.10'",
41
42
  ]
42
43
  optional-dependencies.docs = [
43
44
  "sphinx>=5.3",
@@ -59,9 +60,6 @@ packages = [
59
60
  "pytest_asyncio",
60
61
  ]
61
62
  include-package-data = true
62
- license-files = [
63
- "LICENSE",
64
- ]
65
63
 
66
64
  [tool.setuptools_scm]
67
65
  write_to = "pytest_asyncio/_version.py"
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '0.25.2'
16
- __version_tuple__ = version_tuple = (0, 25, 2)
20
+ __version__ = version = '0.26.0'
21
+ __version_tuple__ = version_tuple = (0, 26, 0)
@@ -9,6 +9,7 @@ import enum
9
9
  import functools
10
10
  import inspect
11
11
  import socket
12
+ import sys
12
13
  import warnings
13
14
  from asyncio import AbstractEventLoop, AbstractEventLoopPolicy
14
15
  from collections.abc import (
@@ -53,17 +54,17 @@ from pytest import (
53
54
  StashKey,
54
55
  )
55
56
 
57
+ if sys.version_info >= (3, 10):
58
+ from typing import ParamSpec
59
+ else:
60
+ from typing_extensions import ParamSpec
61
+
62
+
56
63
  _ScopeName = Literal["session", "package", "module", "class", "function"]
57
64
  _T = TypeVar("_T")
58
-
59
- SimpleFixtureFunction = TypeVar(
60
- "SimpleFixtureFunction", bound=Callable[..., Awaitable[object]]
61
- )
62
- FactoryFixtureFunction = TypeVar(
63
- "FactoryFixtureFunction", bound=Callable[..., AsyncIterator[object]]
64
- )
65
- FixtureFunction = Union[SimpleFixtureFunction, FactoryFixtureFunction]
66
- FixtureFunctionMarker = Callable[[FixtureFunction], FixtureFunction]
65
+ _R = TypeVar("_R", bound=Union[Awaitable[Any], AsyncIterator[Any]])
66
+ _P = ParamSpec("_P")
67
+ FixtureFunction = Callable[_P, _R]
67
68
 
68
69
 
69
70
  class PytestAsyncioError(Exception):
@@ -107,11 +108,17 @@ def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None
107
108
  help="default scope of the asyncio event loop used to execute async fixtures",
108
109
  default=None,
109
110
  )
111
+ parser.addini(
112
+ "asyncio_default_test_loop_scope",
113
+ type="string",
114
+ help="default scope of the asyncio event loop used to execute tests",
115
+ default="function",
116
+ )
110
117
 
111
118
 
112
119
  @overload
113
120
  def fixture(
114
- fixture_function: FixtureFunction,
121
+ fixture_function: FixtureFunction[_P, _R],
115
122
  *,
116
123
  scope: _ScopeName | Callable[[str, Config], _ScopeName] = ...,
117
124
  loop_scope: _ScopeName | None = ...,
@@ -123,7 +130,7 @@ def fixture(
123
130
  | None
124
131
  ) = ...,
125
132
  name: str | None = ...,
126
- ) -> FixtureFunction: ...
133
+ ) -> FixtureFunction[_P, _R]: ...
127
134
 
128
135
 
129
136
  @overload
@@ -140,14 +147,17 @@ def fixture(
140
147
  | None
141
148
  ) = ...,
142
149
  name: str | None = None,
143
- ) -> FixtureFunctionMarker: ...
150
+ ) -> Callable[[FixtureFunction[_P, _R]], FixtureFunction[_P, _R]]: ...
144
151
 
145
152
 
146
153
  def fixture(
147
- fixture_function: FixtureFunction | None = None,
154
+ fixture_function: FixtureFunction[_P, _R] | None = None,
148
155
  loop_scope: _ScopeName | None = None,
149
156
  **kwargs: Any,
150
- ) -> FixtureFunction | FixtureFunctionMarker:
157
+ ) -> (
158
+ FixtureFunction[_P, _R]
159
+ | Callable[[FixtureFunction[_P, _R]], FixtureFunction[_P, _R]]
160
+ ):
151
161
  if fixture_function is not None:
152
162
  _make_asyncio_fixture_function(fixture_function, loop_scope)
153
163
  return pytest.fixture(fixture_function, **kwargs)
@@ -155,7 +165,7 @@ def fixture(
155
165
  else:
156
166
 
157
167
  @functools.wraps(fixture)
158
- def inner(fixture_function: FixtureFunction) -> FixtureFunction:
168
+ def inner(fixture_function: FixtureFunction[_P, _R]) -> FixtureFunction[_P, _R]:
159
169
  return fixture(fixture_function, loop_scope=loop_scope, **kwargs)
160
170
 
161
171
  return inner
@@ -217,9 +227,15 @@ def pytest_configure(config: Config) -> None:
217
227
  def pytest_report_header(config: Config) -> list[str]:
218
228
  """Add asyncio config to pytest header."""
219
229
  mode = _get_asyncio_mode(config)
220
- default_loop_scope = config.getini("asyncio_default_fixture_loop_scope")
230
+ default_fixture_loop_scope = config.getini("asyncio_default_fixture_loop_scope")
231
+ default_test_loop_scope = _get_default_test_loop_scope(config)
232
+ header = [
233
+ f"mode={mode}",
234
+ f"asyncio_default_fixture_loop_scope={default_fixture_loop_scope}",
235
+ f"asyncio_default_test_loop_scope={default_test_loop_scope}",
236
+ ]
221
237
  return [
222
- f"asyncio: mode={mode}, asyncio_default_fixture_loop_scope={default_loop_scope}"
238
+ "asyncio: " + ", ".join(header),
223
239
  ]
224
240
 
225
241
 
@@ -667,7 +683,7 @@ _fixture_scope_by_collector_type: Mapping[type[pytest.Collector], _ScopeName] =
667
683
 
668
684
  # A stack used to push package-scoped loops during collection of a package
669
685
  # and pop those loops during collection of a Module
670
- __package_loop_stack: list[FixtureFunctionMarker | FixtureFunction] = []
686
+ __package_loop_stack: list[Callable[..., Any]] = []
671
687
 
672
688
 
673
689
  @pytest.hookimpl
@@ -807,7 +823,8 @@ def pytest_generate_tests(metafunc: Metafunc) -> None:
807
823
  marker = metafunc.definition.get_closest_marker("asyncio")
808
824
  if not marker:
809
825
  return
810
- scope = _get_marked_loop_scope(marker)
826
+ default_loop_scope = _get_default_test_loop_scope(metafunc.config)
827
+ scope = _get_marked_loop_scope(marker, default_loop_scope)
811
828
  if scope == "function":
812
829
  return
813
830
  event_loop_node = _retrieve_scope_root(metafunc.definition, scope)
@@ -1078,7 +1095,8 @@ def pytest_runtest_setup(item: pytest.Item) -> None:
1078
1095
  marker = item.get_closest_marker("asyncio")
1079
1096
  if marker is None:
1080
1097
  return
1081
- scope = _get_marked_loop_scope(marker)
1098
+ default_loop_scope = _get_default_test_loop_scope(item.config)
1099
+ scope = _get_marked_loop_scope(marker, default_loop_scope)
1082
1100
  if scope != "function":
1083
1101
  parent_node = _retrieve_scope_root(item, scope)
1084
1102
  event_loop_fixture_id = parent_node.stash[_event_loop_fixture_id]
@@ -1108,7 +1126,9 @@ Please use the "loop_scope" argument instead.
1108
1126
  """
1109
1127
 
1110
1128
 
1111
- def _get_marked_loop_scope(asyncio_marker: Mark) -> _ScopeName:
1129
+ def _get_marked_loop_scope(
1130
+ asyncio_marker: Mark, default_loop_scope: _ScopeName
1131
+ ) -> _ScopeName:
1112
1132
  assert asyncio_marker.name == "asyncio"
1113
1133
  if asyncio_marker.args or (
1114
1134
  asyncio_marker.kwargs and set(asyncio_marker.kwargs) - {"loop_scope", "scope"}
@@ -1119,12 +1139,18 @@ def _get_marked_loop_scope(asyncio_marker: Mark) -> _ScopeName:
1119
1139
  raise pytest.UsageError(_DUPLICATE_LOOP_SCOPE_DEFINITION_ERROR)
1120
1140
  warnings.warn(PytestDeprecationWarning(_MARKER_SCOPE_KWARG_DEPRECATION_WARNING))
1121
1141
  scope = asyncio_marker.kwargs.get("loop_scope") or asyncio_marker.kwargs.get(
1122
- "scope", "function"
1142
+ "scope"
1123
1143
  )
1144
+ if scope is None:
1145
+ scope = default_loop_scope
1124
1146
  assert scope in {"function", "class", "module", "package", "session"}
1125
1147
  return scope
1126
1148
 
1127
1149
 
1150
+ def _get_default_test_loop_scope(config: Config) -> _ScopeName:
1151
+ return config.getini("asyncio_default_test_loop_scope")
1152
+
1153
+
1128
1154
  def _retrieve_scope_root(item: Collector | Item, scope: str) -> Collector:
1129
1155
  node_type_by_scope = {
1130
1156
  "class": Class,
@@ -1164,10 +1190,14 @@ def _provide_event_loop() -> Iterator[asyncio.AbstractEventLoop]:
1164
1190
  try:
1165
1191
  yield loop
1166
1192
  finally:
1167
- try:
1168
- loop.run_until_complete(loop.shutdown_asyncgens())
1169
- finally:
1170
- loop.close()
1193
+ # cleanup the event loop if it hasn't been cleaned up already
1194
+ if not loop.is_closed():
1195
+ try:
1196
+ loop.run_until_complete(loop.shutdown_asyncgens())
1197
+ except Exception as e:
1198
+ warnings.warn(f"Error cleaning up asyncio loop: {e}", RuntimeWarning)
1199
+ finally:
1200
+ loop.close()
1171
1201
 
1172
1202
 
1173
1203
  @pytest.fixture(scope="session")
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pytest-asyncio
3
- Version: 0.25.2
3
+ Version: 0.26.0
4
4
  Summary: Pytest support for asyncio
5
5
  Author-email: "Tin Tvrtković <tinchester@gmail.com>" <tinchester@gmail.com>
6
- License: Apache 2.0
6
+ License-Expression: Apache-2.0
7
7
  Project-URL: Bug Tracker, https://github.com/pytest-dev/pytest-asyncio/issues
8
8
  Project-URL: Changelog, https://pytest-asyncio.readthedocs.io/en/latest/reference/changelog.html
9
9
  Project-URL: Documentation, https://pytest-asyncio.readthedocs.io
@@ -13,7 +13,6 @@ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Framework :: AsyncIO
14
14
  Classifier: Framework :: Pytest
15
15
  Classifier: Intended Audience :: Developers
16
- Classifier: License :: OSI Approved :: Apache Software License
17
16
  Classifier: Programming Language :: Python :: 3 :: Only
18
17
  Classifier: Programming Language :: Python :: 3.9
19
18
  Classifier: Programming Language :: Python :: 3.10
@@ -26,12 +25,14 @@ Requires-Python: >=3.9
26
25
  Description-Content-Type: text/x-rst
27
26
  License-File: LICENSE
28
27
  Requires-Dist: pytest<9,>=8.2
28
+ Requires-Dist: typing-extensions>=4.12; python_version < "3.10"
29
29
  Provides-Extra: docs
30
30
  Requires-Dist: sphinx>=5.3; extra == "docs"
31
31
  Requires-Dist: sphinx-rtd-theme>=1; extra == "docs"
32
32
  Provides-Extra: testing
33
33
  Requires-Dist: coverage>=6.2; extra == "testing"
34
34
  Requires-Dist: hypothesis>=5.7.1; extra == "testing"
35
+ Dynamic: license-file
35
36
 
36
37
  pytest-asyncio
37
38
  ==============
@@ -20,6 +20,7 @@ docs/conf.py
20
20
  docs/index.rst
21
21
  docs/support.rst
22
22
  docs/how-to-guides/change_default_fixture_loop.rst
23
+ docs/how-to-guides/change_default_test_loop.rst
23
24
  docs/how-to-guides/change_fixture_loop.rst
24
25
  docs/how-to-guides/change_fixture_loop_example.py
25
26
  docs/how-to-guides/class_scoped_loop_example.py
@@ -33,11 +34,8 @@ docs/how-to-guides/package_scoped_loop_example.py
33
34
  docs/how-to-guides/run_class_tests_in_same_loop.rst
34
35
  docs/how-to-guides/run_module_tests_in_same_loop.rst
35
36
  docs/how-to-guides/run_package_tests_in_same_loop.rst
36
- docs/how-to-guides/run_session_tests_in_same_loop.rst
37
- docs/how-to-guides/session_scoped_loop_example.py
38
37
  docs/how-to-guides/test_item_is_async.rst
39
38
  docs/how-to-guides/test_item_is_async_example.py
40
- docs/how-to-guides/test_session_scoped_loop_example.py
41
39
  docs/how-to-guides/uvloop.rst
42
40
  docs/reference/changelog.rst
43
41
  docs/reference/configuration.rst
@@ -1,5 +1,8 @@
1
1
  pytest<9,>=8.2
2
2
 
3
+ [:python_version < "3.10"]
4
+ typing-extensions>=4.12
5
+
3
6
  [docs]
4
7
  sphinx>=5.3
5
8
  sphinx-rtd-theme>=1
@@ -146,3 +146,80 @@ def test_asyncio_mark_on_async_generator_staticmethod_emits_warning_in_auto_mode
146
146
  result.stdout.fnmatch_lines(
147
147
  ["*Tests based on asynchronous generators are not supported*"]
148
148
  )
149
+
150
+
151
+ def test_asyncio_marker_fallbacks_to_configured_default_loop_scope_if_not_set(
152
+ pytester: Pytester,
153
+ ):
154
+ pytester.makeini(
155
+ dedent(
156
+ """\
157
+ [pytest]
158
+ asyncio_default_fixture_loop_scope = function
159
+ asyncio_default_test_loop_scope = session
160
+ """
161
+ )
162
+ )
163
+
164
+ pytester.makepyfile(
165
+ dedent(
166
+ """\
167
+ import asyncio
168
+ import pytest_asyncio
169
+ import pytest
170
+
171
+ loop: asyncio.AbstractEventLoop
172
+
173
+ @pytest_asyncio.fixture(loop_scope="session", scope="session")
174
+ async def session_loop_fixture():
175
+ global loop
176
+ loop = asyncio.get_running_loop()
177
+
178
+ async def test_a(session_loop_fixture):
179
+ global loop
180
+ assert asyncio.get_running_loop() is loop
181
+ """
182
+ )
183
+ )
184
+
185
+ result = pytester.runpytest("--asyncio-mode=auto")
186
+ result.assert_outcomes(passed=1)
187
+
188
+
189
+ def test_asyncio_marker_uses_marker_loop_scope_even_if_config_is_set(
190
+ pytester: Pytester,
191
+ ):
192
+ pytester.makeini(
193
+ dedent(
194
+ """\
195
+ [pytest]
196
+ asyncio_default_fixture_loop_scope = function
197
+ asyncio_default_test_loop_scope = module
198
+ """
199
+ )
200
+ )
201
+
202
+ pytester.makepyfile(
203
+ dedent(
204
+ """\
205
+ import asyncio
206
+ import pytest_asyncio
207
+ import pytest
208
+
209
+ loop: asyncio.AbstractEventLoop
210
+
211
+ @pytest_asyncio.fixture(loop_scope="session", scope="session")
212
+ async def session_loop_fixture():
213
+ global loop
214
+ loop = asyncio.get_running_loop()
215
+
216
+ @pytest.mark.asyncio(loop_scope="session")
217
+ async def test_a(session_loop_fixture):
218
+ global loop
219
+ assert asyncio.get_running_loop() is loop
220
+ """
221
+ )
222
+ )
223
+
224
+ result = pytester.runpytest("--asyncio-mode=auto")
225
+ result.assert_outcomes(passed=1)
@@ -80,3 +80,61 @@ def test_event_loop_fixture_handles_unclosed_async_gen(
80
80
  )
81
81
  result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "default")
82
82
  result.assert_outcomes(passed=1, warnings=0)
83
+
84
+
85
+ def test_event_loop_already_closed(
86
+ pytester: Pytester,
87
+ ):
88
+ pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
89
+ pytester.makepyfile(
90
+ dedent(
91
+ """\
92
+ import asyncio
93
+ import pytest
94
+ import pytest_asyncio
95
+ pytest_plugins = 'pytest_asyncio'
96
+
97
+ @pytest_asyncio.fixture
98
+ async def _event_loop():
99
+ return asyncio.get_running_loop()
100
+
101
+ @pytest.fixture
102
+ def cleanup_after(_event_loop):
103
+ yield
104
+ # fixture has its own cleanup code
105
+ _event_loop.close()
106
+
107
+ @pytest.mark.asyncio
108
+ async def test_something(cleanup_after):
109
+ await asyncio.sleep(0.01)
110
+ """
111
+ )
112
+ )
113
+ result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "default")
114
+ result.assert_outcomes(passed=1, warnings=0)
115
+
116
+
117
+ def test_event_loop_fixture_asyncgen_error(
118
+ pytester: Pytester,
119
+ ):
120
+ pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
121
+ pytester.makepyfile(
122
+ dedent(
123
+ """\
124
+ import asyncio
125
+ import pytest
126
+
127
+ pytest_plugins = 'pytest_asyncio'
128
+
129
+ @pytest.mark.asyncio
130
+ async def test_something():
131
+ # mock shutdown_asyncgen failure
132
+ loop = asyncio.get_running_loop()
133
+ async def fail():
134
+ raise RuntimeError("mock error cleaning up...")
135
+ loop.shutdown_asyncgens = fail
136
+ """
137
+ )
138
+ )
139
+ result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "default")
140
+ result.assert_outcomes(passed=1, warnings=1)
@@ -1,10 +0,0 @@
1
- ==========================================================
2
- How to run all tests in the session in the same event loop
3
- ==========================================================
4
- All tests can be run inside the same event loop by marking them with ``pytest.mark.asyncio(loop_scope="session")``.
5
- The easiest way to mark all tests is via a ``pytest_collection_modifyitems`` hook in the ``conftest.py`` at the root folder of your test suite.
6
-
7
- .. include:: session_scoped_loop_example.py
8
- :code: python
9
-
10
- Note that this will also override *all* manually applied marks in *strict* mode.
@@ -1,10 +0,0 @@
1
- import pytest
2
-
3
- from pytest_asyncio import is_async_test
4
-
5
-
6
- def pytest_collection_modifyitems(items):
7
- pytest_asyncio_tests = (item for item in items if is_async_test(item))
8
- session_scope_marker = pytest.mark.asyncio(loop_scope="session")
9
- for async_test in pytest_asyncio_tests:
10
- async_test.add_marker(session_scope_marker, append=False)
@@ -1,63 +0,0 @@
1
- from pathlib import Path
2
- from textwrap import dedent
3
-
4
- from pytest import Pytester
5
-
6
-
7
- def test_session_scoped_loop_configuration_works_in_auto_mode(
8
- pytester: Pytester,
9
- ):
10
- session_wide_mark_conftest = (
11
- Path(__file__).parent / "session_scoped_loop_example.py"
12
- )
13
- pytester.makeconftest(session_wide_mark_conftest.read_text())
14
- pytester.makepyfile(
15
- dedent(
16
- """\
17
- import asyncio
18
-
19
- session_loop = None
20
-
21
- async def test_store_loop(request):
22
- global session_loop
23
- session_loop = asyncio.get_running_loop()
24
-
25
- async def test_compare_loop(request):
26
- global session_loop
27
- assert asyncio.get_running_loop() is session_loop
28
- """
29
- )
30
- )
31
- result = pytester.runpytest_subprocess("--asyncio-mode=auto")
32
- result.assert_outcomes(passed=2)
33
-
34
-
35
- def test_session_scoped_loop_configuration_works_in_strict_mode(
36
- pytester: Pytester,
37
- ):
38
- session_wide_mark_conftest = (
39
- Path(__file__).parent / "session_scoped_loop_example.py"
40
- )
41
- pytester.makeconftest(session_wide_mark_conftest.read_text())
42
- pytester.makepyfile(
43
- dedent(
44
- """\
45
- import asyncio
46
- import pytest
47
-
48
- session_loop = None
49
-
50
- @pytest.mark.asyncio
51
- async def test_store_loop(request):
52
- global session_loop
53
- session_loop = asyncio.get_running_loop()
54
-
55
- @pytest.mark.asyncio
56
- async def test_compare_loop(request):
57
- global session_loop
58
- assert asyncio.get_running_loop() is session_loop
59
- """
60
- )
61
- )
62
- result = pytester.runpytest_subprocess("--asyncio-mode=strict")
63
- result.assert_outcomes(passed=2)
File without changes
File without changes