diff options
| author | Sergey Shepelev <temotor@gmail.com> | 2016-12-22 04:35:14 +0300 |
|---|---|---|
| committer | Sergey Shepelev <temotor@gmail.com> | 2017-01-04 23:42:32 +0300 |
| commit | e4049a8a0016b28cbbdf3ab01425fc92a623e380 (patch) | |
| tree | 58d140b6e9f784e5b28a0435fb41ae8f238984af | |
| parent | 461348f9a8459724be87c281eea4e0408138a82c (diff) | |
| download | eventlet-is-timeout.tar.gz | |
WIP https://github.com/eventlet/eventlet/pull/346is-timeout
| -rw-r--r-- | .travis.yml | 2 | ||||
| -rw-r--r-- | codecov.yml | 4 | ||||
| -rw-r--r-- | eventlet/__init__.py | 12 | ||||
| -rw-r--r-- | eventlet/green/_socket_nodns.py | 16 | ||||
| -rw-r--r-- | eventlet/greenio/base.py | 27 | ||||
| -rw-r--r-- | eventlet/greenthread.py | 8 | ||||
| -rw-r--r-- | eventlet/hubs/__init__.py | 1 | ||||
| -rw-r--r-- | eventlet/support/__init__.py | 38 | ||||
| -rw-r--r-- | eventlet/timeout.py | 41 | ||||
| -rw-r--r-- | tests/__init__.py | 7 | ||||
| -rw-r--r-- | tests/api_test.py | 44 | ||||
| -rw-r--r-- | tests/fork_test.py | 52 | ||||
| -rw-r--r-- | tests/greenio_test.py | 2 | ||||
| -rw-r--r-- | tests/hub_test.py | 63 | ||||
| -rw-r--r-- | tests/hub_test_fork.py | 25 | ||||
| -rw-r--r-- | tests/isolated/hub_fork.py | 33 | ||||
| -rw-r--r-- | tests/isolated/hub_fork_simple.py | 58 | ||||
| -rw-r--r-- | tests/socket_test.py | 11 | ||||
| -rw-r--r-- | tests/test__refcount.py | 65 | ||||
| -rw-r--r-- | tests/test__socket_errors.py | 2 | ||||
| -rw-r--r-- | tests/timeout_test.py | 10 | ||||
| -rw-r--r-- | tests/tpool_test.py | 15 | ||||
| -rw-r--r-- | tox.ini | 5 |
23 files changed, 327 insertions, 214 deletions
diff --git a/.travis.yml b/.travis.yml index 15f5459..3fb1915 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,7 +51,7 @@ before_script: - "export PATH=/usr/lib/ccache:$PATH" script: - tox -v -v -e $TOX_ENV - - codecov + - codecov --flags $TOX_ENV after_failure: - for X in .tox/$TOX_ENV/log/*; do echo "$X\n"; cat "$X"; echo "\n\n"; done - echo "pip.log\n"; cat $HOME/.pip/pip.log diff --git a/codecov.yml b/codecov.yml index 676557d..6e9350e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,6 @@ codecov: token: 2a926756-1923-42a1-89c7-97925ea0e17a + +coverage: + precision: 0 + round: down diff --git a/eventlet/__init__.py b/eventlet/__init__.py index 4700261..0f57a1d 100644 --- a/eventlet/__init__.py +++ b/eventlet/__init__.py @@ -14,6 +14,7 @@ if os.environ.get('EVENTLET_IMPORT_VERSION_ONLY') != '1': from eventlet import patcher from eventlet import queue from eventlet import semaphore + from eventlet import support from eventlet import timeout import greenlet @@ -45,10 +46,15 @@ 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 getcurrent = greenlet.greenlet.getcurrent # deprecated - TimeoutError = timeout.Timeout - exc_after = greenthread.exc_after - call_after_global = greenthread.call_after_global + TimeoutError, exc_after, call_after_global = ( + support.wrap_deprecated(old, new)(fun) for old, new, fun in ( + ('TimeoutError', 'Timeout', Timeout), + ('exc_after', 'greenthread.exc_after', greenthread.exc_after), + ('call_after_global', 'greenthread.call_after_global', greenthread.call_after_global), + )) 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..042f7d8 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,9 @@ if six.PY2: _original_socket = eventlet.patcher.original('socket').socket +socket_timeout = eventlet.timeout.wrap_is_timeout(socket.timeout) + + def socket_connect(descriptor, address): """ Attempts to connect to the address, returns the descriptor if it succeeds, @@ -210,14 +214,14 @@ class GreenSocket(object): if self.act_non_blocking: return self.fd.accept() fd = self.fd + _timeout_exc = socket_timeout('timed out') while True: res = socket_accept(fd) if res is not None: 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 """ @@ -233,6 +237,7 @@ class GreenSocket(object): if self.act_non_blocking: return self.fd.connect(address) fd = self.fd + _timeout_exc = socket_timeout('timed out') if self.gettimeout() is None: while not socket_connect(fd, address): try: @@ -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=socket_timeout('timed out')) def _recv_loop(self, recv_meth, empty_val, *args): fd = self.fd @@ -366,7 +372,8 @@ class GreenSocket(object): if self.act_non_blocking: return send_method(data, *args) - while 1: + _timeout_exc = socket_timeout('timed out') + while True: try: return send_method(data, *args) except socket.error as e: @@ -376,7 +383,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/greenthread.py b/eventlet/greenthread.py index 921d7a9..c0f2982 100644 --- a/eventlet/greenthread.py +++ b/eventlet/greenthread.py @@ -3,6 +3,7 @@ import sys from eventlet import event from eventlet import hubs +from eventlet import support from eventlet import timeout from eventlet.hubs import timer from eventlet.support import greenlets as greenlet, six @@ -144,8 +145,11 @@ def exc_after(seconds, *throw_args): return hub.schedule_call_local(seconds, getcurrent().throw, *throw_args) # deprecate, remove -TimeoutError = timeout.Timeout -with_timeout = timeout.with_timeout +TimeoutError, with_timeout = ( + support.wrap_deprecated(old, new)(fun) for old, new, fun in ( + ('greenthread.TimeoutError', 'Timeout', timeout.Timeout), + ('greenthread.with_timeout', 'with_timeout', timeout.with_timeout), + )) def _spawn_n(seconds, func, args, kwargs): diff --git a/eventlet/hubs/__init__.py b/eventlet/hubs/__init__.py index 9f72c16..879ec9a 100644 --- a/eventlet/hubs/__init__.py +++ b/eventlet/hubs/__init__.py @@ -118,6 +118,7 @@ def get_hub(): return hub +# Lame middle file import because complex dependencies in import graph from eventlet import timeout diff --git a/eventlet/support/__init__.py b/eventlet/support/__init__.py index 4c2b75d..b311c8a 100644 --- a/eventlet/support/__init__.py +++ b/eventlet/support/__init__.py @@ -1,9 +1,15 @@ +import inspect +import functools import sys -from contextlib import contextmanager +import warnings +import contextlib from eventlet.support import greenlets, six +_MISSING = object() + + def get_errno(exc): """ Get the error code out of socket.error objects. socket.error in <2.5 does not have errno attribute @@ -43,7 +49,8 @@ else: PY33 = sys.version_info[:2] == (3, 3) -@contextmanager + +@contextlib.contextmanager def capture_stderr(): stream = six.StringIO() original = sys.stderr @@ -53,3 +60,30 @@ def capture_stderr(): finally: sys.stderr = original stream.seek(0) + + +def wrap_deprecated(old, new): + def _resolve(s): + return 'eventlet.'+s if '.' not in s else s + msg = '''\ +{old} is deprecated and will be removed in next version. Use {new} instead. +Autoupgrade: fgrep -rl '{old}' . |xargs -t sed --in-place='' -e 's/{old}/{new}/' +'''.format(old=_resolve(old), new=_resolve(new)) + + def wrapper(base): + klass = None + if inspect.isclass(base): + klass = base + base = klass.__init__ + + @functools.wraps(base) + def wrapped(*a, **kw): + warnings.warn(msg, DeprecationWarning, stacklevel=5) + return base(*a, **kw) + + if klass is not None: + klass.__init__ = wrapped + return klass + + return wrapped + return wrapper diff --git a/eventlet/timeout.py b/eventlet/timeout.py index f45c6d1..f431871 100644 --- a/eventlet/timeout.py +++ b/eventlet/timeout.py @@ -20,13 +20,16 @@ # 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 +import inspect + +import eventlet from eventlet.support import greenlets as greenlet from eventlet.hubs import get_hub -__all__ = ['Timeout', - 'with_timeout'] +__all__ = ['Timeout', 'with_timeout', 'wrap_is_timeout', 'is_timeout'] -_NONE = object() +_MISSING = object() # deriving from BaseException so that "except Exception as e" doesn't catch # Timeout exceptions. @@ -128,20 +131,48 @@ 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 function fails to return before the timeout, cancel it and return a flag value. """ - timeout_value = kwds.pop("timeout_value", _NONE) + timeout_value = kwds.pop("timeout_value", _MISSING) timeout = Timeout(seconds) try: try: return function(*args, **kwds) except Timeout as ex: - if ex is timeout and timeout_value is not _NONE: + if ex is timeout and timeout_value is not _MISSING: return timeout_value raise finally: timeout.cancel() + + +def wrap_is_timeout(base): + '''Adds `.is_timeout=True` attribute to objects returned by `base()`. + + When `base` is class, attribute is added as read-only property. Returns `base`. + Otherwise, it returns a function that sets attribute on result of `base()` call. + + Wrappers make best effort to be transparent. + ''' + if inspect.isclass(base): + base.is_timeout = property(lambda _: True) + return base + + @functools.wraps(base) + def fun(*args, **kwargs): + ex = base(*args, **kwargs) + ex.is_timeout = True + return ex + return fun + + +def is_timeout(obj): + return bool(getattr(obj, 'is_timeout', False)) diff --git a/tests/__init__.py b/tests/__init__.py index 1c7a5b3..7b6bd09 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -304,6 +304,8 @@ def get_database_auth(): def run_python(path, env=None, args=None, timeout=None, pythonpath_extend=None, expect_pass=False): new_argv = [sys.executable] + if sys.version[:2] <= (2, 6): + new_argv += ['-W', 'ignore::DeprecationWarning'] new_env = os.environ.copy() new_env.setdefault('eventlet_test_in_progress', 'yes') src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -357,6 +359,11 @@ def run_isolated(path, prefix='tests/isolated/', **kwargs): run_python(prefix + path, **kwargs) +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..2b2e93f 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,12 +90,12 @@ 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)) hubs.trampoline(desc, read=True, write=False, timeout=0.001) - except eventlet.TimeoutError: + except eventlet.Timeout: pass # test passed else: assert False, "Didn't timeout" @@ -126,7 +118,7 @@ class TestApi(TestCase): desc = eventlet.connect(('127.0.0.1', bound_port)) try: hubs.trampoline(desc, read=True, timeout=0.1) - except eventlet.TimeoutError: + except eventlet.Timeout: assert False, "Timed out" server.close() @@ -174,14 +166,14 @@ class TestApi(TestCase): try: eventlet.with_timeout(0.1, func) - self.fail(u'Expected TimeoutError') - except eventlet.TimeoutError: + self.fail(u'Expected Timeout') + except eventlet.Timeout: 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/fork_test.py b/tests/fork_test.py deleted file mode 100644 index f15883f..0000000 --- a/tests/fork_test.py +++ /dev/null @@ -1,52 +0,0 @@ -from tests.patcher_test import ProcessBase - - -class ForkTest(ProcessBase): - def test_simple(self): - newmod = ''' -import eventlet -import os -import sys -import signal -from eventlet.support import bytes_to_str, six -mydir = %r -signal_file = os.path.join(mydir, "output.txt") -pid = os.fork() -if (pid != 0): - eventlet.Timeout(10) - try: - port = None - while True: - try: - contents = open(signal_file, "rb").read() - port = int(contents.split()[0]) - break - except (IOError, IndexError, ValueError, TypeError): - eventlet.sleep(0.1) - eventlet.connect(('127.0.0.1', port)) - while True: - try: - contents = open(signal_file, "rb").read() - result = contents.split()[1] - break - except (IOError, IndexError): - eventlet.sleep(0.1) - print('result {0}'.format(bytes_to_str(result))) - finally: - os.kill(pid, signal.SIGTERM) -else: - try: - s = eventlet.listen(('', 0)) - fd = open(signal_file, "wb") - fd.write(six.b(str(s.getsockname()[1]))) - fd.write(b"\\n") - fd.flush() - s.accept() - fd.write(b"done") - fd.flush() - finally: - fd.close() -''' - self.write_to_tempfile("newmod", newmod % self.tempdir) - output, lines = self.launch_subprocess('newmod.py') - self.assertEqual(lines[0], "result done", output) diff --git a/tests/greenio_test.py b/tests/greenio_test.py index 619ccd0..6d01857 100644 --- a/tests/greenio_test.py +++ b/tests/greenio_test.py @@ -443,7 +443,7 @@ class TestGreenSocket(tests.LimitedTestCase): wrap_rfile = client.makefile() wrap_rfile.read(1) self.fail() - except eventlet.TimeoutError: + except eventlet.Timeout: pass result = evt.wait() diff --git a/tests/hub_test.py b/tests/hub_test.py index fc0ce6f..65ba0a9 100644 --- a/tests/hub_test.py +++ b/tests/hub_test.py @@ -1,14 +1,12 @@ from __future__ import with_statement import sys +import time import tests -from tests import LimitedTestCase, main, skip_with_pyevent, skip_if_no_itimer, skip_unless +from tests import skip_with_pyevent, skip_if_no_itimer, skip_unless from tests.patcher_test import ProcessBase -import time import eventlet from eventlet import hubs -from eventlet.event import Event -from eventlet.semaphore import Semaphore from eventlet.support import greenlets, six @@ -19,7 +17,7 @@ def noop(): pass -class TestTimerCleanup(LimitedTestCase): +class TestTimerCleanup(tests.LimitedTestCase): TEST_TIMEOUT = 2 @skip_with_pyevent @@ -85,7 +83,7 @@ class TestTimerCleanup(LimitedTestCase): eventlet.sleep() -class TestScheduleCall(LimitedTestCase): +class TestScheduleCall(tests.LimitedTestCase): def test_local(self): lst = [1] @@ -111,7 +109,7 @@ class TestScheduleCall(LimitedTestCase): self.assertEqual(lst, [1, 2, 3]) -class TestDebug(LimitedTestCase): +class TestDebug(tests.LimitedTestCase): def test_debug_listeners(self): hubs.get_hub().set_debug_listeners(True) @@ -122,7 +120,7 @@ class TestDebug(LimitedTestCase): hubs.get_hub().set_timer_exceptions(False) -class TestExceptionInMainloop(LimitedTestCase): +class TestExceptionInMainloop(tests.LimitedTestCase): def test_sleep(self): # even if there was an error in the mainloop, the hub should continue @@ -149,13 +147,13 @@ class TestExceptionInMainloop(LimitedTestCase): delay, DELAY) -class TestExceptionInGreenthread(LimitedTestCase): +class TestExceptionInGreenthread(tests.LimitedTestCase): @skip_unless(greenlets.preserves_excinfo) def test_exceptionpreservation(self): # events for controlling execution order - gt1event = Event() - gt2event = Event() + gt1event = eventlet.Event() + gt2event = eventlet.Event() def test_gt1(): try: @@ -196,7 +194,7 @@ class TestExceptionInGreenthread(LimitedTestCase): hubs.get_hub().switch() # semaphores for controlling execution order - sem = Semaphore() + sem = eventlet.Semaphore() sem.acquire() g = eventlet.spawn(test_gt, sem) try: @@ -206,7 +204,7 @@ class TestExceptionInGreenthread(LimitedTestCase): g.kill() -class TestHubSelection(LimitedTestCase): +class TestHubSelection(tests.LimitedTestCase): def test_explicit_hub(self): oldhub = hubs.get_hub() @@ -217,7 +215,7 @@ class TestHubSelection(LimitedTestCase): hubs._threadlocal.hub = oldhub -class TestHubBlockingDetector(LimitedTestCase): +class TestHubBlockingDetector(tests.LimitedTestCase): TEST_TIMEOUT = 10 @skip_with_pyevent @@ -245,7 +243,7 @@ class TestHubBlockingDetector(LimitedTestCase): debug.hub_blocking_detection(False) -class TestSuspend(LimitedTestCase): +class TestSuspend(tests.LimitedTestCase): TEST_TIMEOUT = 4 longMessage = True maxDiff = None @@ -283,25 +281,30 @@ except eventlet.Timeout: shutil.rmtree(self.tempdir) -class TestBadFilenos(LimitedTestCase): +def test_repeated_select_bad_fd(): + from eventlet.green import select - @skip_with_pyevent - def test_repeated_selects(self): - from eventlet.green import select - self.assertRaises(ValueError, select.select, [-1], [], []) - self.assertRaises(ValueError, select.select, [-1], [], []) + def once(): + try: + select.select([-1], [], []) + assert False, 'Expected ValueError' + except ValueError: + pass + once() + once() -class TestFork(LimitedTestCase): - @skip_with_pyevent - def test_fork(self): - output = tests.run_python('tests/hub_test_fork.py') - lines = output.splitlines() - self.assertEqual(lines, [b"accept blocked", b"child died ok"], output) +@skip_with_pyevent +def test_fork(): + tests.run_isolated('hub_fork.py') + + +def test_fork_simple(): + tests.run_isolated('hub_fork_simple.py') -class TestDeadRunLoop(LimitedTestCase): +class TestDeadRunLoop(tests.LimitedTestCase): TEST_TIMEOUT = 2 class CustomException(Exception): @@ -397,7 +400,3 @@ print('ok') self.write_to_tempfile('newmod', module_source) output, _ = self.launch_subprocess('newmod.py') self.assertEqual(output, 'kqueue tried\nok\n') - - -if __name__ == '__main__': - main() diff --git a/tests/hub_test_fork.py b/tests/hub_test_fork.py deleted file mode 100644 index f886a96..0000000 --- a/tests/hub_test_fork.py +++ /dev/null @@ -1,25 +0,0 @@ -# no standard tests in this file, ignore -__test__ = False - -if __name__ == '__main__': - import os - import eventlet - server = eventlet.listen(('localhost', 12345)) - t = eventlet.Timeout(0.01) - try: - new_sock, address = server.accept() - except eventlet.Timeout as t: - pass - - pid = os.fork() - if not pid: - t = eventlet.Timeout(0.1) - try: - new_sock, address = server.accept() - except eventlet.Timeout as t: - print("accept blocked") - else: - kpid, status = os.wait() - assert kpid == pid - assert status == 0 - print("child died ok") diff --git a/tests/isolated/hub_fork.py b/tests/isolated/hub_fork.py new file mode 100644 index 0000000..1872942 --- /dev/null +++ b/tests/isolated/hub_fork.py @@ -0,0 +1,33 @@ +# verify eventlet.listen() accepts in forked children +__test__ = False + +if __name__ == '__main__': + import os + import sys + import eventlet + + server = eventlet.listen(('127.0.0.1', 0)) + result = eventlet.with_timeout(0.01, server.accept, timeout_value=True) + assert result is True, 'Expected timeout' + + pid = os.fork() + if pid < 0: + print('fork error') + sys.exit(1) + elif pid == 0: + with eventlet.Timeout(1): + sock, _ = server.accept() + sock.sendall('ok {0}'.format(os.getpid()).encode()) + sock.close() + sys.exit(0) + elif pid > 0: + with eventlet.Timeout(1): + sock = eventlet.connect(server.getsockname()) + data = sock.recv(20).decode() + assert data.startswith('ok ') + spid = int(data[3:].strip()) + assert spid == pid + kpid, status = os.wait() + assert kpid == pid + assert status == 0 + print('pass') diff --git a/tests/isolated/hub_fork_simple.py b/tests/isolated/hub_fork_simple.py new file mode 100644 index 0000000..96389de --- /dev/null +++ b/tests/isolated/hub_fork_simple.py @@ -0,0 +1,58 @@ +import os +import signal +import sys +import tempfile +__test__ = False + + +def parent(signal_path, pid): + eventlet.Timeout(5) + port = None + while True: + try: + contents = open(signal_path, 'rb').read() + port = int(contents.strip()) + break + except Exception: + eventlet.sleep(0.1) + eventlet.connect(('127.0.0.1', port)) + while True: + try: + contents = open(signal_path, 'rb').read() + result = contents.split()[1] + break + except Exception: + eventlet.sleep(0.1) + assert result == b'done', repr(result) + print('pass') + + +def child(signal_path): + eventlet.Timeout(5) + s = eventlet.listen(('127.0.0.1', 0)) + with open(signal_path, 'wb') as f: + f.write(str(s.getsockname()[1]).encode() + b'\n') + f.flush() + s.accept() + f.write(b'done\n') + f.flush() + + +if __name__ == '__main__': + import eventlet + + with tempfile.NamedTemporaryFile() as signal_file: + signal_path = signal_file.name + + pid = os.fork() + if pid < 0: + sys.stderr.write('fork error\n') + sys.exit(1) + elif pid == 0: + child(signal_path) + sys.exit(0) + elif pid > 0: + try: + parent(signal_path, pid) + except Exception: + os.kill(pid, signal.SIGTERM) diff --git a/tests/socket_test.py b/tests/socket_test.py index 7b61a7f..740c2c3 100644 --- a/tests/socket_test.py +++ b/tests/socket_test.py @@ -88,3 +88,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/test__refcount.py b/tests/test__refcount.py index 6ccfed7..1090a1f 100644 --- a/tests/test__refcount.py +++ b/tests/test__refcount.py @@ -2,78 +2,71 @@ are not leaked by the hub. """ import gc -from pprint import pformat +import pprint +import sys import weakref -from eventlet.support import clear_sys_exc_info +import eventlet from eventlet.green import socket -from eventlet.green.thread import start_new_thread -from eventlet.green.time import sleep - -SOCKET_TIMEOUT = 0.1 -def init_server(): - s = socket.socket() - s.settimeout(SOCKET_TIMEOUT) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind(('localhost', 0)) - s.listen(5) - return s, s.getsockname()[1] +SOCKET_TIMEOUT = 0.1 def handle_request(s, raise_on_timeout): try: conn, address = s.accept() except socket.timeout: + print('handle_request: server accept timeout') if raise_on_timeout: raise else: return - # print('handle_request - accepted') + print('handle_request: accepted') res = conn.recv(100) assert res == b'hello', repr(res) - # print('handle_request - recvd %r' % res) - res = conn.send(b'bye') - # print('handle_request - sent %r' % res) - # print('handle_request - conn refcount: %s' % sys.getrefcount(conn)) - # conn.close() + # print('handle_request: recvd %r' % res) + res = conn.sendall(b'bye') + # print('handle_request: sent %r' % res) + # print('handle_request: conn refcount: %s' % sys.getrefcount(conn)) -def make_request(port): +def make_request(addr): # print('make_request') - s = socket.socket() - s.connect(('localhost', port)) + s = eventlet.connect(addr) # print('make_request - connected') - res = s.send(b'hello') + res = s.sendall(b'hello') # print('make_request - sent %s' % res) res = s.recv(100) assert res == b'bye', repr(res) # print('make_request - recvd %r' % res) - # s.close() def run_interaction(run_client): - s, port = init_server() - start_new_thread(handle_request, (s, run_client)) + s = eventlet.listen(('127.0.0.1', 0)) + s.settimeout(SOCKET_TIMEOUT) + addr = s.getsockname() + print('run_interaction: addr:', addr) + eventlet.spawn(handle_request, s, run_client) if run_client: - start_new_thread(make_request, (port,)) - sleep(0.1 + SOCKET_TIMEOUT) - # print(sys.getrefcount(s.fd)) - # s.close() + eventlet.spawn(make_request, addr) + eventlet.sleep(0.1 + SOCKET_TIMEOUT) + print('run_interaction: refcount(s.fd)', sys.getrefcount(s.fd)) return weakref.ref(s.fd) def run_and_check(run_client): w = run_interaction(run_client=run_client) - clear_sys_exc_info() + # clear_sys_exc_info() gc.collect() - if w(): - print(pformat(gc.get_referrers(w()))) - for x in gc.get_referrers(w()): - print(pformat(x)) + fd = w() + print('run_and_check: weakref fd:', fd) + if fd: + print(pprint.pformat(gc.get_referrers(fd))) + for x in gc.get_referrers(fd): + print(pprint.pformat(x)) for y in gc.get_referrers(x): - print('- {0}'.format(pformat(y))) + print('- {0}'.format(pprint.pformat(y))) raise AssertionError('server should be dead by now') diff --git a/tests/test__socket_errors.py b/tests/test__socket_errors.py index c33b227..8cb1f87 100644 --- a/tests/test__socket_errors.py +++ b/tests/test__socket_errors.py @@ -56,7 +56,7 @@ class TestSocketErrors(unittest.TestCase): def test_create_connection_refused(): errno = None try: - socket.create_connection(('127.0.0.1', 0)) + socket.create_connection(('127.0.0.1', 1)) except socket.error as ex: errno = ex.errno assert errno in [111, 61, 10061], 'Expected socket.error ECONNREFUSED, got {0}'.format(errno) 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()) diff --git a/tests/tpool_test.py b/tests/tpool_test.py index d44ff6b..78e3437 100644 --- a/tests/tpool_test.py +++ b/tests/tpool_test.py @@ -20,7 +20,7 @@ import re import time import eventlet -from eventlet import tpool, debug, event +from eventlet import tpool from eventlet.support import six import tests @@ -218,10 +218,13 @@ class TestTpool(tests.LimitedTestCase): @tests.skip_with_pyevent def test_timeout(self): - import time - eventlet.Timeout(0.1, eventlet.TimeoutError()) - self.assertRaises(eventlet.TimeoutError, - tpool.execute, time.sleep, 0.3) + blocking = eventlet.patcher.original('time') + eventlet.Timeout(0.1, eventlet.Timeout()) + try: + tpool.execute(blocking.sleep, 0.3) + assert False, 'Expected Timeout' + except eventlet.Timeout: + pass @tests.skip_with_pyevent def test_killall(self): @@ -230,7 +233,7 @@ class TestTpool(tests.LimitedTestCase): @tests.skip_with_pyevent def test_killall_remaining_results(self): - semaphore = event.Event() + semaphore = eventlet.Event() def native_fun(): time.sleep(.5) @@ -14,7 +14,7 @@ show-source = 1 statistics = 1 [tox] -minversion=1.8 +minversion=2.5 envlist = pep8, py{26,27,33,34,35,py}-{selects,poll,epolls} @@ -34,6 +34,7 @@ setenv = selects: EVENTLET_HUB = selects poll: EVENTLET_HUB = poll epolls: EVENTLET_HUB = epolls + tox_cover_args = --with-coverage --cover-erase --cover-package=eventlet basepython = py26: python2.6 py27: python2.7 @@ -52,5 +53,5 @@ deps = {selects,poll,epolls}: psycopg2cffi-compat==1.1 {selects,poll,epolls}: pyzmq==13.1.0 commands = - nosetests --verbose --with-coverage --cover-erase --cover-package=eventlet {posargs:tests/} + nosetests --verbose {env:tox_cover_args} {posargs:tests/} coverage xml -i |
