Commit d060fd4d authored by Jason Madden's avatar Jason Madden

Merge pull request #557 from gevent/py34-test

Add Python 3.4 to test set. Fixes #408. Fixes #487. The build is green for PyPy, Py26 and Py34. Py27 intermittently displays a timing issue; Py33 intermittently displays a refcount "leak".
parents dbe77ded a2e74478
...@@ -3,6 +3,7 @@ python: ...@@ -3,6 +3,7 @@ python:
- "2.6" - "2.6"
- "2.7" - "2.7"
- "3.3" - "3.3"
- "3.4"
- "pypy" - "pypy"
script: script:
- if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then NWORKERS=4 PYTHON=pypy make travis_pypy; fi - if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then NWORKERS=4 PYTHON=pypy make travis_pypy; fi
......
...@@ -44,9 +44,11 @@ class socket(_socket.socket): ...@@ -44,9 +44,11 @@ class socket(_socket.socket):
self._write_event = io_class(fileno, 2) self._write_event = io_class(fileno, 2)
self.timeout = _socket.getdefaulttimeout() self.timeout = _socket.getdefaulttimeout()
@property if hasattr(_socket, 'SOCK_NONBLOCK'):
def type(self): # Only defined under Linux
return _socket.socket.type.__get__(self) & ~_socket.SOCK_NONBLOCK @property
def type(self):
return _socket.socket.type.__get__(self) & ~_socket.SOCK_NONBLOCK
def __enter__(self): def __enter__(self):
return self return self
......
...@@ -60,6 +60,7 @@ class SSLSocket(socket): ...@@ -60,6 +60,7 @@ class SSLSocket(socket):
suppress_ragged_eofs=True, npn_protocols=None, ciphers=None, suppress_ragged_eofs=True, npn_protocols=None, ciphers=None,
server_hostname=None, server_hostname=None,
_context=None): _context=None):
if _context: if _context:
self.context = _context self.context = _context
else: else:
......
...@@ -153,7 +153,7 @@ class AsyncResult(object): ...@@ -153,7 +153,7 @@ class AsyncResult(object):
>>> try: >>> try:
... result.get() ... result.get()
... except ZeroDivisionError: ... except ZeroDivisionError:
... print 'ZeroDivisionError' ... print('ZeroDivisionError')
ZeroDivisionError ZeroDivisionError
""" """
def __init__(self): def __init__(self):
......
...@@ -44,3 +44,40 @@ if PYPY: ...@@ -44,3 +44,40 @@ if PYPY:
k,v = __threading__._active.items()[0] k,v = __threading__._active.items()[0]
del __threading__._active[k] del __threading__._active[k]
__threading__._active[_get_ident()] = v __threading__._active[_get_ident()] = v
import sys
if sys.version_info[:2] >= (3, 4):
# XXX: Issue 18808 breaks us on Python 3.4.
# Thread objects now expect a callback from the interpreter itself
# (threadmodule.c:release_sentinel). Because this never happens
# when a greenlet exits, join() and friends will block forever.
# The solution below involves capturing the greenlet when it is
# started and deferring the known broken methods to it.
class Thread(__threading__.Thread):
_greenlet = None
def is_alive(self):
return bool(self._greenlet)
isAlive = is_alive
def _set_tstate_lock(self):
self._greenlet = getcurrent()
def run(self):
try:
super(Thread, self).run()
finally:
del self._greenlet # avoid ref cycles
self._stop() # mark as finished
def join(self, timeout=None):
if self._greenlet is None:
return
self._greenlet.join(timeout=timeout)
def _wait_for_tstate_lock(self, *args, **kwargs):
raise NotImplementedError()
__implements__.append('Thread')
...@@ -33,7 +33,7 @@ class Timeout(BaseException): ...@@ -33,7 +33,7 @@ class Timeout(BaseException):
>>> import gevent >>> import gevent
>>> gevent.Timeout(0.1).start() >>> gevent.Timeout(0.1).start()
>>> gevent.sleep(0.2) >>> gevent.sleep(0.2) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last): Traceback (most recent call last):
... ...
Timeout: 0.1 seconds Timeout: 0.1 seconds
...@@ -132,7 +132,7 @@ class Timeout(BaseException): ...@@ -132,7 +132,7 @@ class Timeout(BaseException):
def __str__(self): def __str__(self):
""" """
>>> raise Timeout >>> raise Timeout #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last): Traceback (most recent call last):
... ...
Timeout Timeout
......
...@@ -21,20 +21,10 @@ class TestPickle(greentest.TestCase): ...@@ -21,20 +21,10 @@ class TestPickle(greentest.TestCase):
assert r == loaded, (r, loaded) assert r == loaded, (r, loaded)
assert r.family == loaded.family, (r, loaded) assert r.family == loaded.family, (r, loaded)
def test0(self): for i in range(0, pickle.HIGHEST_PROTOCOL):
return self._test(0) def make_test(j):
return lambda self: self._test(j)
def test1(self): setattr(TestPickle, 'test' + str(i), make_test(i) )
return self._test(1)
def test2(self):
return self._test(2)
if pickle.HIGHEST_PROTOCOL == 3:
def test3(self):
return self._test(3)
else:
assert pickle.HIGHEST_PROTOCOL == 2, pickle.HIGHEST_PROTOCOL
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -40,6 +40,8 @@ class TestTCP(greentest.TestCase): ...@@ -40,6 +40,8 @@ class TestTCP(greentest.TestCase):
__timeout__ = None __timeout__ = None
TIMEOUT_ERROR = socket.timeout TIMEOUT_ERROR = socket.timeout
long_data = ", ".join([str(x) for x in range(20000)]) long_data = ", ".join([str(x) for x in range(20000)])
if six.PY3:
long_data = long_data.encode('ascii')
def setUp(self): def setUp(self):
greentest.TestCase.setUp(self) greentest.TestCase.setUp(self)
...@@ -49,6 +51,10 @@ class TestTCP(greentest.TestCase): ...@@ -49,6 +51,10 @@ class TestTCP(greentest.TestCase):
self.port = listener.getsockname()[1] self.port = listener.getsockname()[1]
def cleanup(self): def cleanup(self):
try:
self.listener.close()
except:
pass
del self.listener del self.listener
def create_connection(self): def create_connection(self):
...@@ -62,7 +68,11 @@ class TestTCP(greentest.TestCase): ...@@ -62,7 +68,11 @@ class TestTCP(greentest.TestCase):
def accept_and_read(): def accept_and_read():
try: try:
read_data.append(self.listener.accept()[0].makefile().read()) conn, _ = self.listener.accept()
r = conn.makefile(mode='rb')
read_data.append(r.read())
r.close()
conn.close()
except: except:
traceback.print_exc() traceback.print_exc()
os._exit(1) os._exit(1)
...@@ -72,13 +82,14 @@ class TestTCP(greentest.TestCase): ...@@ -72,13 +82,14 @@ class TestTCP(greentest.TestCase):
client.sendall(data) client.sendall(data)
client.close() client.close()
server.join() server.join()
assert read_data[0] == self.long_data, read_data self.assertEqual(read_data[0], self.long_data)
def test_sendall_str(self): def test_sendall_str(self):
self._test_sendall(self.long_data) self._test_sendall(self.long_data)
def test_sendall_unicode(self): if not six.PY3:
self._test_sendall(six.text_type(self.long_data)) def test_sendall_unicode(self):
self._test_sendall(six.text_type(self.long_data))
def test_sendall_array(self): def test_sendall_array(self):
data = array.array("B", self.long_data) data = array.array("B", self.long_data)
...@@ -89,25 +100,28 @@ class TestTCP(greentest.TestCase): ...@@ -89,25 +100,28 @@ class TestTCP(greentest.TestCase):
N = 100000 N = 100000
def server(): def server():
(client, addr) = self.listener.accept() (remote_client, _) = self.listener.accept()
# start reading, then, while reading, start writing. the reader should not hang forever # start reading, then, while reading, start writing. the reader should not hang forever
def sendall(): def sendall():
client.sendall('t' * N) remote_client.sendall(b't' * N)
sender = Thread(target=sendall) sender = Thread(target=sendall)
result = client.recv(1000) result = remote_client.recv(1000)
self.assertEqual(result, 'hello world') self.assertEqual(result, b'hello world')
sender.join() sender.join()
remote_client.close()
server_thread = Thread(target=server) server_thread = Thread(target=server)
client = self.create_connection() client = self.create_connection()
client_reader = Thread(target=client.makefile().read, args=(N, )) client_file = client.makefile()
client_reader = Thread(target=client_file.read, args=(N, ))
time.sleep(0.1) time.sleep(0.1)
client.send('hello world') client.sendall(b'hello world')
time.sleep(0.1) time.sleep(0.1)
# close() used to hang # close() used to hang
client_file.close()
client.close() client.close()
# this tests "full duplex" bug; # this tests "full duplex" bug;
...@@ -125,6 +139,8 @@ class TestTCP(greentest.TestCase): ...@@ -125,6 +139,8 @@ class TestTCP(greentest.TestCase):
took = time.time() - start took = time.time() - start
assert 1 - 0.1 <= took <= 1 + 0.1, (time.time() - start) assert 1 - 0.1 <= took <= 1 + 0.1, (time.time() - start)
acceptor.join() acceptor.join()
client.close()
client_sock[0][0].close()
# On Windows send() accepts whatever is thrown at it # On Windows send() accepts whatever is thrown at it
if sys.platform != 'win32': if sys.platform != 'win32':
...@@ -136,28 +152,30 @@ class TestTCP(greentest.TestCase): ...@@ -136,28 +152,30 @@ class TestTCP(greentest.TestCase):
time.sleep(0.1) time.sleep(0.1)
assert client_sock assert client_sock
client.settimeout(0.1) client.settimeout(0.1)
data_sent = 'h' * 1000000 data_sent = b'h' * 1000000
start = time.time() start = time.time()
self.assertRaises(self.TIMEOUT_ERROR, client.sendall, data_sent) self.assertRaises(self.TIMEOUT_ERROR, client.sendall, data_sent)
took = time.time() - start took = time.time() - start
assert 0.1 - 0.01 <= took <= 0.1 + 0.1, took assert 0.1 - 0.01 <= took <= 0.1 + 0.1, took
acceptor.join() acceptor.join()
client.close()
client_sock[0][0].close()
def test_makefile(self): def test_makefile(self):
def accept_once(): def accept_once():
conn, addr = self.listener.accept() conn, addr = self.listener.accept()
fd = conn.makefile(mode='w') fd = conn.makefile(mode='wb')
fd.write('hello\n') fd.write(b'hello\n')
fd.close() fd.close()
conn.close() # for pypy conn.close() # for pypy
acceptor = Thread(target=accept_once) acceptor = Thread(target=accept_once)
client = self.create_connection() client = self.create_connection()
fd = client.makefile() fd = client.makefile(mode='rb')
client.close() client.close()
assert fd.readline() == 'hello\n' assert fd.readline() == b'hello\n'
assert fd.read() == '' assert fd.read() == b''
fd.close() fd.close()
acceptor.join() acceptor.join()
......
...@@ -10,12 +10,14 @@ class TestSSL(TestTCP): ...@@ -10,12 +10,14 @@ class TestSSL(TestTCP):
certfile = os.path.join(os.path.dirname(__file__), 'test_server.crt') certfile = os.path.join(os.path.dirname(__file__), 'test_server.crt')
privfile = os.path.join(os.path.dirname(__file__), 'test_server.key') privfile = os.path.join(os.path.dirname(__file__), 'test_server.key')
TIMEOUT_ERROR = socket.sslerror # Python 2.x has socket.sslerror, which we need to be sure is an alias for
# ssl.SSLError. That's gone in Py3 though.
TIMEOUT_ERROR = getattr(socket, 'sslerror', ssl.SSLError)
def setUp(self): def setUp(self):
greentest.TestCase.setUp(self) greentest.TestCase.setUp(self)
self.listener, r = ssl_listener(('127.0.0.1', 0), self.privfile, self.certfile) self.listener, raw_listener = ssl_listener(('127.0.0.1', 0), self.privfile, self.certfile)
self.port = r.getsockname()[1] self.port = self.listener.getsockname()[1]
def create_connection(self): def create_connection(self):
return ssl.wrap_socket(super(TestSSL, self).create_connection()) return ssl.wrap_socket(super(TestSSL, self).create_connection())
...@@ -27,10 +29,10 @@ del TestTCP ...@@ -27,10 +29,10 @@ del TestTCP
def ssl_listener(address, private_key, certificate): def ssl_listener(address, private_key, certificate):
r = socket.socket() raw_listener = socket.socket()
greentest.bind_and_listen(r, address) greentest.bind_and_listen(raw_listener, address)
sock = ssl.wrap_socket(r, private_key, certificate) sock = ssl.wrap_socket(raw_listener, private_key, certificate)
return sock, r return sock, raw_listener
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -122,7 +122,13 @@ def discover(tests=None, ignore=None): ...@@ -122,7 +122,13 @@ def discover(tests=None, ignore=None):
default_options = {'timeout': TIMEOUT} default_options = {'timeout': TIMEOUT}
for filename in tests: for filename in tests:
if 'TESTRUNNER' in open(filename).read(): with open(filename, 'rb') as f:
# Some of the test files (e.g., test__socket_dns) are
# UTF8 encoded. Depending on the environment, Python 3 may
# try to decode those as ASCII, which fails with UnicodeDecodeError.
# Thus, be sure to open and compare in binary mode.
contents = f.read()
if b'TESTRUNNER' in contents:
module = __import__(filename.rsplit('.', 1)[0]) module = __import__(filename.rsplit('.', 1)[0])
for cmd, options in module.TESTRUNNER(): for cmd, options in module.TESTRUNNER():
if remove_options(cmd)[-1] in ignore: if remove_options(cmd)[-1] in ignore:
......
...@@ -92,7 +92,6 @@ test__os.py ...@@ -92,7 +92,6 @@ test__os.py
test__backdoor.py test__backdoor.py
test_threading_2.py test_threading_2.py
test__refcount.py test__refcount.py
test__socket.py
test__subprocess.py test__subprocess.py
test__all__.py test__all__.py
test__fileobject.py test__fileobject.py
...@@ -100,7 +99,6 @@ test__pywsgi.py ...@@ -100,7 +99,6 @@ test__pywsgi.py
test__socket_ex.py test__socket_ex.py
test__example_echoserver.py test__example_echoserver.py
test__subprocess_poll.py test__subprocess_poll.py
test__ssl.py
test__makefile_ref.py test__makefile_ref.py
test__socketpair.py test__socketpair.py
test__server_pywsgi.py test__server_pywsgi.py
...@@ -109,6 +107,7 @@ test__server.py ...@@ -109,6 +107,7 @@ test__server.py
test__example_portforwarder.py test__example_portforwarder.py
test__execmodules.py test__execmodules.py
FLAKY test__greenio.py FLAKY test__greenio.py
FLAKY test__socket_dns.py
'''.strip().split('\n') '''.strip().split('\n')
if os.environ.get('GEVENT_RESOLVER') == 'ares': if os.environ.get('GEVENT_RESOLVER') == 'ares':
...@@ -132,6 +131,7 @@ FLAKY test__greenio.py ...@@ -132,6 +131,7 @@ FLAKY test__greenio.py
test__socket_close.py test__socket_close.py
test__select.py test__select.py
test__greenlet.py test__greenlet.py
FLAKY test__socket.py
'''.strip().split() '''.strip().split()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment