Commit 5dfe00d6 authored by Jason Madden's avatar Jason Madden

Get the basic tests passing on 3.11; add it to the CI matrix to see what we see there.

Haven't tried any of the 3.11-specific tests yet.
parent 6d938e37
...@@ -148,7 +148,7 @@ jobs: ...@@ -148,7 +148,7 @@ jobs:
# 3.10 needs more work: dnspython for example doesn't work # 3.10 needs more work: dnspython for example doesn't work
# with it. That means for the bulk of our testing we need to # with it. That means for the bulk of our testing we need to
# stick to 3.9. # stick to 3.9.
python-version: [2.7, pypy-2.7, pypy-3.7, 3.6, 3.7, 3.8, 3.9, '3.10'] python-version: [2.7, pypy-2.7, pypy-3.7, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11.0-rc.2']
# ubuntu-latest is at least 20.04. But this breaks the SSL # ubuntu-latest is at least 20.04. But this breaks the SSL
# tests because Ubuntu increased the default OpenSSL # tests because Ubuntu increased the default OpenSSL
# strictness. # strictness.
...@@ -178,6 +178,8 @@ jobs: ...@@ -178,6 +178,8 @@ jobs:
python-version: 3.9 python-version: 3.9
- os: ubuntu-18.04 - os: ubuntu-18.04
python-version: '3.10' python-version: '3.10'
- os: ubuntu-18.04
python-version: '3.11-rc.2'
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
......
Added preliminary support for Python 3.11 (rc2 and later).
Some platforms may or may not have binary wheels at this time.
.. important:: Support for legacy versions of Python, including 2.7
and 3.6, will be ending soon. The
maintenance burden has become too great and the
maintainer's time is too limited.
Ideally, there will be a release of gevent compatible
with a final release of greenlet 2.0 that still
supports those legacy versions, but that may not be
possible; this may be the final release to support them.
...@@ -19,6 +19,7 @@ PY36 = sys.version_info[:2] >= (3, 6) ...@@ -19,6 +19,7 @@ PY36 = sys.version_info[:2] >= (3, 6)
PY37 = sys.version_info[:2] >= (3, 7) PY37 = sys.version_info[:2] >= (3, 7)
PY38 = sys.version_info[:2] >= (3, 8) PY38 = sys.version_info[:2] >= (3, 8)
PY39 = sys.version_info[:2] >= (3, 9) PY39 = sys.version_info[:2] >= (3, 9)
PY311 = sys.version_info[:2] >= (3, 11)
PYPY = hasattr(sys, 'pypy_version_info') PYPY = hasattr(sys, 'pypy_version_info')
WIN = sys.platform.startswith("win") WIN = sys.platform.startswith("win")
LINUX = sys.platform.startswith('linux') LINUX = sys.platform.startswith('linux')
......
...@@ -375,7 +375,7 @@ class Greenlet(greenlet): ...@@ -375,7 +375,7 @@ class Greenlet(greenlet):
@property @property
def loop(self): def loop(self):
# needed by killall # needed by killall
hub = get_my_hub(self) # type:SwitchOutGreenletWithLoop pylint:disable=undefined-variable hub = get_my_hub(self) # pylint:disable=undefined-variable
return hub.loop return hub.loop
def __nonzero__(self): def __nonzero__(self):
...@@ -619,7 +619,7 @@ class Greenlet(greenlet): ...@@ -619,7 +619,7 @@ class Greenlet(greenlet):
"""Schedule the greenlet to run in this loop iteration""" """Schedule the greenlet to run in this loop iteration"""
if self._start_event is None: if self._start_event is None:
_call_spawn_callbacks(self) _call_spawn_callbacks(self)
hub = get_my_hub(self) # type:SwitchOutGreenletWithLoop pylint:disable=undefined-variable hub = get_my_hub(self) # pylint:disable=undefined-variable
self._start_event = hub.loop.run_callback(self.switch) self._start_event = hub.loop.run_callback(self.switch)
def start_later(self, seconds): def start_later(self, seconds):
...@@ -1151,7 +1151,7 @@ def killall(greenlets, exception=GreenletExit, block=True, timeout=None): ...@@ -1151,7 +1151,7 @@ def killall(greenlets, exception=GreenletExit, block=True, timeout=None):
Now accepts raw greenlets created by :func:`gevent.spawn_raw`. Now accepts raw greenlets created by :func:`gevent.spawn_raw`.
""" """
need_killed = [] # type: list need_killed = []
for glet in greenlets: for glet in greenlets:
# Quick pass through to prevent any greenlet from # Quick pass through to prevent any greenlet from
# actually being switched to if it hasn't already. # actually being switched to if it hasn't already.
......
...@@ -14,6 +14,7 @@ as well as the constants from the :mod:`socket` module are imported into this mo ...@@ -14,6 +14,7 @@ as well as the constants from the :mod:`socket` module are imported into this mo
# pylint: disable=undefined-variable # pylint: disable=undefined-variable
from gevent._compat import PY3 from gevent._compat import PY3
from gevent._compat import PY311
from gevent._compat import exc_clear from gevent._compat import exc_clear
from gevent._util import copy_globals from gevent._util import copy_globals
...@@ -59,9 +60,9 @@ except AttributeError: ...@@ -59,9 +60,9 @@ except AttributeError:
_GLOBAL_DEFAULT_TIMEOUT = object() _GLOBAL_DEFAULT_TIMEOUT = object()
def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None): def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None, **kwargs):
""" """
create_connection(address, timeout=None, source_address=None) -> socket create_connection(address, timeout=None, source_address=None, *, all_errors=False) -> socket
Connect to *address* and return the :class:`gevent.socket.socket` Connect to *address* and return the :class:`gevent.socket.socket`
object. object.
...@@ -80,9 +81,18 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N ...@@ -80,9 +81,18 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N
If the host part of the address includes an IPv6 scope ID, If the host part of the address includes an IPv6 scope ID,
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:: NEXT
Add the *all_errors* argument. This only has meaning on Python 3.11;
it is a programming error to pass it on earlier versions.
""" """
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 = []
# getaddrinfo is documented as returning a list, but our interface # getaddrinfo is documented as returning a list, but our interface
# is pluggable, so be sure it does. # is pluggable, so be sure it does.
addrs = list(getaddrinfo(host, port, 0, SOCK_STREAM)) addrs = list(getaddrinfo(host, port, 0, SOCK_STREAM))
...@@ -99,12 +109,25 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N ...@@ -99,12 +109,25 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N
if source_address: if source_address:
sock.bind(source_address) sock.bind(source_address)
sock.connect(sa) sock.connect(sa)
except error:
except error as exc:
if not all_errors:
exceptions = [exc] # raise only the last error
else:
exceptions.append(exc)
del exc # cycle
if sock is not None: if sock is not None:
sock.close() sock.close()
sock = None sock = None
if res is addrs[-1]: if res is addrs[-1]:
raise if not all_errors:
del exceptions[:]
raise
try:
raise ExceptionGroup("create_connection failed", exceptions)
finally:
# Break explicitly a reference cycle
del exceptions[:]
# without exc_clear(), if connect() fails once, the socket # without exc_clear(), if connect() fails once, the socket
# is referenced by the frame in exc_info and the next # is referenced by the frame in exc_info and the next
# bind() fails (see test__socket.TestCreateConnection) # bind() fails (see test__socket.TestCreateConnection)
...@@ -122,6 +145,8 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N ...@@ -122,6 +145,8 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N
sock = None sock = None
raise raise
else: else:
# break reference cycles
del exceptions[:]
try: try:
return sock return sock
finally: finally:
......
...@@ -431,9 +431,12 @@ class TestCase(TestCaseMetaClass("NewBase", ...@@ -431,9 +431,12 @@ class TestCase(TestCaseMetaClass("NewBase",
if hasattr(sig, 'keywords'): # the old version if hasattr(sig, 'keywords'): # the old version
self.assertEqual(sig.keywords, gevent_sig.keywords, func_name) self.assertEqual(sig.keywords, gevent_sig.keywords, func_name)
else: else:
# The new hotness # The new hotness. Unfortunately, we can't actually check these things
self.assertEqual(sig.kwonlyargs, gevent_sig.kwonlyargs) # until we drop Python 2 support from the shared code. The only known place
self.assertEqual(sig.kwonlydefaults, gevent_sig.kwonlydefaults) # this is a problem is python 3.11 socket.create_connection(), which we manually
# ignore. So the checks all pass as is.
self.assertEqual(sig.kwonlyargs, gevent_sig.kwonlyargs, func_name)
self.assertEqual(sig.kwonlydefaults, gevent_sig.kwonlydefaults, func_name)
# Should deal with others: https://docs.python.org/3/library/inspect.html#inspect.getfullargspec # Should deal with others: https://docs.python.org/3/library/inspect.html#inspect.getfullargspec
def assertEqualFlakyRaceCondition(self, a, b): def assertEqualFlakyRaceCondition(self, a, b):
......
...@@ -581,6 +581,10 @@ class TestFunctions(greentest.TestCase): ...@@ -581,6 +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):
# Be careful not to exclude this on 3.12, etc, in case of
# more changes.
exclude.append('create_connection')
self.assertMonkeyPatchedFuncSignatures('socket', exclude=exclude) self.assertMonkeyPatchedFuncSignatures('socket', exclude=exclude)
def test_resolve_ipv6_scope_id(self): def test_resolve_ipv6_scope_id(self):
......
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