6
6
import socket
7
7
8
8
import pytest
9
+
9
10
try :
10
11
from _pytest .python import transfer_markers
11
12
except ImportError : # Pytest 4.1.0 removes the transfer_marker api (#104)
13
+
12
14
def transfer_markers (* args , ** kwargs ): # noqa
13
15
"""Noop when over pytest 4.1.0"""
14
16
pass
15
17
18
+
16
19
from inspect import isasyncgenfunction
17
20
18
21
@@ -23,10 +26,12 @@ def _is_coroutine(obj):
23
26
24
27
def pytest_configure (config ):
25
28
"""Inject documentation."""
26
- config .addinivalue_line ("markers" ,
27
- "asyncio: "
28
- "mark the test as a coroutine, it will be "
29
- "run using an asyncio event loop" )
29
+ config .addinivalue_line (
30
+ "markers" ,
31
+ "asyncio: "
32
+ "mark the test as a coroutine, it will be "
33
+ "run using an asyncio event loop" ,
34
+ )
30
35
31
36
32
37
@pytest .mark .tryfirst
@@ -41,12 +46,13 @@ def pytest_pycollect_makeitem(collector, name, obj):
41
46
transfer_markers (obj , item .cls , item .module )
42
47
item = pytest .Function .from_parent (collector , name = name ) # To reload keywords.
43
48
44
- if ' asyncio' in item .keywords :
49
+ if " asyncio" in item .keywords :
45
50
return list (collector ._genfunctions (name , obj ))
46
51
47
52
48
53
class FixtureStripper :
49
54
"""Include additional Fixture, and then strip them"""
55
+
50
56
REQUEST = "request"
51
57
EVENT_LOOP = "event_loop"
52
58
@@ -59,7 +65,7 @@ def add(self, name):
59
65
and record in to_strip list (If not previously included)"""
60
66
if name in self .fixturedef .argnames :
61
67
return
62
- self .fixturedef .argnames += (name , )
68
+ self .fixturedef .argnames += (name ,)
63
69
self .to_strip .add (name )
64
70
65
71
def get_and_strip_from (self , name , data_dict ):
@@ -69,6 +75,7 @@ def get_and_strip_from(self, name, data_dict):
69
75
del data_dict [name ]
70
76
return result
71
77
78
+
72
79
@pytest .hookimpl (trylast = True )
73
80
def pytest_fixture_post_finalizer (fixturedef , request ):
74
81
"""Called after fixture teardown"""
@@ -77,14 +84,16 @@ def pytest_fixture_post_finalizer(fixturedef, request):
77
84
asyncio .set_event_loop_policy (None )
78
85
79
86
80
-
81
87
@pytest .hookimpl (hookwrapper = True )
82
88
def pytest_fixture_setup (fixturedef , request ):
83
89
"""Adjust the event loop policy when an event loop is produced."""
84
90
if fixturedef .argname == "event_loop" :
85
91
outcome = yield
86
92
loop = outcome .get_result ()
87
93
policy = asyncio .get_event_loop_policy ()
94
+ old_loop = policy .get_event_loop ()
95
+ if old_loop is not loop :
96
+ old_loop .close ()
88
97
policy .set_event_loop (loop )
89
98
return
90
99
@@ -96,10 +105,13 @@ def pytest_fixture_setup(fixturedef, request):
96
105
fixture_stripper .add (FixtureStripper .EVENT_LOOP )
97
106
fixture_stripper .add (FixtureStripper .REQUEST )
98
107
99
-
100
108
def wrapper (* args , ** kwargs ):
101
- loop = fixture_stripper .get_and_strip_from (FixtureStripper .EVENT_LOOP , kwargs )
102
- request = fixture_stripper .get_and_strip_from (FixtureStripper .REQUEST , kwargs )
109
+ loop = fixture_stripper .get_and_strip_from (
110
+ FixtureStripper .EVENT_LOOP , kwargs
111
+ )
112
+ request = fixture_stripper .get_and_strip_from (
113
+ FixtureStripper .REQUEST , kwargs
114
+ )
103
115
104
116
gen_obj = generator (* args , ** kwargs )
105
117
@@ -109,6 +121,7 @@ async def setup():
109
121
110
122
def finalizer ():
111
123
"""Yield again, to finalize."""
124
+
112
125
async def async_finalizer ():
113
126
try :
114
127
await gen_obj .__anext__ ()
@@ -118,6 +131,7 @@ async def async_finalizer():
118
131
msg = "Async generator fixture didn't stop."
119
132
msg += "Yield only once."
120
133
raise ValueError (msg )
134
+
121
135
loop .run_until_complete (async_finalizer ())
122
136
123
137
request .addfinalizer (finalizer )
@@ -131,7 +145,9 @@ async def async_finalizer():
131
145
fixture_stripper .add (FixtureStripper .EVENT_LOOP )
132
146
133
147
def wrapper (* args , ** kwargs ):
134
- loop = fixture_stripper .get_and_strip_from (FixtureStripper .EVENT_LOOP , kwargs )
148
+ loop = fixture_stripper .get_and_strip_from (
149
+ FixtureStripper .EVENT_LOOP , kwargs
150
+ )
135
151
136
152
async def setup ():
137
153
res = await coro (* args , ** kwargs )
@@ -149,16 +165,15 @@ def pytest_pyfunc_call(pyfuncitem):
149
165
Run asyncio marked test functions in an event loop instead of a normal
150
166
function call.
151
167
"""
152
- if ' asyncio' in pyfuncitem .keywords :
153
- if getattr (pyfuncitem .obj , ' is_hypothesis_test' , False ):
168
+ if " asyncio" in pyfuncitem .keywords :
169
+ if getattr (pyfuncitem .obj , " is_hypothesis_test" , False ):
154
170
pyfuncitem .obj .hypothesis .inner_test = wrap_in_sync (
155
171
pyfuncitem .obj .hypothesis .inner_test ,
156
- _loop = pyfuncitem .funcargs [' event_loop' ]
172
+ _loop = pyfuncitem .funcargs [" event_loop" ],
157
173
)
158
174
else :
159
175
pyfuncitem .obj = wrap_in_sync (
160
- pyfuncitem .obj ,
161
- _loop = pyfuncitem .funcargs ['event_loop' ]
176
+ pyfuncitem .obj , _loop = pyfuncitem .funcargs ["event_loop" ]
162
177
)
163
178
yield
164
179
@@ -181,22 +196,25 @@ def inner(**kwargs):
181
196
if task .done () and not task .cancelled ():
182
197
task .exception ()
183
198
raise
199
+
184
200
return inner
185
201
186
202
187
203
def pytest_runtest_setup (item ):
188
- if ' asyncio' in item .keywords :
204
+ if " asyncio" in item .keywords :
189
205
# inject an event loop fixture for all async tests
190
- if 'event_loop' in item .fixturenames :
191
- item .fixturenames .remove ('event_loop' )
192
- item .fixturenames .insert (0 , 'event_loop' )
193
- if item .get_closest_marker ("asyncio" ) is not None \
194
- and not getattr (item .obj , 'hypothesis' , False ) \
195
- and getattr (item .obj , 'is_hypothesis_test' , False ):
196
- pytest .fail (
197
- 'test function `%r` is using Hypothesis, but pytest-asyncio '
198
- 'only works with Hypothesis 3.64.0 or later.' % item
199
- )
206
+ if "event_loop" in item .fixturenames :
207
+ item .fixturenames .remove ("event_loop" )
208
+ item .fixturenames .insert (0 , "event_loop" )
209
+ if (
210
+ item .get_closest_marker ("asyncio" ) is not None
211
+ and not getattr (item .obj , "hypothesis" , False )
212
+ and getattr (item .obj , "is_hypothesis_test" , False )
213
+ ):
214
+ pytest .fail (
215
+ "test function `%r` is using Hypothesis, but pytest-asyncio "
216
+ "only works with Hypothesis 3.64.0 or later." % item
217
+ )
200
218
201
219
202
220
@pytest .fixture
@@ -210,7 +228,7 @@ def event_loop(request):
210
228
def _unused_tcp_port ():
211
229
"""Find an unused localhost TCP port from 1024-65535 and return it."""
212
230
with contextlib .closing (socket .socket ()) as sock :
213
- sock .bind ((' 127.0.0.1' , 0 ))
231
+ sock .bind ((" 127.0.0.1" , 0 ))
214
232
return sock .getsockname ()[1 ]
215
233
216
234
@@ -219,7 +237,7 @@ def unused_tcp_port():
219
237
return _unused_tcp_port ()
220
238
221
239
222
- @pytest .fixture (scope = ' session' )
240
+ @pytest .fixture (scope = " session" )
223
241
def unused_tcp_port_factory ():
224
242
"""A factory function, producing different unused TCP ports."""
225
243
produced = set ()
@@ -234,4 +252,5 @@ def factory():
234
252
produced .add (port )
235
253
236
254
return port
255
+
237
256
return factory
0 commit comments