pytest-asyncio-concurrent-fork 0.5.2__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 (25) hide show
  1. pytest_asyncio_concurrent_fork-0.5.2/LICENSE +22 -0
  2. pytest_asyncio_concurrent_fork-0.5.2/PKG-INFO +212 -0
  3. pytest_asyncio_concurrent_fork-0.5.2/README.rst +158 -0
  4. pytest_asyncio_concurrent_fork-0.5.2/pyproject.toml +119 -0
  5. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent/__init__.py +7 -0
  6. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent/fixture_async.py +139 -0
  7. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent/grouping.py +152 -0
  8. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent/hooks.py +40 -0
  9. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent/plugin.py +534 -0
  10. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent_fork.egg-info/PKG-INFO +212 -0
  11. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent_fork.egg-info/SOURCES.txt +23 -0
  12. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent_fork.egg-info/dependency_links.txt +1 -0
  13. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent_fork.egg-info/entry_points.txt +2 -0
  14. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent_fork.egg-info/requires.txt +4 -0
  15. pytest_asyncio_concurrent_fork-0.5.2/pytest_asyncio_concurrent_fork.egg-info/top_level.txt +1 -0
  16. pytest_asyncio_concurrent_fork-0.5.2/setup.cfg +4 -0
  17. pytest_asyncio_concurrent_fork-0.5.2/tests/test_async_fixture.py +139 -0
  18. pytest_asyncio_concurrent_fork-0.5.2/tests/test_compatibility.py +34 -0
  19. pytest_asyncio_concurrent_fork-0.5.2/tests/test_error.py +64 -0
  20. pytest_asyncio_concurrent_fork-0.5.2/tests/test_fixture_lifecycle.py +275 -0
  21. pytest_asyncio_concurrent_fork-0.5.2/tests/test_fixture_scoping.py +343 -0
  22. pytest_asyncio_concurrent_fork-0.5.2/tests/test_grouping.py +138 -0
  23. pytest_asyncio_concurrent_fork-0.5.2/tests/test_grouping_strategy.py +209 -0
  24. pytest_asyncio_concurrent_fork-0.5.2/tests/test_logging.py +30 -0
  25. pytest_asyncio_concurrent_fork-0.5.2/tests/test_status.py +288 -0
@@ -0,0 +1,22 @@
1
+
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2024 Zane Chen
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytest-asyncio-concurrent-fork
3
+ Version: 0.5.2
4
+ Summary: Temporary pytest compatibility fork of pytest-asyncio-concurrent.
5
+ Author-email: Zane Chen <czl970721@gmail.com>
6
+ Maintainer-email: Zane Chen <czl970721@gmail.com>
7
+ License:
8
+ The MIT License (MIT)
9
+
10
+ Copyright (c) 2024 Zane Chen
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in
20
+ all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
+ THE SOFTWARE.
29
+
30
+ Project-URL: Repository, https://github.com/AnswerDotAI/pytest-asyncio-concurrent
31
+ Project-URL: Homepage, https://github.com/AnswerDotAI/pytest-asyncio-concurrent
32
+ Project-URL: Issues, https://github.com/AnswerDotAI/pytest-asyncio-concurrent/issues
33
+ Classifier: Framework :: Pytest
34
+ Classifier: Development Status :: 4 - Beta
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: Topic :: Software Development :: Testing
37
+ Classifier: Operating System :: OS Independent
38
+ Classifier: Programming Language :: Python
39
+ Classifier: Programming Language :: Python :: 3.8
40
+ Classifier: Programming Language :: Python :: 3.9
41
+ Classifier: Programming Language :: Python :: 3.10
42
+ Classifier: Programming Language :: Python :: 3.11
43
+ Classifier: Programming Language :: Python :: 3.12
44
+ Classifier: Programming Language :: Python :: 3.13
45
+ Classifier: Programming Language :: Python :: 3 :: Only
46
+ Classifier: License :: OSI Approved :: MIT License
47
+ Requires-Python: >=3.8
48
+ Description-Content-Type: text/x-rst
49
+ License-File: LICENSE
50
+ Requires-Dist: pytest>=6.2.0
51
+ Provides-Extra: testing
52
+ Requires-Dist: coverage>=7.6.0; extra == "testing"
53
+ Dynamic: license-file
54
+
55
+ ==============================
56
+ pytest-asyncio-concurrent-fork
57
+ ==============================
58
+
59
+ This is a temporary pytest compatibility fork of `pytest-asyncio-concurrent <https://github.com/czl9707/pytest-asyncio-concurrent>`_. The intent is to return to upstream once the fixes land there.
60
+
61
+ The fork registers concurrent tests as active during fixture setup and execution, as required by pytest 9.1. Async fixtures and concurrent tests share one session-owned event loop rather than relying on an implicit current loop.
62
+
63
+ .. image:: https://img.shields.io/pypi/v/pytest-asyncio-concurrent-fork.svg
64
+ :target: https://pypi.org/project/pytest-asyncio-concurrent-fork
65
+ :alt: PyPI version
66
+
67
+ .. image:: https://img.shields.io/pypi/pyversions/pytest-asyncio-concurrent-fork.svg
68
+ :target: https://pypi.org/project/pytest-asyncio-concurrent-fork
69
+ :alt: Python versions
70
+
71
+ .. image:: https://github.com/AnswerDotAI/pytest-asyncio-concurrent/actions/workflows/main.yml/badge.svg
72
+ :target: https://github.com/AnswerDotAI/pytest-asyncio-concurrent/actions/workflows/main.yml
73
+ :alt: See Build Status on GitHub Actions
74
+
75
+
76
+ System/Integration tests can take a really long time.
77
+
78
+ And ``pytest-asyncio-concurrent`` A pytest plugin aiming to solve this by running asynchronous tests in true parallel, enabling faster execution for high I/O or network-bound test suites.
79
+
80
+ Unlike ``pytest-asyncio``, which runs async tests **sequentially**, ``pytest-asyncio-concurrent`` takes advantage of Python's asyncio capabilities to execute tests **concurrently** by specifying **async group**.
81
+
82
+ Note: This plugin would more or less `Break Test Isolation Principle` \(for none function scoped fixture\). Please make sure your tests is ok to run concurrently before you use this plugin.
83
+
84
+
85
+ Key Features
86
+ ------------
87
+
88
+ * Giving the capability to run pytest async functions.
89
+ * Providing granular control over Concurrency -- See **Async Group** for details
90
+
91
+ * Specifying Async Group to control tests that can run together.
92
+ * Limitation: Only test functions defined under same direct parent can be put into same group.
93
+
94
+ * ``asyncio_concurrent`` mark accept a ``timeout`` parameter, which would throw error when test reach the given time.
95
+ * Compatible with ``pytest-asyncio``.
96
+
97
+ Key Concept: Async Group
98
+ ------------------------
99
+
100
+ The plugin control tests concurrency by putting them into different groups.
101
+
102
+ * Limitation: Each group can **only** contain tests under **same direct parent**. For example, class methods within same class, or functions within same file.
103
+ * Explicitly specify group by providing ``group`` parameter to ``asyncio_concurrent`` mark.
104
+
105
+ * All tests marked with same group name will be executed together.
106
+ * All tests will be skipped if the ``Same Parent`` rule got violated.
107
+
108
+ * Use default grouping strategy. The plugin accept ``--default-group-strategy`` cli parameter, or ``default_group_strategy`` in ini or toml file. Possible value: ``self``, ``parent``.
109
+
110
+ * **self**\(default\): Each test will be executed by itself if no group provided.
111
+ * **parent**: Tests will grouped by their parent node. For example, all method within same class will be grouped together.
112
+
113
+ Installation
114
+ ------------
115
+
116
+ Replace the upstream distribution with the fork; do not install both, since they provide the same Python package and pytest plugin::
117
+
118
+ $ pip uninstall pytest-asyncio-concurrent
119
+ $ pip install pytest-asyncio-concurrent-fork
120
+
121
+ The marker and plugin names are unchanged. With pytest 8.4 or later, projects can load only the plugins they use:
122
+
123
+ .. code-block:: toml
124
+
125
+ [tool.pytest.ini_options]
126
+ addopts = "--disable-plugin-autoload -p asyncio-concurrent"
127
+
128
+ Use plain ``@pytest.fixture`` for async fixtures owned by this plugin. If also using ``pytest-asyncio``, explicitly load ``-p asyncio`` and use its strict mode so it does not take ownership of concurrent tests.
129
+
130
+
131
+ How this work?
132
+ --------------
133
+
134
+ This plugin extend ``pytest_runtestloop`` hook to handle async tests seperately.
135
+
136
+ In the 'async loop', tests are grouped by the `group` provided in the mark. and executed one group at a time.
137
+
138
+ In each group, instead of sequentially calling ``setup``, ``call``, ``teardown`` for individual test, these hooks are called for all tests in the group in batch.
139
+
140
+ The fixture lifecycle within group is handled by this plugin instead of pytest core, to work around pytest sequential test execution assumption.
141
+
142
+
143
+ Usage
144
+ -----
145
+
146
+ Run test Sequentially
147
+
148
+ .. code-block:: python
149
+
150
+ @pytest.mark.asyncio_concurrent
151
+ async def async_test_A():
152
+ res = await wait_for_something_async()
153
+ assert result.is_valid()
154
+
155
+ @pytest.mark.asyncio_concurrent
156
+ async def async_test_B():
157
+ res = await wait_for_something_async()
158
+ assert result.is_valid()
159
+
160
+
161
+ Run tests Concurrently
162
+
163
+ .. code-block:: python
164
+
165
+ # the test below will run by itself
166
+ @pytest.mark.asyncio_concurrent
167
+ async def test_by_itself():
168
+ res = await wait_for_something_async()
169
+ assert result.is_valid()
170
+
171
+ # the two tests below will run concurrently
172
+ @pytest.mark.asyncio_concurrent(group="my_group")
173
+ async def test_groupA():
174
+ res = await wait_for_something_async()
175
+ assert result.is_valid()
176
+
177
+ # this one will have a 10s timeout
178
+ @pytest.mark.asyncio_concurrent(group="my_group", timeout=10)
179
+ async def test_groupB():
180
+ res = await wait_for_something_async()
181
+ assert result.is_valid()
182
+
183
+
184
+ Parametrized Tests
185
+
186
+ .. code-block:: python
187
+
188
+ # the parametrized tests below will run sequential
189
+ @pytest.mark.asyncio_concurrent
190
+ @pytest.parametrize("p", [0, 1, 2])
191
+ async def test_parametrize_sequential(p):
192
+ res = await wait_for_something_async()
193
+ assert result.is_valid()
194
+
195
+ # the parametrized tests below will run concurrently
196
+ @pytest.mark.asyncio_concurrent(group="my_group")
197
+ @pytest.parametrize("p", [0, 1, 2])
198
+ async def test_parametrize_concurrent():
199
+ res = await wait_for_something_async()
200
+ assert result.is_valid()
201
+
202
+
203
+ Contributing
204
+ ------------
205
+
206
+ Contributions are very welcome. Tests can be run with ``tox``, please ensure
207
+ the coverage at least stays the same before you submit a pull request.
208
+
209
+ License
210
+ -------
211
+
212
+ Distributed under the terms of the ``MIT`` license, "pytest-asyncio-concurrent" is free and open source software
@@ -0,0 +1,158 @@
1
+ ==============================
2
+ pytest-asyncio-concurrent-fork
3
+ ==============================
4
+
5
+ This is a temporary pytest compatibility fork of `pytest-asyncio-concurrent <https://github.com/czl9707/pytest-asyncio-concurrent>`_. The intent is to return to upstream once the fixes land there.
6
+
7
+ The fork registers concurrent tests as active during fixture setup and execution, as required by pytest 9.1. Async fixtures and concurrent tests share one session-owned event loop rather than relying on an implicit current loop.
8
+
9
+ .. image:: https://img.shields.io/pypi/v/pytest-asyncio-concurrent-fork.svg
10
+ :target: https://pypi.org/project/pytest-asyncio-concurrent-fork
11
+ :alt: PyPI version
12
+
13
+ .. image:: https://img.shields.io/pypi/pyversions/pytest-asyncio-concurrent-fork.svg
14
+ :target: https://pypi.org/project/pytest-asyncio-concurrent-fork
15
+ :alt: Python versions
16
+
17
+ .. image:: https://github.com/AnswerDotAI/pytest-asyncio-concurrent/actions/workflows/main.yml/badge.svg
18
+ :target: https://github.com/AnswerDotAI/pytest-asyncio-concurrent/actions/workflows/main.yml
19
+ :alt: See Build Status on GitHub Actions
20
+
21
+
22
+ System/Integration tests can take a really long time.
23
+
24
+ And ``pytest-asyncio-concurrent`` A pytest plugin aiming to solve this by running asynchronous tests in true parallel, enabling faster execution for high I/O or network-bound test suites.
25
+
26
+ Unlike ``pytest-asyncio``, which runs async tests **sequentially**, ``pytest-asyncio-concurrent`` takes advantage of Python's asyncio capabilities to execute tests **concurrently** by specifying **async group**.
27
+
28
+ Note: This plugin would more or less `Break Test Isolation Principle` \(for none function scoped fixture\). Please make sure your tests is ok to run concurrently before you use this plugin.
29
+
30
+
31
+ Key Features
32
+ ------------
33
+
34
+ * Giving the capability to run pytest async functions.
35
+ * Providing granular control over Concurrency -- See **Async Group** for details
36
+
37
+ * Specifying Async Group to control tests that can run together.
38
+ * Limitation: Only test functions defined under same direct parent can be put into same group.
39
+
40
+ * ``asyncio_concurrent`` mark accept a ``timeout`` parameter, which would throw error when test reach the given time.
41
+ * Compatible with ``pytest-asyncio``.
42
+
43
+ Key Concept: Async Group
44
+ ------------------------
45
+
46
+ The plugin control tests concurrency by putting them into different groups.
47
+
48
+ * Limitation: Each group can **only** contain tests under **same direct parent**. For example, class methods within same class, or functions within same file.
49
+ * Explicitly specify group by providing ``group`` parameter to ``asyncio_concurrent`` mark.
50
+
51
+ * All tests marked with same group name will be executed together.
52
+ * All tests will be skipped if the ``Same Parent`` rule got violated.
53
+
54
+ * Use default grouping strategy. The plugin accept ``--default-group-strategy`` cli parameter, or ``default_group_strategy`` in ini or toml file. Possible value: ``self``, ``parent``.
55
+
56
+ * **self**\(default\): Each test will be executed by itself if no group provided.
57
+ * **parent**: Tests will grouped by their parent node. For example, all method within same class will be grouped together.
58
+
59
+ Installation
60
+ ------------
61
+
62
+ Replace the upstream distribution with the fork; do not install both, since they provide the same Python package and pytest plugin::
63
+
64
+ $ pip uninstall pytest-asyncio-concurrent
65
+ $ pip install pytest-asyncio-concurrent-fork
66
+
67
+ The marker and plugin names are unchanged. With pytest 8.4 or later, projects can load only the plugins they use:
68
+
69
+ .. code-block:: toml
70
+
71
+ [tool.pytest.ini_options]
72
+ addopts = "--disable-plugin-autoload -p asyncio-concurrent"
73
+
74
+ Use plain ``@pytest.fixture`` for async fixtures owned by this plugin. If also using ``pytest-asyncio``, explicitly load ``-p asyncio`` and use its strict mode so it does not take ownership of concurrent tests.
75
+
76
+
77
+ How this work?
78
+ --------------
79
+
80
+ This plugin extend ``pytest_runtestloop`` hook to handle async tests seperately.
81
+
82
+ In the 'async loop', tests are grouped by the `group` provided in the mark. and executed one group at a time.
83
+
84
+ In each group, instead of sequentially calling ``setup``, ``call``, ``teardown`` for individual test, these hooks are called for all tests in the group in batch.
85
+
86
+ The fixture lifecycle within group is handled by this plugin instead of pytest core, to work around pytest sequential test execution assumption.
87
+
88
+
89
+ Usage
90
+ -----
91
+
92
+ Run test Sequentially
93
+
94
+ .. code-block:: python
95
+
96
+ @pytest.mark.asyncio_concurrent
97
+ async def async_test_A():
98
+ res = await wait_for_something_async()
99
+ assert result.is_valid()
100
+
101
+ @pytest.mark.asyncio_concurrent
102
+ async def async_test_B():
103
+ res = await wait_for_something_async()
104
+ assert result.is_valid()
105
+
106
+
107
+ Run tests Concurrently
108
+
109
+ .. code-block:: python
110
+
111
+ # the test below will run by itself
112
+ @pytest.mark.asyncio_concurrent
113
+ async def test_by_itself():
114
+ res = await wait_for_something_async()
115
+ assert result.is_valid()
116
+
117
+ # the two tests below will run concurrently
118
+ @pytest.mark.asyncio_concurrent(group="my_group")
119
+ async def test_groupA():
120
+ res = await wait_for_something_async()
121
+ assert result.is_valid()
122
+
123
+ # this one will have a 10s timeout
124
+ @pytest.mark.asyncio_concurrent(group="my_group", timeout=10)
125
+ async def test_groupB():
126
+ res = await wait_for_something_async()
127
+ assert result.is_valid()
128
+
129
+
130
+ Parametrized Tests
131
+
132
+ .. code-block:: python
133
+
134
+ # the parametrized tests below will run sequential
135
+ @pytest.mark.asyncio_concurrent
136
+ @pytest.parametrize("p", [0, 1, 2])
137
+ async def test_parametrize_sequential(p):
138
+ res = await wait_for_something_async()
139
+ assert result.is_valid()
140
+
141
+ # the parametrized tests below will run concurrently
142
+ @pytest.mark.asyncio_concurrent(group="my_group")
143
+ @pytest.parametrize("p", [0, 1, 2])
144
+ async def test_parametrize_concurrent():
145
+ res = await wait_for_something_async()
146
+ assert result.is_valid()
147
+
148
+
149
+ Contributing
150
+ ------------
151
+
152
+ Contributions are very welcome. Tests can be run with ``tox``, please ensure
153
+ the coverage at least stays the same before you submit a pull request.
154
+
155
+ License
156
+ -------
157
+
158
+ Distributed under the terms of the ``MIT`` license, "pytest-asyncio-concurrent" is free and open source software
@@ -0,0 +1,119 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=61.0",
4
+ ]
5
+ build-backend = "setuptools.build_meta"
6
+
7
+ [project]
8
+ name = "pytest-asyncio-concurrent-fork"
9
+ description = "Temporary pytest compatibility fork of pytest-asyncio-concurrent."
10
+ version = "0.5.2"
11
+ readme = "README.rst"
12
+ requires-python = ">=3.8"
13
+ authors = [
14
+ { name = "Zane Chen", email = "czl970721@gmail.com" },
15
+ ]
16
+ maintainers = [
17
+ { name = "Zane Chen", email = "czl970721@gmail.com" },
18
+ ]
19
+ license = {file = "LICENSE"}
20
+ classifiers = [
21
+ "Framework :: Pytest",
22
+ "Development Status :: 4 - Beta",
23
+ "Intended Audience :: Developers",
24
+ "Topic :: Software Development :: Testing",
25
+ "Operating System :: OS Independent",
26
+ "Programming Language :: Python",
27
+ "Programming Language :: Python :: 3.8",
28
+ "Programming Language :: Python :: 3.9",
29
+ "Programming Language :: Python :: 3.10",
30
+ "Programming Language :: Python :: 3.11",
31
+ "Programming Language :: Python :: 3.12",
32
+ "Programming Language :: Python :: 3.13",
33
+ "Programming Language :: Python :: 3 :: Only",
34
+ "License :: OSI Approved :: MIT License",
35
+ ]
36
+ dependencies = [
37
+ "pytest>=6.2.0",
38
+ ]
39
+ optional-dependencies.testing = [
40
+ "coverage>=7.6.0",
41
+ ]
42
+ [project.urls]
43
+ Repository = "https://github.com/AnswerDotAI/pytest-asyncio-concurrent"
44
+ Homepage = "https://github.com/AnswerDotAI/pytest-asyncio-concurrent"
45
+ Issues = "https://github.com/AnswerDotAI/pytest-asyncio-concurrent/issues"
46
+
47
+ [project.entry-points.pytest11]
48
+ asyncio-concurrent = "pytest_asyncio_concurrent.plugin"
49
+
50
+ [tool.setuptools]
51
+ packages = [
52
+ "pytest_asyncio_concurrent",
53
+ ]
54
+ include-package-data = true
55
+ license-files = [
56
+ "LICENSE",
57
+ ]
58
+
59
+ [tool.black]
60
+ line-length = 100
61
+
62
+ [tool.flake8]
63
+ max-line-length = 100
64
+
65
+ [tool.pytest.ini_options]
66
+ addopts = "--disable-plugin-autoload"
67
+
68
+ [tool.coverage.run]
69
+ source = [
70
+ "pytest_asyncio_concurrent",
71
+ ]
72
+ branch = true
73
+ data_file = "coverage/coverage"
74
+ omit = [
75
+ "*/_version.py",
76
+ ]
77
+ parallel = true
78
+ concurrency = [
79
+ "multiprocessing"
80
+ ]
81
+
82
+ [tool.coverage.report]
83
+ show_missing = true
84
+
85
+ [tool.bumpversion]
86
+ current_version = "0.5.2"
87
+ parse = """(?x)
88
+ (?P<major>0|[1-9]\\d*)\\.
89
+ (?P<minor>0|[1-9]\\d*)\\.
90
+ (?P<patch>0|[1-9]\\d*)
91
+ (?:
92
+ -prerelease-(?P<pre>0|[1-9]\\d*)
93
+ )?
94
+ """
95
+ serialize = [
96
+ "{major}.{minor}.{patch}-prerelease-{pre}",
97
+ "{major}.{minor}.{patch}",
98
+ ]
99
+ search = "{current_version}"
100
+ replace = "{new_version}"
101
+ regex = false
102
+ ignore_missing_version = false
103
+ ignore_missing_files = false
104
+ tag = false
105
+ sign_tags = false
106
+ tag_name = "v{new_version}"
107
+ tag_message = "Bump version: {current_version} → {new_version}"
108
+ allow_dirty = false
109
+ commit = true
110
+ message = "Bump version: {current_version} → {new_version}"
111
+ commit_args = ""
112
+ setup_hooks = []
113
+ pre_commit_hooks = []
114
+ post_commit_hooks = []
115
+
116
+ [[tool.bumpversion.files]]
117
+ filename = "pyproject.toml"
118
+ search = "version = \"{current_version}\""
119
+ replace = "version = \"{new_version}\""
@@ -0,0 +1,7 @@
1
+ """The main point for importing pytest-asyncio-concurrent items."""
2
+
3
+ from .plugin import AsyncioConcurrentGroup
4
+
5
+ __all__ = [
6
+ AsyncioConcurrentGroup.__name__,
7
+ ]
@@ -0,0 +1,139 @@
1
+ import copy
2
+ import inspect
3
+ import asyncio
4
+ import functools
5
+
6
+ from typing import Any, Dict, Optional, Sequence
7
+ import warnings
8
+
9
+ import pytest
10
+ from _pytest import fixtures
11
+ from _pytest import nodes
12
+
13
+ event_loop_key = pytest.StashKey[asyncio.AbstractEventLoop]()
14
+
15
+
16
+ @pytest.hookimpl(specname="pytest_fixture_setup", tryfirst=True)
17
+ def pytest_fixture_setup_wrap_async(
18
+ fixturedef: pytest.FixtureDef, request: pytest.FixtureRequest
19
+ ) -> None:
20
+ """Wraps the fixture function of an async fixture in a synchronous function."""
21
+ event_loop = request.config.stash[event_loop_key]
22
+ if inspect.isasyncgenfunction(fixturedef.func):
23
+ _wrap_asyncgen_fixture(fixturedef, event_loop)
24
+ elif inspect.iscoroutinefunction(fixturedef.func):
25
+ _wrap_asyncfunc_fixture(fixturedef, event_loop)
26
+
27
+
28
+ def _wrap_asyncgen_fixture(fixturedef: pytest.FixtureDef, event_loop) -> None:
29
+ fixtureFunc = fixturedef.func
30
+
31
+ @functools.wraps(fixtureFunc)
32
+ def _asyncgen_fixture_wrapper(**kwargs: Any):
33
+ gen_obj = fixtureFunc(**kwargs)
34
+
35
+ async def setup():
36
+ res = await gen_obj.__anext__() # type: ignore[union-attr]
37
+ return res
38
+
39
+ async def teardown() -> None:
40
+ try:
41
+ await gen_obj.__anext__() # type: ignore[union-attr]
42
+ except StopAsyncIteration:
43
+ pass
44
+ else:
45
+ msg = "Async generator fixture didn't stop."
46
+ msg += "Yield only once."
47
+ raise ValueError(msg)
48
+
49
+ result = event_loop.run_until_complete(setup())
50
+ yield result
51
+ event_loop.run_until_complete(teardown())
52
+
53
+ fixturedef.func = _asyncgen_fixture_wrapper # type: ignore[misc]
54
+
55
+
56
+ def _wrap_asyncfunc_fixture(fixturedef: pytest.FixtureDef, event_loop) -> None:
57
+ fixtureFunc = fixturedef.func
58
+
59
+ @functools.wraps(fixtureFunc)
60
+ def _async_fixture_wrapper(**kwargs: Dict[str, Any]):
61
+ async def setup():
62
+ res = await fixtureFunc(**kwargs)
63
+ return res
64
+
65
+ return event_loop.run_until_complete(setup())
66
+
67
+ fixturedef.func = _async_fixture_wrapper # type: ignore[misc]
68
+
69
+
70
+ fixture_cache_key = pytest.StashKey[Dict[str, Optional[Sequence[pytest.FixtureDef[Any]]]]]()
71
+
72
+
73
+ @pytest.hookimpl(specname="pytest_sessionstart", trylast=True)
74
+ def pytest_sessionstart_cache_fixture(session: pytest.Session):
75
+ event_loop = session.config.stash[event_loop_key] = asyncio.new_event_loop()
76
+ session.config.add_cleanup(event_loop.close)
77
+ # This function in general utilized some private properties.
78
+
79
+ # This is to solve two problem:
80
+ # 1. Funtion scoped fixture result value got shared in different tests in same group.
81
+ # 2. And fixture teardown got registered under right test using it.
82
+
83
+ # FixtureDef for each fixture is unique and held in FixtureManger and got injected into
84
+ # pytest.Item when the Item is constructed, and FixtureDef class is also in charge of
85
+ # holding finalizers and cache value.
86
+
87
+ # Fixture value caching is highly coupled with pytest entire lifecycle, implementing a
88
+ # thirdparty fixture cache manager will be hard.
89
+ # The first problem can be solved by shallow copy the fixtureDef, to split the cache_value.
90
+ # The finalizers are stored in a private list property in fixtureDef, which need to touch
91
+ # private API anyway.
92
+
93
+ # If the private API change, finalizer errors from this fixture but in different
94
+ # tests in same group will be reported in one function.
95
+
96
+ fixManager: fixtures.FixtureManager = session.config.pluginmanager.get_plugin(
97
+ "funcmanage"
98
+ ) # type: ignore
99
+ getfixturedefs_original = fixManager.getfixturedefs
100
+
101
+ @functools.wraps(getfixturedefs_original)
102
+ def getfixturedefs_wrapper(
103
+ argname: str,
104
+ node: nodes.Node,
105
+ ) -> Optional[Sequence[pytest.FixtureDef[Any]]]:
106
+ if fixture_cache_key not in node.stash:
107
+ node.stash[fixture_cache_key] = {}
108
+
109
+ cache = node.stash[fixture_cache_key]
110
+ if argname not in cache:
111
+ fixtureDefs = getfixturedefs_original(argname, node)
112
+
113
+ if fixtureDefs:
114
+ fixtureDefs = tuple(_clone_function_fixture(fixDef) for fixDef in fixtureDefs)
115
+
116
+ cache[argname] = fixtureDefs
117
+
118
+ return cache[argname]
119
+
120
+ fixManager.getfixturedefs = getfixturedefs_wrapper # type: ignore
121
+
122
+
123
+ def _clone_function_fixture(fixtureDef: pytest.FixtureDef) -> pytest.FixtureDef:
124
+ if fixtureDef.scope != "function":
125
+ return fixtureDef
126
+
127
+ new_fixdef = copy.copy(fixtureDef)
128
+ if hasattr(fixtureDef, "_finalizers"):
129
+ new_fixdef._finalizers = [] # type: ignore
130
+ else:
131
+ warnings.warn(
132
+ f"""
133
+ pytest {pytest.__version__} changed internal property which this plugin relies on.
134
+ The teardown error in fixture {fixtureDef.argname} might be reported in wrong place.
135
+ Please raise an issue.
136
+ """
137
+ )
138
+
139
+ return new_fixdef