-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Applies a default timeout on all returned futures. Fixes #43
- Loading branch information
Showing
6 changed files
with
109 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
"""Create futures with a default timeout.""" | ||
from concurrent.futures import Executor | ||
|
||
from more_executors.map import _MapFuture | ||
|
||
__pdoc__ = {} | ||
__pdoc__['TimeoutExecutor.map'] = None | ||
__pdoc__['TimeoutExecutor.shutdown'] = None | ||
__pdoc__['TimeoutExecutor.submit'] = None | ||
|
||
|
||
class _TimeoutFuture(_MapFuture): | ||
def __init__(self, delegate, timeout): | ||
super(_TimeoutFuture, self).__init__(delegate, lambda x: x) | ||
self._timeout = timeout | ||
|
||
def result(self, timeout=None): | ||
if timeout is None: | ||
timeout = self._timeout | ||
return super(_TimeoutFuture, self).result(timeout) | ||
|
||
def exception(self, timeout=None): | ||
if timeout is None: | ||
timeout = self._timeout | ||
return super(_TimeoutFuture, self).exception(timeout) | ||
|
||
|
||
class TimeoutExecutor(Executor): | ||
"""An `Executor` which delegates to another `Executor` while adding | ||
default timeouts to each returned future. | ||
Note that the default timeouts only apply to the `future.result()` and | ||
`future.exception()` methods. Other methods of waiting on futures, | ||
such as `concurrent.futures.wait()`, will not be affected. | ||
*Since version 1.6.0* | ||
""" | ||
def __init__(self, delegate, timeout): | ||
"""Create a new executor. | ||
- `delegate`: the delegate executor to which callables are submitted. | ||
- `timeout`: the default timeout applied to any calls to `future.result()` | ||
or `future.exception()`, where a timeout has not been provided. | ||
""" | ||
self._delegate = delegate | ||
self._timeout = timeout | ||
|
||
def submit(self, fn, *args, **kwargs): | ||
future = self._delegate.submit(fn, *args, **kwargs) | ||
return _TimeoutFuture(future, self._timeout) | ||
|
||
def shutdown(self, wait=True): | ||
self._delegate.shutdown(wait) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from concurrent.futures import TimeoutError | ||
from hamcrest import assert_that, equal_to, is_, calling, raises | ||
from pytest import fixture | ||
import time | ||
|
||
from more_executors._executors import Executors | ||
|
||
|
||
@fixture | ||
def executor(): | ||
return Executors.thread_pool().with_timeout(0.01) | ||
|
||
|
||
def test_basic_timeout(executor): | ||
def fn(sleep_time, retval): | ||
time.sleep(sleep_time) | ||
return retval | ||
|
||
f1 = executor.submit(fn, 1.0, 'abc') | ||
f2 = executor.submit(fn, 1.0, 'def') | ||
|
||
# Default should time out | ||
assert_that(calling(f1.result), raises(TimeoutError)) | ||
assert_that(calling(f2.exception), raises(TimeoutError)) | ||
|
||
# But specifying a value should make it work | ||
assert_that(f1.result(2.0), equal_to('abc')) | ||
assert_that(f1.exception(), is_(None)) | ||
|
||
assert_that(f2.exception(2.0), is_(None)) | ||
assert_that(f2.result(), equal_to('def')) |