Skip to content

Commit 39b5c0e

Browse files
authored
Async context manager (#17)
* Implement async context manager support * Fix tests * Update CHANGES
1 parent 9806b48 commit 39b5c0e

File tree

5 files changed

+48
-6
lines changed

5 files changed

+48
-6
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ CHANGES
99

1010
* Introduce `.timeout` property (#16)
1111

12+
* Add methods for using as async context manager (#9)
13+
1214
1.2.1 (2017-05-02)
1315
------------------
1416

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ because ``timeout`` doesn't create a new task.
1616
The ``timeout(timeout, *, loop=None)`` call returns a context manager
1717
that cancels a block on *timeout* expiring::
1818

19-
with timeout(1.5):
19+
async with timeout(1.5):
2020
await inner()
2121

2222
1. If ``inner()`` is executed faster than in ``1.5`` seconds nothing
@@ -31,7 +31,7 @@ that cancels a block on *timeout* expiring::
3131
Context manager has ``.expired`` property for check if timeout happens
3232
exactly in context manager::
3333

34-
with timeout(1.5) as cm:
34+
async with timeout(1.5) as cm:
3535
await inner()
3636
print(cm.expired)
3737

async_timeout/__init__.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ def __init__(self, timeout, *, loop=None):
3030
self._cancel_handler = None
3131

3232
def __enter__(self):
33+
return self._do_enter()
34+
35+
def __exit__(self, exc_type, exc_val, exc_tb):
36+
self._do_exit(exc_type)
37+
38+
@asyncio.coroutine
39+
def __aenter__(self):
40+
return self._do_enter()
41+
42+
@asyncio.coroutine
43+
def __aexit__(self, exc_type, exc_val, exc_tb):
44+
self._do_exit(exc_type)
45+
46+
@property
47+
def expired(self):
48+
return self._cancelled
49+
50+
def _do_enter(self):
3351
if self._timeout is not None:
3452
self._task = current_task(self._loop)
3553
if self._task is None:
@@ -39,7 +57,7 @@ def __enter__(self):
3957
self._timeout, self._cancel_task)
4058
return self
4159

42-
def __exit__(self, exc_type, exc_val, exc_tb):
60+
def _do_exit(self, exc_type):
4361
if exc_type is asyncio.CancelledError and self._cancelled:
4462
self._cancel_handler = None
4563
self._task = None
@@ -53,9 +71,6 @@ def _cancel_task(self):
5371
self._task.cancel()
5472
self._cancelled = True
5573

56-
@property
57-
def expired(self):
58-
return self._cancelled
5974

6075

6176
def current_task(loop):

tests/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import sys
2+
3+
4+
def pytest_ignore_collect(path, config):
5+
if 'py35' in str(path):
6+
if sys.version_info < (3, 5, 0):
7+
return True

tests/test_py35.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import asyncio
2+
3+
import pytest
4+
5+
from async_timeout import timeout
6+
7+
8+
async def test_async_timeout(loop):
9+
with pytest.raises(asyncio.TimeoutError):
10+
async with timeout(0.01, loop=loop) as cm:
11+
await asyncio.sleep(10, loop=loop)
12+
assert cm.expired
13+
14+
15+
async def test_async_no_timeout(loop):
16+
async with timeout(1, loop=loop) as cm:
17+
await asyncio.sleep(0, loop=loop)
18+
assert not cm.expired

0 commit comments

Comments
 (0)