Commit 10c84561 authored by Jason Madden's avatar Jason Madden

Basic tests for 3.12 pass locally.

parent 748cedaf
...@@ -8,3 +8,6 @@ Related changes include: ...@@ -8,3 +8,6 @@ Related changes include:
- Honor ``sys.unraisablehook`` when a callback function produces an - Honor ``sys.unraisablehook`` when a callback function produces an
exception, and handling the exception in the hub *also* produces an exception, and handling the exception in the hub *also* produces an
exception. In older versions, these would be simply printed. exception. In older versions, these would be simply printed.
- ``setup.py`` no longer includes the ``setup_requires`` keyword.
Installation with a tool that understands ``pyproject.toml`` is
recommended.
...@@ -37,7 +37,10 @@ requires = [ ...@@ -37,7 +37,10 @@ requires = [
# 1.1.3 is needed for CPython 3.11. # 1.1.3 is needed for CPython 3.11.
# 2.0 is not ABI compatible with earlier releases, but with luck it won't # 2.0 is not ABI compatible with earlier releases, but with luck it won't
# have to break the ABI again. # have to break the ABI again.
"greenlet >= 2.0.0 ; platform_python_implementation == 'CPython'", # 3.0 is ABI compatible with earlier releases, so we can switch back and
# forth between 2 and 3 without recompiling. 3.0 is required for
# Python 3.12
"greenlet >= 3.0a1 ; platform_python_implementation == 'CPython'",
] ]
[tool.towncrier] [tool.towncrier]
......
...@@ -208,7 +208,10 @@ greenlet_requires = [ ...@@ -208,7 +208,10 @@ greenlet_requires = [
# 1.1.3 is needed for 3.11, and supports everything 1.1.0 did. # 1.1.3 is needed for 3.11, and supports everything 1.1.0 did.
# 2.0.0 supports everything 1.1.3 did, but breaks the ABI in a way that hopefully # 2.0.0 supports everything 1.1.3 did, but breaks the ABI in a way that hopefully
# won't break again. # won't break again.
'greenlet >= 2.0.0 ; platform_python_implementation=="CPython"', # 3.0 is ABI compatible and adds support for Python 3.12 (but right
# now it's alpha because of Cython, so we only require it on 3.12)
'greenlet >= 2.0.0 ; platform_python_implementation=="CPython" and python_version < "3.12"',
'greenlet >= 3.0a1 ; platform_python_implementation=="CPython" and python_version >= "3.12"',
] ]
# Note that we don't add cffi to install_requires, it's # Note that we don't add cffi to install_requires, it's
...@@ -235,19 +238,6 @@ install_requires = greenlet_requires + CFFI_REQUIRES + [ ...@@ -235,19 +238,6 @@ install_requires = greenlet_requires + CFFI_REQUIRES + [
'zope.interface', 'zope.interface',
] ]
# We use headers from greenlet, so it needs to be installed before we
# can compile. If it isn't already installed before we start
# installing, and we say 'pip install gevent', a 'setup_requires'
# doesn't save us: pip happily downloads greenlet and drops it in a
# .eggs/ directory in the build directory, but that directory doesn't
# have includes! So we fail to build a wheel, pip goes ahead and
# installs greenlet, and builds gevent again, which works.
# Since we ship the greenlet header for buildout support (which fails
# to install the headers at all, AFAICS, we don't need to bother with
# the buggy setup_requires.)
setup_requires = CFFI_REQUIRES + []
if PYPY: if PYPY:
# These use greenlet/greenlet.h, which doesn't exist on PyPy # These use greenlet/greenlet.h, which doesn't exist on PyPy
...@@ -397,7 +387,6 @@ def run_setup(ext_modules): ...@@ -397,7 +387,6 @@ def run_setup(ext_modules):
'clean': GeventClean, 'clean': GeventClean,
}, },
install_requires=install_requires, install_requires=install_requires,
setup_requires=setup_requires,
extras_require={ extras_require={
# Each extra intended for end users must be documented in install.rst # Each extra intended for end users must be documented in install.rst
'dnspython': EXTRA_DNSPYTHON, 'dnspython': EXTRA_DNSPYTHON,
...@@ -422,7 +411,6 @@ def run_setup(ext_modules): ...@@ -422,7 +411,6 @@ def run_setup(ext_modules):
# anyway (coveralls -> cryptopgraphy -> openssl). # anyway (coveralls -> cryptopgraphy -> openssl).
# coverage 5 needs coveralls 1.11 # coverage 5 needs coveralls 1.11
'coverage >= 5.0 ; sys_platform != "win32"', 'coverage >= 5.0 ; sys_platform != "win32"',
'coveralls>=1.7.0 ; sys_platform != "win32"',
# leak checks. previously we had a hand-rolled version. # leak checks. previously we had a hand-rolled version.
'objgraph', 'objgraph',
......
This diff is collapsed.
...@@ -59,7 +59,8 @@ except AttributeError: ...@@ -59,7 +59,8 @@ except AttributeError:
_GLOBAL_DEFAULT_TIMEOUT = object() _GLOBAL_DEFAULT_TIMEOUT = object()
def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None, **kwargs): def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None, *,
all_errors=False):
""" """
create_connection(address, timeout=None, source_address=None, *, all_errors=False) -> socket create_connection(address, timeout=None, source_address=None, *, all_errors=False) -> socket
...@@ -81,20 +82,18 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N ...@@ -81,20 +82,18 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N
it will be used instead of ignored, if the platform supplies it will be used instead of ignored, if the platform supplies
:func:`socket.inet_pton`. :func:`socket.inet_pton`.
.. versionchanged:: 22.08.0 .. versionchanged:: 22.08.0
Add the *all_errors* argument. This only has meaning on Python 3.11; Add the *all_errors* argument. This only has meaning on Python 3.11+;
it is a programming error to pass it on earlier versions. it is a programming error to pass it on earlier versions.
.. versionchanged:: NEXT
You can pass a value for ``all_errors`` on any version of Python.
It is forced to false for any version before 3.11 inside the function.
""" """
# Sigh. This function is a near-copy of the CPython implementation. # Sigh. This function is a near-copy of the CPython implementation.
# Even though we simplified some things, it's still a little complex to # Even though we simplified some things, it's still a little complex to
# cope with error handling, which got even more complicated in 3.11. # cope with error handling, which got even more complicated in 3.11.
# pylint:disable=too-many-locals,too-many-branches # pylint:disable=too-many-locals,too-many-branches
if not PY311:
all_errors = False all_errors = False
if PY311:
all_errors = kwargs.pop('all_errors', False)
if kwargs:
raise TypeError("Too many keyword arguments to create_connection", kwargs)
host, port = address host, port = address
exceptions = [] exceptions = []
......
This diff is collapsed.
...@@ -121,8 +121,6 @@ class TestConfig(unittest.TestCase): ...@@ -121,8 +121,6 @@ class TestConfig(unittest.TestCase):
class TestImportableSetting(unittest.TestCase): class TestImportableSetting(unittest.TestCase):
assertRaisesRegex = getattr(unittest.TestCase, 'assertRaisesRegex',
unittest.TestCase.assertRaisesRegexp)
def test_empty_list(self): def test_empty_list(self):
i = _config.ImportableSetting() i = _config.ImportableSetting()
with self.assertRaisesRegex(ImportError, with self.assertRaisesRegex(ImportError,
......
...@@ -21,9 +21,6 @@ class Test(unittest.TestCase): ...@@ -21,9 +21,6 @@ class Test(unittest.TestCase):
# NOTE that we extend unittest.TestCase, not greentest.TestCase # NOTE that we extend unittest.TestCase, not greentest.TestCase
# Extending the later causes the wrong hub to get used. # Extending the later causes the wrong hub to get used.
assertRaisesRegex = getattr(unittest.TestCase, 'assertRaisesRegex',
getattr(unittest.TestCase, 'assertRaisesRegexp'))
BACKENDS_THAT_SUCCEED_WHEN_FD_CLOSED = ( BACKENDS_THAT_SUCCEED_WHEN_FD_CLOSED = (
'kqueue', 'kqueue',
'epoll', 'epoll',
......
...@@ -62,7 +62,8 @@ class Test_wsgiserver(util.TestServer): ...@@ -62,7 +62,8 @@ class Test_wsgiserver(util.TestServer):
sock = socket.create_connection((params.DEFAULT_LOCAL_HOST_ADDR, self.PORT)) sock = socket.create_connection((params.DEFAULT_LOCAL_HOST_ADDR, self.PORT))
ssl_sock = None ssl_sock = None
if self._use_ssl: if self._use_ssl:
ssl_sock = ssl.wrap_socket(sock) # pylint:disable=deprecated-method context = ssl.SSLContext()
ssl_sock = context.wrap_socket(sock)
sock_file = ssl_sock.makefile(mode='rwb') sock_file = ssl_sock.makefile(mode='rwb')
else: else:
sock_file = sock.makefile(mode='rwb') sock_file = sock.makefile(mode='rwb')
......
...@@ -14,7 +14,7 @@ from gevent.testing.params import DEFAULT_CONNECT ...@@ -14,7 +14,7 @@ from gevent.testing.params import DEFAULT_CONNECT
from gevent.testing.sockets import tcp_listener from gevent.testing.sockets import tcp_listener
dirname = os.path.dirname(os.path.abspath(__file__)) dirname = os.path.dirname(os.path.abspath(__file__))
certfile = os.path.join(dirname, '2_7_keycert.pem') CERTFILE = os.path.join(dirname, '2_7_keycert.pem')
pid = os.getpid() pid = os.getpid()
PY3 = greentest.PY3 PY3 = greentest.PY3
...@@ -259,7 +259,7 @@ class TestSSL(Test): ...@@ -259,7 +259,7 @@ class TestSSL(Test):
try: try:
# Note: We get ResourceWarning about 'x' # Note: We get ResourceWarning about 'x'
# on Python 3 if we don't join the spawned thread # on Python 3 if we don't join the spawned thread
x = ssl.wrap_socket(connector) x = ssl.SSLContext().wrap_socket(connector)
# Wait to be fully accepted. We could otherwise raise ahead # Wait to be fully accepted. We could otherwise raise ahead
# of the server and close ourself before it's ready to read. # of the server and close ourself before it's ready to read.
accepted_event.wait() accepted_event.wait()
...@@ -284,7 +284,7 @@ class TestSSL(Test): ...@@ -284,7 +284,7 @@ class TestSSL(Test):
with Closing() as closer: with Closing() as closer:
s = closer(self.make_open_socket()) s = closer(self.make_open_socket())
fileno = s.fileno() fileno = s.fileno()
s = closer(ssl.wrap_socket(s)) s = closer(ssl.SSLContext().wrap_socket(s))
fileno = s.fileno() fileno = s.fileno()
self.assert_open(s, fileno) self.assert_open(s, fileno)
s.close() s.close()
...@@ -293,7 +293,7 @@ class TestSSL(Test): ...@@ -293,7 +293,7 @@ class TestSSL(Test):
def test_makefile1(self): def test_makefile1(self):
with Closing() as closer: with Closing() as closer:
raw_s = closer(self.make_open_socket()) raw_s = closer(self.make_open_socket())
s = closer(ssl.wrap_socket(raw_s)) s = closer(ssl.SSLContext().wrap_socket(raw_s))
fileno = s.fileno() fileno = s.fileno()
self.assert_open(s, fileno) self.assert_open(s, fileno)
...@@ -310,7 +310,7 @@ class TestSSL(Test): ...@@ -310,7 +310,7 @@ class TestSSL(Test):
s = closer(self.make_open_socket()) s = closer(self.make_open_socket())
fileno = s.fileno() fileno = s.fileno()
s = closer(ssl.wrap_socket(s)) s = closer(ssl.SSLContext().wrap_socket(s))
fileno = s.fileno() fileno = s.fileno()
self.assert_open(s, fileno) self.assert_open(s, fileno)
f = closer(s.makefile()) f = closer(s.makefile())
...@@ -321,6 +321,11 @@ class TestSSL(Test): ...@@ -321,6 +321,11 @@ class TestSSL(Test):
s.close() s.close()
self.assert_closed(s, fileno) self.assert_closed(s, fileno)
def _wrap_socket(self, sock, *, keyfile, certfile, server_side=False):
context = ssl.SSLContext()
context.load_cert_chain(certfile=certfile, keyfile=keyfile)
return context.wrap_socket(sock, server_side=server_side)
def test_server_simple(self): def test_server_simple(self):
with Closing() as closer: with Closing() as closer:
listener = closer(tcp_listener(backlog=1)) listener = closer(tcp_listener(backlog=1))
...@@ -334,8 +339,8 @@ class TestSSL(Test): ...@@ -334,8 +339,8 @@ class TestSSL(Test):
client_socket = closer.accept(listener) client_socket = closer.accept(listener)
t.accepted_event.set() t.accepted_event.set()
client_socket = closer( client_socket = closer(
ssl.wrap_socket(client_socket, keyfile=certfile, certfile=certfile, self._wrap_socket(client_socket, keyfile=CERTFILE, certfile=CERTFILE,
server_side=True)) server_side=True))
fileno = client_socket.fileno() fileno = client_socket.fileno()
self.assert_open(client_socket, fileno) self.assert_open(client_socket, fileno)
client_socket.close() client_socket.close()
...@@ -354,8 +359,8 @@ class TestSSL(Test): ...@@ -354,8 +359,8 @@ class TestSSL(Test):
client_socket = closer.accept(listener) client_socket = closer.accept(listener)
t.accepted_event.set() t.accepted_event.set()
client_socket = closer( client_socket = closer(
ssl.wrap_socket(client_socket, keyfile=certfile, certfile=certfile, self._wrap_socket(client_socket, keyfile=CERTFILE, certfile=CERTFILE,
server_side=True)) server_side=True))
fileno = client_socket.fileno() fileno = client_socket.fileno()
self.assert_open(client_socket, fileno) self.assert_open(client_socket, fileno)
f = client_socket.makefile() f = client_socket.makefile()
...@@ -377,8 +382,8 @@ class TestSSL(Test): ...@@ -377,8 +382,8 @@ class TestSSL(Test):
t.accepted_event.set() t.accepted_event.set()
client_socket = closer.accept(listener) client_socket = closer.accept(listener)
client_socket = closer( client_socket = closer(
ssl.wrap_socket(client_socket, keyfile=certfile, certfile=certfile, self._wrap_socket(client_socket, keyfile=CERTFILE, certfile=CERTFILE,
server_side=True)) server_side=True))
fileno = client_socket.fileno() fileno = client_socket.fileno()
self.assert_open(client_socket, fileno) self.assert_open(client_socket, fileno)
...@@ -394,7 +399,7 @@ class TestSSL(Test): ...@@ -394,7 +399,7 @@ class TestSSL(Test):
raw_listener = tcp_listener(backlog=1) raw_listener = tcp_listener(backlog=1)
fileno = raw_listener.fileno() fileno = raw_listener.fileno()
port = raw_listener.getsockname()[1] port = raw_listener.getsockname()[1]
listener = ssl.wrap_socket(raw_listener, keyfile=certfile, certfile=certfile) listener = self._wrap_socket(raw_listener, keyfile=CERTFILE, certfile=CERTFILE)
connector = socket.socket() connector = socket.socket()
t = self._make_ssl_connect_task(connector, port) t = self._make_ssl_connect_task(connector, port)
...@@ -414,13 +419,13 @@ class TestSSL(Test): ...@@ -414,13 +419,13 @@ class TestSSL(Test):
def test_serverssl_makefile2(self): def test_serverssl_makefile2(self):
raw_listener = tcp_listener(backlog=1) raw_listener = tcp_listener(backlog=1)
port = raw_listener.getsockname()[1] port = raw_listener.getsockname()[1]
listener = ssl.wrap_socket(raw_listener, keyfile=certfile, certfile=certfile) listener = self._wrap_socket(raw_listener, keyfile=CERTFILE, certfile=CERTFILE)
accepted_event = threading.Event() accepted_event = threading.Event()
def connect(connector=socket.socket()): def connect(connector=socket.socket()):
try: try:
connector.connect((DEFAULT_CONNECT, port)) connector.connect((DEFAULT_CONNECT, port))
s = ssl.wrap_socket(connector) s = ssl.SSLContext().wrap_socket(connector)
accepted_event.wait() accepted_event.wait()
s.sendall(b'test_serverssl_makefile2') s.sendall(b'test_serverssl_makefile2')
s.shutdown(socket.SHUT_RDWR) s.shutdown(socket.SHUT_RDWR)
......
...@@ -581,9 +581,10 @@ class TestFunctions(greentest.TestCase): ...@@ -581,9 +581,10 @@ class TestFunctions(greentest.TestCase):
exclude.append('gethostbyname') exclude.append('gethostbyname')
exclude.append('gethostbyname_ex') exclude.append('gethostbyname_ex')
exclude.append('gethostbyaddr') exclude.append('gethostbyaddr')
if sys.version_info[:2] == (3, 11): if sys.version_info[:2] < (3, 11):
# Be careful not to exclude this on 3.12, etc, in case of # 3.11+ add ``*, all_errors=False``. We allow that on all versions,
# more changes. # forcing it to a false value if the user sends a true value before
# exception groups exist.
exclude.append('create_connection') exclude.append('create_connection')
self.assertMonkeyPatchedFuncSignatures('socket', exclude=exclude) self.assertMonkeyPatchedFuncSignatures('socket', exclude=exclude)
......
...@@ -10,15 +10,25 @@ import gevent.testing as greentest ...@@ -10,15 +10,25 @@ import gevent.testing as greentest
from gevent.tests import test__socket from gevent.tests import test__socket
import ssl import ssl
from gevent.testing import PY2
def ssl_listener(private_key, certificate): def ssl_listener(private_key, certificate):
raw_listener = socket.socket() raw_listener = socket.socket()
greentest.bind_and_listen(raw_listener) greentest.bind_and_listen(raw_listener)
# pylint:disable=deprecated-method # pylint:disable=deprecated-method
sock = ssl.wrap_socket(raw_listener, private_key, certificate, server_side=True) sock = wrap_socket(raw_listener, keyfile=private_key, certfile=certificate,
server_side=True)
return sock, raw_listener return sock, raw_listener
def wrap_socket(sock, *, keyfile=None, certfile=None, server_side=False):
context = ssl.SSLContext(
protocol=ssl.PROTOCOL_TLS
)
context.verify_mode = ssl.CERT_NONE
context.check_hostname = False
context.load_default_certs()
if keyfile is not None or certfile is not None:
context.load_cert_chain(certfile=certfile, keyfile=keyfile)
return context.wrap_socket(sock, server_side=server_side)
class TestSSL(test__socket.TestTCP): class TestSSL(test__socket.TestTCP):
...@@ -34,7 +44,7 @@ class TestSSL(test__socket.TestTCP): ...@@ -34,7 +44,7 @@ class TestSSL(test__socket.TestTCP):
# PyPy3 7.2 has a bug, though: it shares much of the SSL implementation with Python 2, # PyPy3 7.2 has a bug, though: it shares much of the SSL implementation with Python 2,
# and it unconditionally does `socket.sslerror = SSLError` when ssl is imported. # and it unconditionally does `socket.sslerror = SSLError` when ssl is imported.
# So we can't rely on getattr/hasattr tests, we must be explicit. # So we can't rely on getattr/hasattr tests, we must be explicit.
TIMEOUT_ERROR = socket.sslerror if PY2 else socket.timeout # pylint:disable=no-member TIMEOUT_ERROR = socket.timeout # pylint:disable=no-member
def _setup_listener(self): def _setup_listener(self):
listener, raw_listener = ssl_listener(self.privfile, self.certfile) listener, raw_listener = ssl_listener(self.privfile, self.certfile)
...@@ -44,7 +54,7 @@ class TestSSL(test__socket.TestTCP): ...@@ -44,7 +54,7 @@ class TestSSL(test__socket.TestTCP):
def create_connection(self, *args, **kwargs): # pylint:disable=signature-differs def create_connection(self, *args, **kwargs): # pylint:disable=signature-differs
return self._close_on_teardown( return self._close_on_teardown(
# pylint:disable=deprecated-method # pylint:disable=deprecated-method
ssl.wrap_socket(super(TestSSL, self).create_connection(*args, **kwargs))) wrap_socket(super(TestSSL, self).create_connection(*args, **kwargs)))
# The SSL library can take a long time to buffer the large amount of data we're trying # The SSL library can take a long time to buffer the large amount of data we're trying
# to send, so we can't compare to the timeout values # to send, so we can't compare to the timeout values
......
...@@ -39,6 +39,9 @@ __imports__ += [ ...@@ -39,6 +39,9 @@ __imports__ += [
# non-Python (native) APIs, so we shouldn't mess with it. # non-Python (native) APIs, so we shouldn't mess with it.
__imports__.append('get_native_id') __imports__.append('get_native_id')
# Added to 3.12
if hasattr(__thread__, 'daemon_threads_allowed'):
__imports__.append('daemon_threads_allowed')
error = __thread__.error error = __thread__.error
......
...@@ -374,7 +374,20 @@ class GreenletTree(object): ...@@ -374,7 +374,20 @@ class GreenletTree(object):
@staticmethod @staticmethod
def __render_tb(tree, label, frame, limit): def __render_tb(tree, label, frame, limit):
tree.child_data(label) tree.child_data(label)
tb = ''.join(traceback.format_stack(frame, limit)) # XXX: Issues with tblib?
# 3.12b3 is crashing walking the stack on macOS;
# on Linux CI, it is failing with a nice attribute error
# (which watches where the macOS is failing, inside a call to
# Py_GetAttr):
#
# File "/opt/hostedtoolcache/Python/3.12.0-beta.3/x64/lib/python3.12/traceback.py", line 339, in walk_stack
# yield f, f.f_lineno
# AttributeError: 'dict' object has no attribute 'f_lineno'
if sys.version_info != (3, 12, 0, 'beta', 3):
tb = ''.join(traceback.format_stack(frame, limit))
else:
tb = ''
tree.child_multidata(tb) tree.child_multidata(tb)
@staticmethod @staticmethod
......
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