File: asyncio_test.py

package info (click to toggle)
python-eventlet 0.40.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,200 kB
  • sloc: python: 25,101; sh: 78; makefile: 31
file content (313 lines) | stat: -rw-r--r-- 7,777 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
"""Tests for asyncio integration."""

import pytest

import eventlet
from eventlet.hubs import get_hub
from eventlet.hubs.asyncio import Hub as AsyncioHub
if not isinstance(get_hub(), AsyncioHub):
    pytest.skip("Only works on asyncio hub", allow_module_level=True)

import asyncio
from time import time
import socket
import sys

from greenlet import GreenletExit

from eventlet.asyncio import spawn_for_awaitable
from eventlet.greenthread import getcurrent
from eventlet.support import greendns
from .wsgi_test import _TestBase, Site

import tests


class CallingAsyncFunctionsFromGreenletsHighLevelTests(_TestBase):
    """
    High-level tests for using ``asyncio``-based code inside greenlets.

    For this functionality to be useful, users need to be able to use 3rd party
    libraries that use sockets etc..  Merely hooking up futures to greenlets
    doesn't help if you can't use the asyncio library ecosystem.  So this set
    of tests does more integration-y tests showing that functionality works.
    """

    def set_site(self):
        self.site = Site()

    def test_aiohttp_client(self):
        """
        The ``aiohttp`` HTTP client works correctly on top of eventlet.
        """
        import aiohttp

        async def request():
            host, port = self.server_addr
            async with aiohttp.ClientSession() as session:
                url = "http://{}:{}/".format(host, port)
                async with session.get(url) as response:
                    html = await response.text()
                    return html

        gthread = spawn_for_awaitable(request())
        assert gthread.wait() == "hello world"


def test_result():
    """
    The result of the coroutine is returned by the ``GreenThread`` created by
    ``spawn_for_awaitable``.
    """

    async def go():
        await asyncio.sleep(0.0001)
        return 13

    assert spawn_for_awaitable(go()).wait() == 13


def test_exception():
    """
    An exception raised by the coroutine is raised by ``GreenThread.wait()``
    for the green thread created by ``spawn_for_awaitable()``.
    """

    async def go():
        await asyncio.sleep(0.0001)
        raise ZeroDivisionError()

    with pytest.raises(ZeroDivisionError):
        assert spawn_for_awaitable(go()).wait()


def test_future_and_task():
    """
    ``spawn_for_awaitable()`` can take an ``asyncio.Future`` or an
    ``asyncio.Task``.
    """

    async def go(value):
        return value * 2

    assert spawn_for_awaitable(asyncio.ensure_future(go(8))).wait() == 16
    assert spawn_for_awaitable(asyncio.create_task(go(6))).wait() == 12


def test_asyncio_sleep():
    """
    ``asyncio`` scheduled events work on eventlet.
    """

    async def go():
        start = time()
        await asyncio.sleep(0.07)
        return time() - start

    elapsed = spawn_for_awaitable(go()).wait()
    assert 0.05 < elapsed < 0.09


def test_kill_greenthread():
    """
    If a ``GreenThread`` wrapping an ``asyncio.Future``/coroutine is killed,
    the ``asyncio.Future`` is cancelled.
    """

    the_greenthread = []
    progress = []

    async def go():
        await asyncio.sleep(0.1)
        progress.append(1)
        while not the_greenthread:
            await asyncio.sleep(0.001)
        # Kill the green thread.
        progress.append(2)
        the_greenthread[0].kill()
        progress.append(3)
        await asyncio.sleep(1)
        # This should never be reached:
        progress.append(4)

    future = asyncio.ensure_future(go())
    the_greenthread.append(spawn_for_awaitable(future))
    with pytest.raises(GreenletExit):
        the_greenthread[0].wait()
    assert progress == [1, 2, 3]
    # Cancellation may not be immediate.
    eventlet.sleep(0.01)
    assert future.cancelled()
    assert progress == [1, 2, 3]


def test_await_greenthread_success():
    """
    ``await`` on a ``GreenThread`` returns its eventual result.
    """

    def greenlet():
        eventlet.sleep(0.001)
        return 23

    async def go():
        result = await eventlet.spawn(greenlet)
        return result

    assert spawn_for_awaitable(go()).wait() == 23


def test_await_greenthread_exception():
    """
    ``await`` on a ``GreenThread`` raises its eventual exception.
    """

    def greenlet():
        eventlet.sleep(0.001)
        return 1 / 0

    async def go():
        try:
            await eventlet.spawn(greenlet)
        except ZeroDivisionError as e:
            return e

    result = spawn_for_awaitable(go()).wait()
    assert isinstance(result, ZeroDivisionError)


def test_await_greenthread_success_immediate():
    """
    ``await`` on a ``GreenThread`` returns its immediate result.
    """

    def greenlet():
        return 23

    async def go():
        result = await eventlet.spawn(greenlet)
        return result

    assert spawn_for_awaitable(go()).wait() == 23


def test_await_greenthread_exception_immediate():
    """
    ``await`` on a ``GreenThread`` raises its immediate exception.
    """

    def greenlet():
        return 1 / 0

    async def go():
        try:
            await eventlet.spawn(greenlet)
        except ZeroDivisionError as e:
            return e

    result = spawn_for_awaitable(go()).wait()
    assert isinstance(result, ZeroDivisionError)


def test_ensure_future():
    """
    ``asyncio.ensure_future()`` works correctly on a ``GreenThread``.
    """

    def greenlet():
        eventlet.sleep(0.001)
        return 27

    async def go():
        future = asyncio.ensure_future(eventlet.spawn(greenlet))
        result = await future
        return result

    assert spawn_for_awaitable(go()).wait() == 27


def test_cancelling_future_kills_greenthread():
    """
    If the ``Future`` created by ``asyncio.ensure_future(a_green_thread)`` is
    cancelled, the ``a_green_thread`` ``GreenThread`` is killed.
    """

    phases = []

    def green():
        phases.append(1)
        future.cancel()
        eventlet.sleep(1)
        # This should never be reached:
        phases.append(2)

    gthread = eventlet.spawn(green)

    async def go():
        try:
            await gthread
        except asyncio.CancelledError:
            return "good"
        else:
            return "bad"

    future = asyncio.ensure_future(go())
    assert spawn_for_awaitable(future).wait() == "good"

    with pytest.raises(GreenletExit):
        gthread.wait()
    assert phases == [1]


def test_greenthread_killed_while_awaited():
    """
    If a ``GreenThread`` is killed, the ``async`` function ``await``ing it sees
    it as cancellation.
    """
    phases = []

    def green():
        phases.append(1)
        eventlet.sleep(0.001)
        phases.append(2)
        getcurrent().kill()
        eventlet.sleep(1)
        # Should never be reached:
        phases.append(3)

    gthread = eventlet.spawn(green)

    async def go():
        try:
            await gthread
            return "where is my cancellation?"
        except asyncio.CancelledError:
            return "canceled!"

    assert spawn_for_awaitable(go()).wait() == "canceled!"
    assert phases == [1, 2]


@pytest.mark.skipif(
    sys.version_info[:2] < (3, 9), reason="to_thread() is new Python 3.9"
)
def test_asyncio_to_thread():
    """
    ``asyncio.to_thread()`` works with Eventlet.
    """
    tests.run_isolated("asyncio_to_thread.py")


def test_asyncio_does_not_use_greendns():
    """
    ``asyncio`` loops' ``getaddrinfo()`` and ``getnameinfo()`` do not use green
    DNS.
    """
    tests.run_isolated("asyncio_dns.py")


def test_make_sure_monkey_patching_asyncio_is_restricted():
    """
    ``asyncio`` continues to have original, unpatched ``socket`` etc classes.
    """
    tests.run_isolated("asyncio_correct_patching.py")