Skip to content

Commit 92962d1

Browse files
tjkusonseifertm
authored andcommitted
Add debug option
1 parent 07cb01f commit 92962d1

File tree

3 files changed

+246
-1
lines changed

3 files changed

+246
-1
lines changed

changelog.d/980.added.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
``--asyncio-debug`` CLI option and ``asyncio_debug`` configuration option to enable asyncio debug mode for the default event loop.

pytest_asyncio/plugin.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,24 @@ def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None
9191
metavar="MODE",
9292
help=ASYNCIO_MODE_HELP,
9393
)
94+
group.addoption(
95+
"--asyncio-debug",
96+
dest="asyncio_debug",
97+
action="store_true",
98+
default=None,
99+
help="enable asyncio debug mode for the default event loop",
100+
)
94101
parser.addini(
95102
"asyncio_mode",
96103
help="default value for --asyncio-mode",
97104
default="strict",
98105
)
106+
parser.addini(
107+
"asyncio_debug",
108+
help="enable asyncio debug mode for the default event loop",
109+
type="bool",
110+
default="false",
111+
)
99112
parser.addini(
100113
"asyncio_default_fixture_loop_scope",
101114
type="string",
@@ -195,6 +208,17 @@ def _get_asyncio_mode(config: Config) -> Mode:
195208
) from e
196209

197210

211+
def _get_asyncio_debug(config: Config) -> bool:
212+
val = config.getoption("asyncio_debug")
213+
if val is None:
214+
val = config.getini("asyncio_debug")
215+
216+
if isinstance(val, bool):
217+
return val
218+
else:
219+
return val == "true"
220+
221+
198222
_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET = """\
199223
The configuration option "asyncio_default_fixture_loop_scope" is unset.
200224
The event loop scope for asynchronous fixtures will default to the fixture caching \
@@ -221,10 +245,12 @@ def pytest_configure(config: Config) -> None:
221245
def pytest_report_header(config: Config) -> list[str]:
222246
"""Add asyncio config to pytest header."""
223247
mode = _get_asyncio_mode(config)
248+
debug = _get_asyncio_debug(config)
224249
default_fixture_loop_scope = config.getini("asyncio_default_fixture_loop_scope")
225250
default_test_loop_scope = _get_default_test_loop_scope(config)
226251
header = [
227252
f"mode={mode}",
253+
f"debug={debug}",
228254
f"asyncio_default_fixture_loop_scope={default_fixture_loop_scope}",
229255
f"asyncio_default_test_loop_scope={default_test_loop_scope}",
230256
]
@@ -751,10 +777,12 @@ def _create_scoped_runner_fixture(scope: _ScopeName) -> Callable:
751777
)
752778
def _scoped_runner(
753779
event_loop_policy,
780+
request: FixtureRequest,
754781
) -> Iterator[Runner]:
755782
new_loop_policy = event_loop_policy
783+
debug_mode = _get_asyncio_debug(request.config)
756784
with _temporary_event_loop_policy(new_loop_policy):
757-
runner = Runner().__enter__()
785+
runner = Runner(debug=debug_mode).__enter__()
758786
try:
759787
yield runner
760788
except Exception as e:

tests/test_asyncio_debug.py

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
from __future__ import annotations
2+
3+
from textwrap import dedent
4+
5+
import pytest
6+
from pytest import Pytester
7+
8+
9+
def test_asyncio_debug_disabled_by_default(pytester: Pytester):
10+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
11+
pytester.makepyfile(
12+
dedent(
13+
"""\
14+
import asyncio
15+
import pytest
16+
17+
pytest_plugins = "pytest_asyncio"
18+
19+
@pytest.mark.asyncio
20+
async def test_debug_mode_disabled():
21+
loop = asyncio.get_running_loop()
22+
assert not loop.get_debug()
23+
"""
24+
)
25+
)
26+
result = pytester.runpytest()
27+
result.assert_outcomes(passed=1)
28+
29+
30+
def test_asyncio_debug_enabled_via_cli_option(pytester: Pytester):
31+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
32+
pytester.makepyfile(
33+
dedent(
34+
"""\
35+
import asyncio
36+
import pytest
37+
38+
pytest_plugins = "pytest_asyncio"
39+
40+
@pytest.mark.asyncio
41+
async def test_debug_mode_enabled():
42+
loop = asyncio.get_running_loop()
43+
assert loop.get_debug()
44+
"""
45+
)
46+
)
47+
result = pytester.runpytest("--asyncio-debug")
48+
result.assert_outcomes(passed=1)
49+
50+
51+
@pytest.mark.parametrize("config_value", ("true", "1"))
52+
def test_asyncio_debug_enabled_via_config_option(pytester: Pytester, config_value: str):
53+
pytester.makeini(
54+
dedent(
55+
f"""\
56+
[pytest]
57+
asyncio_default_fixture_loop_scope = function
58+
asyncio_debug = {config_value}
59+
"""
60+
)
61+
)
62+
pytester.makepyfile(
63+
dedent(
64+
"""\
65+
import asyncio
66+
import pytest
67+
68+
pytest_plugins = "pytest_asyncio"
69+
70+
@pytest.mark.asyncio
71+
async def test_debug_mode_enabled():
72+
loop = asyncio.get_running_loop()
73+
assert loop.get_debug()
74+
"""
75+
)
76+
)
77+
result = pytester.runpytest()
78+
result.assert_outcomes(passed=1)
79+
80+
81+
@pytest.mark.parametrize("config_value", ("false", "0"))
82+
def test_asyncio_debug_disabled_via_config_option(
83+
pytester: Pytester,
84+
config_value: str,
85+
):
86+
pytester.makeini(
87+
dedent(
88+
f"""\
89+
[pytest]
90+
asyncio_default_fixture_loop_scope = function
91+
asyncio_debug = {config_value}
92+
"""
93+
)
94+
)
95+
pytester.makepyfile(
96+
dedent(
97+
"""\
98+
import asyncio
99+
import pytest
100+
101+
pytest_plugins = "pytest_asyncio"
102+
103+
@pytest.mark.asyncio
104+
async def test_debug_mode_disabled():
105+
loop = asyncio.get_running_loop()
106+
assert not loop.get_debug()
107+
"""
108+
)
109+
)
110+
result = pytester.runpytest()
111+
result.assert_outcomes(passed=1)
112+
113+
114+
def test_asyncio_debug_cli_option_overrides_config(pytester: Pytester):
115+
pytester.makeini(
116+
"[pytest]\nasyncio_default_fixture_loop_scope = function\nasyncio_debug = false"
117+
)
118+
pytester.makepyfile(
119+
dedent(
120+
"""\
121+
import asyncio
122+
import pytest
123+
124+
pytest_plugins = "pytest_asyncio"
125+
126+
@pytest.mark.asyncio
127+
async def test_debug_mode_enabled():
128+
loop = asyncio.get_running_loop()
129+
assert loop.get_debug()
130+
"""
131+
)
132+
)
133+
result = pytester.runpytest("--asyncio-debug")
134+
result.assert_outcomes(passed=1)
135+
136+
137+
@pytest.mark.parametrize("loop_scope", ("function", "module", "session"))
138+
def test_asyncio_debug_with_different_loop_scopes(pytester: Pytester, loop_scope: str):
139+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
140+
pytester.makepyfile(
141+
dedent(
142+
f"""\
143+
import asyncio
144+
import pytest
145+
146+
pytest_plugins = "pytest_asyncio"
147+
148+
@pytest.mark.asyncio(loop_scope="{loop_scope}")
149+
async def test_debug_mode_with_scope():
150+
loop = asyncio.get_running_loop()
151+
assert loop.get_debug()
152+
"""
153+
)
154+
)
155+
result = pytester.runpytest("--asyncio-debug")
156+
result.assert_outcomes(passed=1)
157+
158+
159+
def test_asyncio_debug_with_async_fixtures(pytester: Pytester):
160+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
161+
pytester.makepyfile(
162+
dedent(
163+
"""\
164+
import asyncio
165+
import pytest
166+
import pytest_asyncio
167+
168+
pytest_plugins = "pytest_asyncio"
169+
170+
@pytest_asyncio.fixture
171+
async def async_fixture():
172+
loop = asyncio.get_running_loop()
173+
assert loop.get_debug()
174+
return "fixture_value"
175+
176+
@pytest.mark.asyncio
177+
async def test_debug_mode_with_fixture(async_fixture):
178+
loop = asyncio.get_running_loop()
179+
assert loop.get_debug()
180+
assert async_fixture == "fixture_value"
181+
"""
182+
)
183+
)
184+
result = pytester.runpytest("--asyncio-debug")
185+
result.assert_outcomes(passed=1)
186+
187+
188+
def test_asyncio_debug_multiple_test_functions(pytester: Pytester):
189+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
190+
pytester.makepyfile(
191+
dedent(
192+
"""\
193+
import asyncio
194+
import pytest
195+
196+
pytest_plugins = "pytest_asyncio"
197+
198+
@pytest.mark.asyncio
199+
async def test_debug_first():
200+
loop = asyncio.get_running_loop()
201+
assert loop.get_debug()
202+
203+
@pytest.mark.asyncio
204+
async def test_debug_second():
205+
loop = asyncio.get_running_loop()
206+
assert loop.get_debug()
207+
208+
@pytest.mark.asyncio
209+
async def test_debug_third():
210+
loop = asyncio.get_running_loop()
211+
assert loop.get_debug()
212+
"""
213+
)
214+
)
215+
result = pytester.runpytest("--asyncio-debug")
216+
result.assert_outcomes(passed=3)

0 commit comments

Comments
 (0)