diff options
| author | Sergey Shepelev <temotor@gmail.com> | 2016-12-22 04:35:14 +0300 |
|---|---|---|
| committer | Sergey Shepelev <temotor@gmail.com> | 2016-12-22 04:35:14 +0300 |
| commit | 66ad70c5189310db5c5b8916908581b391463343 (patch) | |
| tree | 23595394d6f68c79d3c268b3eddc5b85e5ec8847 | |
| parent | f190122026463b28c587760c04b4d306ba61430e (diff) | |
| download | eventlet-error-is-timeout.tar.gz | |
| -rw-r--r-- | eventlet/__init__.py | 2 | ||||
| -rw-r--r-- | eventlet/green/_socket_nodns.py | 16 | ||||
| -rw-r--r-- | eventlet/greenio/base.py | 24 | ||||
| -rw-r--r-- | eventlet/timeout.py | 22 | ||||
| -rw-r--r-- | tests/__init__.py | 5 | ||||
| -rw-r--r-- | tests/api_test.py | 36 | ||||
| -rw-r--r-- | tests/socket_test.py | 11 | ||||
| -rw-r--r-- | tests/timeout_test.py | 10 |
8 files changed, 83 insertions, 43 deletions
diff --git a/eventlet/__init__.py b/eventlet/__init__.py index efbdbe6..929f0d9 100644 --- a/eventlet/__init__.py +++ b/eventlet/__init__.py @@ -23,6 +23,8 @@ if os.environ.get('EVENTLET_IMPORT_VERSION_ONLY') != '1': Timeout = timeout.Timeout with_timeout = timeout.with_timeout + wrap_is_timeout = timeout.wrap_is_timeout + is_timeout = timeout.is_timeout GreenPool = greenpool.GreenPool GreenPile = greenpool.GreenPile diff --git a/eventlet/green/_socket_nodns.py b/eventlet/green/_socket_nodns.py index 8bfecfd..7dca20a 100644 --- a/eventlet/green/_socket_nodns.py +++ b/eventlet/green/_socket_nodns.py @@ -1,17 +1,19 @@ __socket = __import__('socket') __all__ = __socket.__all__ -__patched__ = ['fromfd', 'socketpair', 'ssl', 'socket'] +__patched__ = ['fromfd', 'socketpair', 'ssl', 'socket', 'timeout'] -from eventlet.patcher import slurp_properties -slurp_properties(__socket, globals(), - ignore=__patched__, srckeys=dir(__socket)) +import eventlet.patcher +eventlet.patcher.slurp_properties(__socket, globals(), ignore=__patched__, srckeys=dir(__socket)) os = __import__('os') import sys -from eventlet.hubs import get_hub -from eventlet.greenio import GreenSocket as socket -from eventlet.greenio import _GLOBAL_DEFAULT_TIMEOUT +from eventlet import greenio + + +socket = greenio.GreenSocket +_GLOBAL_DEFAULT_TIMEOUT = greenio._GLOBAL_DEFAULT_TIMEOUT +timeout = greenio.socket_timeout try: __original_fromfd__ = __socket.fromfd diff --git a/eventlet/greenio/base.py b/eventlet/greenio/base.py index 7cb68c1..107eb86 100644 --- a/eventlet/greenio/base.py +++ b/eventlet/greenio/base.py @@ -13,6 +13,7 @@ __all__ = [ 'GreenSocket', '_GLOBAL_DEFAULT_TIMEOUT', 'set_nonblocking', 'SOCKET_BLOCKING', 'SOCKET_CLOSED', 'CONNECT_ERR', 'CONNECT_SUCCESS', 'shutdown_safe', 'SSL', + 'socket_timeout', ] BUFFER_SIZE = 4096 @@ -27,6 +28,11 @@ if six.PY2: _original_socket = eventlet.patcher.original('socket').socket +socket_timeout = eventlet.timeout.wrap_is_timeout(socket.timeout) +# Global timeout exception instance - less allocations. +_timeout_exc = socket_timeout('timed out') + + def socket_connect(descriptor, address): """ Attempts to connect to the address, returns the descriptor if it succeeds, @@ -216,8 +222,7 @@ class GreenSocket(object): client, addr = res set_nonblocking(client) return type(self)(client), addr - self._trampoline(fd, read=True, timeout=self.gettimeout(), - timeout_exc=socket.timeout("timed out")) + self._trampoline(fd, read=True, timeout=self.gettimeout(), timeout_exc=_timeout_exc) def _mark_as_closed(self): """ Mark this socket as being closed """ @@ -246,10 +251,10 @@ class GreenSocket(object): if socket_connect(fd, address): return if time.time() >= end: - raise socket.timeout("timed out") + raise _timeout_exc + timeout = end - time.time() try: - self._trampoline(fd, write=True, timeout=end - time.time(), - timeout_exc=socket.timeout("timed out")) + self._trampoline(fd, write=True, timeout=timeout, timeout_exc=_timeout_exc) except IOClosed: # ... we need some workable errno here. raise socket.error(errno.EBADFD) @@ -270,14 +275,15 @@ class GreenSocket(object): return errno.EBADFD else: end = time.time() + self.gettimeout() + timeout_exc = socket.timeout(errno.EAGAIN) while True: try: if socket_connect(fd, address): return 0 if time.time() >= end: - raise socket.timeout(errno.EAGAIN) + raise timeout_exc self._trampoline(fd, write=True, timeout=end - time.time(), - timeout_exc=socket.timeout(errno.EAGAIN)) + timeout_exc=timeout_exc) socket_checkerr(fd) except socket.error as ex: return get_errno(ex) @@ -316,7 +322,7 @@ class GreenSocket(object): self.fd, read=True, timeout=self.gettimeout(), - timeout_exc=socket.timeout("timed out")) + timeout_exc=_timeout_exc) def _recv_loop(self, recv_meth, empty_val, *args): fd = self.fd @@ -376,7 +382,7 @@ class GreenSocket(object): try: self._trampoline(self.fd, write=True, timeout=self.gettimeout(), - timeout_exc=socket.timeout("timed out")) + timeout_exc=_timeout_exc) except IOClosed: raise socket.error(errno.ECONNRESET, 'Connection closed by another thread') diff --git a/eventlet/timeout.py b/eventlet/timeout.py index f45c6d1..d109522 100644 --- a/eventlet/timeout.py +++ b/eventlet/timeout.py @@ -20,11 +20,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE.from eventlet.support import greenlets as greenlet +import functools + from eventlet.support import greenlets as greenlet from eventlet.hubs import get_hub -__all__ = ['Timeout', - 'with_timeout'] +__all__ = ['Timeout', 'with_timeout', 'wrap_is_timeout'] _NONE = object() @@ -128,6 +129,10 @@ class Timeout(BaseException): if value is self and self.exception is False: return True + @property + def is_timeout(self): + return True + def with_timeout(seconds, function, *args, **kwds): """Wrap a call to some (yielding) function with a timeout; if the called @@ -145,3 +150,16 @@ def with_timeout(seconds, function, *args, **kwds): raise finally: timeout.cancel() + + +def wrap_is_timeout(base): + @functools.wraps(base) + def wrapped(*args, **kwargs): + ex = base(*args, **kwargs) + ex.is_timeout = True + return ex + return wrapped + + +def is_timeout(obj): + return bool(getattr(obj, 'is_timeout', None)) diff --git a/tests/__init__.py b/tests/__init__.py index 6ff6d2a..15058e6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -346,6 +346,11 @@ def run_isolated(path, prefix='tests/isolated/', env=None, args=None, timeout=No assert ok, 'Expected single line "pass" in stdout' +def check_is_timeout(obj): + value_text = getattr(obj, 'is_timeout', '(missing)') + assert obj.is_timeout, 'type={0} str={1} .is_timeout={2}'.format(type(obj), str(obj), value_text) + + certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt') private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key') diff --git a/tests/api_test.py b/tests/api_test.py index 1cdab88..18ab9f5 100644 --- a/tests/api_test.py +++ b/tests/api_test.py @@ -1,12 +1,7 @@ -import os -from unittest import TestCase, main - -from nose.tools import eq_ - import eventlet -from eventlet import greenio, hubs, greenthread, spawn +from eventlet import greenio, hubs, greenthread from eventlet.green import ssl -from tests import skip_if_no_ssl +import tests def check_hub(): @@ -21,10 +16,7 @@ def check_hub(): assert not hub.running -class TestApi(TestCase): - - certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt') - private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key') +class TestApi(tests.LimitedTestCase): def test_tcp_listener(self): socket = eventlet.listen(('0.0.0.0', 0)) @@ -50,13 +42,13 @@ class TestApi(TestCase): client = eventlet.connect(('127.0.0.1', server.getsockname()[1])) fd = client.makefile('rb') client.close() - eq_(fd.readline(), b'hello\n') - eq_(fd.read(), b'') + assert fd.readline() == b'hello\n' + assert fd.read() == b'' fd.close() check_hub() - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_connect_ssl(self): def accept_once(listenfd): try: @@ -70,8 +62,8 @@ class TestApi(TestCase): server = eventlet.wrap_ssl( eventlet.listen(('0.0.0.0', 0)), - self.private_key_file, - self.certificate_file, + tests.private_key_file, + tests.certificate_file, server_side=True ) eventlet.spawn_n(accept_once, server) @@ -98,7 +90,7 @@ class TestApi(TestCase): def server(sock): client, addr = sock.accept() eventlet.sleep(0.1) - server_evt = spawn(server, server_sock) + server_evt = eventlet.spawn(server, server_sock) eventlet.sleep(0) try: desc = eventlet.connect(('127.0.0.1', bound_port)) @@ -179,9 +171,9 @@ class TestApi(TestCase): pass -class Foo(object): - pass - +def test_wrap_is_timeout(): + class A(object): + pass -if __name__ == '__main__': - main() + obj = eventlet.wrap_is_timeout(A)() + tests.check_is_timeout(obj) diff --git a/tests/socket_test.py b/tests/socket_test.py index 954eba3..9d725b5 100644 --- a/tests/socket_test.py +++ b/tests/socket_test.py @@ -91,3 +91,14 @@ def test_getaddrinfo_ipv6_scope(): if not socket.has_ipv6: return socket.getaddrinfo('::1%2', 80, socket.AF_INET6) + + +def test_error_is_timeout(): + s1, _ = socket.socketpair() + s1.settimeout(0.01) + try: + s1.recv(1) + except socket.error as e: + tests.check_is_timeout(e) + else: + assert False, 'No timeout, socket.error was not raised' diff --git a/tests/timeout_test.py b/tests/timeout_test.py index 0254b79..e33003f 100644 --- a/tests/timeout_test.py +++ b/tests/timeout_test.py @@ -1,12 +1,12 @@ import eventlet -from tests import LimitedTestCase +import tests DELAY = 0.01 -class TestDirectRaise(LimitedTestCase): +class TestDirectRaise(tests.LimitedTestCase): def test_direct_raise_class(self): try: raise eventlet.Timeout @@ -36,7 +36,7 @@ class TestDirectRaise(LimitedTestCase): str(tm) -class TestWithTimeout(LimitedTestCase): +class TestWithTimeout(tests.LimitedTestCase): def test_with_timeout(self): self.assertRaises(eventlet.Timeout, eventlet.with_timeout, DELAY, eventlet.sleep, DELAY * 10) X = object() @@ -53,3 +53,7 @@ class TestWithTimeout(LimitedTestCase): eventlet.Timeout, eventlet.with_timeout, DELAY, longer_timeout) + + +def test_is_timeout_attribute(): + tests.check_is_timeout(eventlet.Timeout()) |
