Skip to content

Commit 9fb0880

Browse files
committed
Async fixtures in progress.
1 parent 150e913 commit 9fb0880

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

pytest_asyncio/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1+
"""The main point for importing pytest-asyncio items."""
12
__version__ = '0.5.0'
3+
4+
from .plugin import async_fixture

pytest_asyncio/plugin.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""pytest-asyncio implementation."""
22
import asyncio
3+
import functools
34
import inspect
45
import socket
56

@@ -8,9 +9,11 @@
89

910
import pytest
1011

12+
from _pytest.fixtures import FixtureFunctionMarker
1113
from _pytest.python import transfer_markers
1214

1315

16+
1417
class ForbiddenEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
1518
"""An event loop policy that raises errors on most operations.
1619
@@ -148,6 +151,7 @@ def unused_tcp_port_factory():
148151
produced = set()
149152

150153
def factory():
154+
"""Return an unused port."""
151155
port = unused_tcp_port()
152156

153157
while port in produced:
@@ -157,3 +161,34 @@ def factory():
157161

158162
return port
159163
return factory
164+
165+
166+
class AsyncFixtureFunctionMarker(FixtureFunctionMarker):
167+
168+
def __init__(self, *args, **kwargs):
169+
super().__init__(*args, **kwargs)
170+
171+
def __call__(self, coroutine):
172+
"""The parameter is the actual fixture coroutine."""
173+
if not _is_coroutine(coroutine):
174+
raise ValueError('Only coroutine functions supported')
175+
176+
@functools.wraps(coroutine)
177+
def inner(*args, **kwargs):
178+
loop = None
179+
return loop.run_until_complete(coroutine(*args, **kwargs))
180+
181+
inner._pytestfixturefunction = self
182+
return inner
183+
184+
185+
def async_fixture(scope='function', params=None, autouse=False, ids=None):
186+
if callable(scope) and params is None and not autouse:
187+
# direct invocation
188+
marker = AsyncFixtureFunctionMarker(
189+
'function', params, autouse)
190+
return marker(scope)
191+
if params is not None and not isinstance(params, (list, tuple)):
192+
params = list(params)
193+
return AsyncFixtureFunctionMarker(
194+
scope, params, autouse, ids=ids)

tests/test_async_fixtures.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Tests for async fixtures."""
2+
import asyncio
3+
4+
import pytest
5+
from pytest_asyncio import async_fixture
6+
7+
@pytest.fixture
8+
def shared_state():
9+
"""Some shared state, so we can assert the order of operations."""
10+
return {}
11+
12+
13+
@async_fixture
14+
def minimal_async_fixture(shared_state):
15+
"""A minimal asyncio fixture."""
16+
shared_state['async_fixture'] = 1
17+
yield from asyncio.sleep(0.01)
18+
shared_state['async_fixture'] += 1
19+
20+
21+
@pytest.mark.asyncio
22+
def test_minimal_asynx_fixture(shared_state, minimal_async_fixture):
23+
"""Test minimal async fixture working."""
24+
assert shared_state['async_fixture'] == 2
25+
yield from asyncio.sleep(0)

0 commit comments

Comments
 (0)