summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Shepelev <temotor@gmail.com>2016-12-22 04:35:14 +0300
committerSergey Shepelev <temotor@gmail.com>2016-12-22 04:35:14 +0300
commit66ad70c5189310db5c5b8916908581b391463343 (patch)
tree23595394d6f68c79d3c268b3eddc5b85e5ec8847
parentf190122026463b28c587760c04b4d306ba61430e (diff)
downloadeventlet-error-is-timeout.tar.gz
-rw-r--r--eventlet/__init__.py2
-rw-r--r--eventlet/green/_socket_nodns.py16
-rw-r--r--eventlet/greenio/base.py24
-rw-r--r--eventlet/timeout.py22
-rw-r--r--tests/__init__.py5
-rw-r--r--tests/api_test.py36
-rw-r--r--tests/socket_test.py11
-rw-r--r--tests/timeout_test.py10
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())