Skip to content

Commit e7258d1

Browse files
committed
Add missing files
1 parent a12c932 commit e7258d1

File tree

3 files changed

+250
-0
lines changed

3 files changed

+250
-0
lines changed

MANIFEST.in

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
include LICENSE
2+
include CHANGES.rst
3+
include README.rst
4+
graft async_timeout
5+
graft tests
6+
global-exclude *.pyc
7+
global-exclude *.cache

async_timeout/__init__.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import asyncio
2+
3+
4+
__version__ = '1.0.0'
5+
6+
7+
class timeout:
8+
"""timeout context manager.
9+
10+
Useful in cases when you want to apply timeout logic around block
11+
of code or in cases when asyncio.wait_for is not suitable. For example:
12+
13+
>>> with timeout(0.001):
14+
... async with aiohttp.get('https://github.com') as r:
15+
... await r.text()
16+
17+
18+
timeout - value in seconds or None to disable timeout logic
19+
loop - asyncio compatible event loop
20+
"""
21+
def __init__(self, timeout, *, loop=None):
22+
self._timeout = timeout
23+
if loop is None:
24+
loop = asyncio.get_event_loop()
25+
self._loop = loop
26+
self._task = None
27+
self._cancelled = False
28+
self._cancel_handler = None
29+
30+
def __enter__(self):
31+
self._task = asyncio.Task.current_task(loop=self._loop)
32+
if self._task is None:
33+
raise RuntimeError('Timeout context manager should be used '
34+
'inside a task')
35+
if self._timeout is not None:
36+
self._cancel_handler = self._loop.call_later(
37+
self._timeout, self._cancel_task)
38+
return self
39+
40+
def __exit__(self, exc_type, exc_val, exc_tb):
41+
if exc_type is asyncio.CancelledError and self._cancelled:
42+
self._cancel_handler = None
43+
self._task = None
44+
raise asyncio.TimeoutError from None
45+
if self._timeout is not None:
46+
self._cancel_handler.cancel()
47+
self._cancel_handler = None
48+
self._task = None
49+
50+
def _cancel_task(self):
51+
self._cancelled = self._task.cancel()

tests/test_timeout.py

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import asyncio
2+
import os
3+
import time
4+
5+
import pytest
6+
7+
from async_timeout import timeout
8+
9+
try:
10+
from asyncio import ensure_future
11+
except ImportError:
12+
ensure_future = asyncio.async
13+
14+
15+
def create_future(loop):
16+
"""Compatibility wrapper for the loop.create_future() call introduced in
17+
3.5.2."""
18+
if hasattr(loop, 'create_future'):
19+
return loop.create_future()
20+
else:
21+
return asyncio.Future(loop=loop)
22+
23+
24+
@asyncio.coroutine
25+
def test_timeout(loop):
26+
canceled_raised = False
27+
28+
@asyncio.coroutine
29+
def long_running_task():
30+
try:
31+
yield from asyncio.sleep(10, loop=loop)
32+
except asyncio.CancelledError:
33+
nonlocal canceled_raised
34+
canceled_raised = True
35+
raise
36+
37+
with pytest.raises(asyncio.TimeoutError):
38+
with timeout(0.01, loop=loop) as t:
39+
yield from long_running_task()
40+
assert t._loop is loop
41+
assert canceled_raised, 'CancelledError was not raised'
42+
43+
44+
@asyncio.coroutine
45+
def test_timeout_finish_in_time(loop):
46+
@asyncio.coroutine
47+
def long_running_task():
48+
yield from asyncio.sleep(0.01, loop=loop)
49+
return 'done'
50+
51+
with timeout(0.1, loop=loop):
52+
resp = yield from long_running_task()
53+
assert resp == 'done'
54+
55+
56+
def test_timeout_global_loop(loop):
57+
asyncio.set_event_loop(loop)
58+
59+
@asyncio.coroutine
60+
def run():
61+
with timeout(10) as t:
62+
yield from asyncio.sleep(0.01)
63+
assert t._loop is loop
64+
65+
loop.run_until_complete(run())
66+
67+
68+
@asyncio.coroutine
69+
def test_timeout_disable(loop):
70+
@asyncio.coroutine
71+
def long_running_task():
72+
yield from asyncio.sleep(0.1, loop=loop)
73+
return 'done'
74+
75+
t0 = loop.time()
76+
with timeout(None, loop=loop):
77+
resp = yield from long_running_task()
78+
assert resp == 'done'
79+
dt = loop.time() - t0
80+
assert 0.09 < dt < 0.13, dt
81+
82+
83+
@asyncio.coroutine
84+
def test_timeout_not_relevant_exception(loop):
85+
yield from asyncio.sleep(0, loop=loop)
86+
with pytest.raises(KeyError):
87+
with timeout(0.1, loop=loop):
88+
raise KeyError
89+
90+
91+
@asyncio.coroutine
92+
def test_timeout_canceled_error_is_converted_to_timeout(loop):
93+
yield from asyncio.sleep(0, loop=loop)
94+
with pytest.raises(asyncio.CancelledError):
95+
with timeout(0.001, loop=loop):
96+
raise asyncio.CancelledError
97+
98+
99+
@asyncio.coroutine
100+
def test_timeout_blocking_loop(loop):
101+
@asyncio.coroutine
102+
def long_running_task():
103+
time.sleep(0.1)
104+
return 'done'
105+
106+
with timeout(0.01, loop=loop):
107+
result = yield from long_running_task()
108+
assert result == 'done'
109+
110+
111+
@asyncio.coroutine
112+
def test_for_race_conditions(loop):
113+
fut = create_future(loop)
114+
loop.call_later(0.1, fut.set_result('done'))
115+
with timeout(0.2, loop=loop):
116+
resp = yield from fut
117+
assert resp == 'done'
118+
119+
120+
@asyncio.coroutine
121+
def test_timeout_time(loop):
122+
foo_running = None
123+
124+
start = loop.time()
125+
with pytest.raises(asyncio.TimeoutError):
126+
with timeout(0.1, loop=loop):
127+
foo_running = True
128+
try:
129+
yield from asyncio.sleep(0.2, loop=loop)
130+
finally:
131+
foo_running = False
132+
133+
dt = loop.time() - start
134+
if not (0.09 < dt < 0.11) and os.environ.get('APPVEYOR'):
135+
pytest.xfail('appveyor sometimes is toooo sloooow')
136+
assert 0.09 < dt < 0.11
137+
assert not foo_running
138+
139+
140+
def test_raise_runtimeerror_if_no_task(loop):
141+
with pytest.raises(RuntimeError):
142+
with timeout(0.1, loop=loop):
143+
pass
144+
145+
146+
@asyncio.coroutine
147+
def test_outer_coro_is_not_cancelled(loop):
148+
149+
has_timeout = False
150+
151+
@asyncio.coroutine
152+
def outer():
153+
nonlocal has_timeout
154+
try:
155+
with timeout(0.001, loop=loop):
156+
yield from asyncio.sleep(1, loop=loop)
157+
except asyncio.TimeoutError:
158+
has_timeout = True
159+
160+
task = ensure_future(outer(), loop=loop)
161+
yield from task
162+
assert has_timeout
163+
assert not task.cancelled()
164+
assert task.done()
165+
166+
167+
@asyncio.coroutine
168+
def test_cancel_outer_coro(loop):
169+
fut = create_future(loop)
170+
171+
@asyncio.coroutine
172+
def outer():
173+
fut.set_result(None)
174+
yield from asyncio.sleep(1, loop=loop)
175+
176+
task = ensure_future(outer(), loop=loop)
177+
yield from fut
178+
task.cancel()
179+
with pytest.raises(asyncio.CancelledError):
180+
yield from task
181+
assert task.cancelled()
182+
assert task.done()
183+
184+
185+
@asyncio.coroutine
186+
def test_timeout_suppress_exception_chain(loop):
187+
188+
with pytest.raises(asyncio.TimeoutError) as ctx:
189+
with timeout(0.01, loop=loop) as t:
190+
yield from asyncio.sleep(10, loop=loop)
191+
assert t._loop is loop
192+
assert ctx.value.__suppress_context__

0 commit comments

Comments
 (0)