diff --git a/0001-import-MutableMapping-from-collections.abc.patch b/0001-import-MutableMapping-from-collections.abc.patch new file mode 100644 index 0000000..942b3c0 --- /dev/null +++ b/0001-import-MutableMapping-from-collections.abc.patch @@ -0,0 +1,31 @@ +From e88a14f86390b22ee55a810d0cd1b68c32c54423 Mon Sep 17 00:00:00 2001 +From: Tomas Hrnciar +Date: Wed, 16 Jun 2021 13:29:20 +0200 +Subject: [PATCH] import MutableMapping from collections.abc + +--- + dns/namedict.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dnspython-1.16.0/dns/namedict.py b/dnspython-1.16.0/dns/namedict.py +index 37a1310..ae8d6a4 100644 +--- a/dns/namedict.py ++++ b/dns/namedict.py +@@ -27,12 +27,12 @@ + + """DNS name dictionary""" + +-import collections ++from collections.abc import MutableMapping + import dns.name + from ._compat import xrange + + +-class NameDict(collections.MutableMapping): ++class NameDict(MutableMapping): + """A dictionary whose keys are dns.name.Name objects. + + In addition to being like a regular Python dictionary, this +-- +2.31.1 + diff --git a/715.patch b/715.patch new file mode 100644 index 0000000..874d896 --- /dev/null +++ b/715.patch @@ -0,0 +1,242 @@ +From beb705719827843940ecebe48ef8b8ba65d2b91c Mon Sep 17 00:00:00 2001 +From: Tim Burke +Date: Wed, 9 Jun 2021 14:40:13 -0700 +Subject: [PATCH 1/6] Only wrap socket.timeout on Python < 3.10 + +On py310, socket.timeout is TimeoutError, which our is_timeout() helper +func already knows is a timeout. + +Note that this doesn't get us to py310 support (not by a long shot), but +it's a step along the way. + +Closes #687 +--- + eventlet/greenio/base.py | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/eventlet/greenio/base.py b/eventlet/greenio/base.py +index 2eed86966..51a7ae13e 100644 +--- a/eventlet/greenio/base.py ++++ b/eventlet/greenio/base.py +@@ -29,7 +29,10 @@ + _original_socket = eventlet.patcher.original('socket').socket + + +-socket_timeout = eventlet.timeout.wrap_is_timeout(socket.timeout) ++if sys.version_info >= (3, 10): ++ socket_timeout = socket.timeout # Really, TimeoutError ++else: ++ socket_timeout = eventlet.timeout.wrap_is_timeout(socket.timeout) + + + def socket_connect(descriptor, address): + +From c22a27ace90a55d14e8c60ff37f9832e76266441 Mon Sep 17 00:00:00 2001 +From: Tim Burke +Date: Fri, 11 Jun 2021 12:56:07 -0700 +Subject: [PATCH 2/6] Get a working greenio._open on py310 + +_pyio.open is now a staticmethod, which means we can't get at the +function code to wrap it up with a new environment. + +Instead, create a new wrapper function which swaps out FileIO for +GreenFileIO in the function's __globals__ before calling and replaces +it in a finally. +--- + eventlet/greenio/py3.py | 30 +++++++++++++++++++++++------- + 1 file changed, 23 insertions(+), 7 deletions(-) + +diff --git a/eventlet/greenio/py3.py b/eventlet/greenio/py3.py +index 7a75b52c0..c7f0aef50 100644 +--- a/eventlet/greenio/py3.py ++++ b/eventlet/greenio/py3.py +@@ -2,6 +2,7 @@ + import errno + import os as _original_os + import socket as _original_socket ++import sys + from io import ( + BufferedRandom as _OriginalBufferedRandom, + BufferedReader as _OriginalBufferedReader, +@@ -19,6 +20,7 @@ + SOCKET_BLOCKING, + ) + from eventlet.hubs import notify_close, notify_opened, IOClosed, trampoline ++from eventlet.semaphore import Semaphore + from eventlet.support import get_errno + import six + +@@ -182,21 +184,35 @@ def __exit__(self, *args): + self.close() + + +-_open_environment = dict(globals()) +-_open_environment.update(dict( ++_open_patching = dict( + BufferedRandom=_OriginalBufferedRandom, + BufferedWriter=_OriginalBufferedWriter, + BufferedReader=_OriginalBufferedReader, + TextIOWrapper=_OriginalTextIOWrapper, + FileIO=GreenFileIO, + os=_original_os, +-)) +- +-_open = FunctionType( +- six.get_function_code(_original_pyio.open), +- _open_environment, + ) + ++if sys.version_info < (3, 10): ++ _open_environment = dict(globals()) ++ _open_environment.update(_open_patching) ++ _open = FunctionType( ++ six.get_function_code(_original_pyio.open), ++ _open_environment, ++ ) ++else: ++ _open_lock = Semaphore() ++ _open_originals = {k: _original_pyio.open.__func__.__globals__[k] ++ for k in _open_patching} ++ ++ def _open(*a, **kw): ++ with _open_lock: ++ try: ++ _original_pyio.open.__func__.__globals__.update(_open_patching) ++ return _original_pyio.open(*a, **kw) ++ finally: ++ _original_pyio.open.__func__.__globals__.update(_open_originals) ++ + + def GreenPipe(name, mode="r", buffering=-1, encoding=None, errors=None, + newline=None, closefd=True, opener=None): + +From ab4f8aaa704eb56b1e15730268fffbc8724c53ea Mon Sep 17 00:00:00 2001 +From: Tim Burke +Date: Fri, 11 Jun 2021 13:02:52 -0700 +Subject: [PATCH 3/6] Test using eventlet.is_timeout + +...rather than requiring an is_timeout attribute on errors. + +TimeoutErrors (which are covered by is_timeout) can't necessarily have +attributes added to them. +--- + tests/__init__.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/__init__.py b/tests/__init__.py +index c0b64fd9e..188366774 100644 +--- a/tests/__init__.py ++++ b/tests/__init__.py +@@ -383,7 +383,7 @@ def run_isolated(path, prefix='tests/isolated/', **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) ++ assert eventlet.is_timeout(obj), 'type={0} str={1} .is_timeout={2}'.format(type(obj), str(obj), value_text) + + + @contextlib.contextmanager + +From ceef941b6b730add07eff4a3d4da5d203e255a71 Mon Sep 17 00:00:00 2001 +From: Tim Burke +Date: Fri, 11 Jun 2021 13:07:09 -0700 +Subject: [PATCH 4/6] Fix backdoor tests on py310 + +Python 3.10 started including build info on the version line, so the +expectation in tests had to change. Also, start printing the banner as +we read it to aid in future debugging. +--- + tests/backdoor_test.py | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/tests/backdoor_test.py b/tests/backdoor_test.py +index 03a569259..67a817947 100644 +--- a/tests/backdoor_test.py ++++ b/tests/backdoor_test.py +@@ -1,5 +1,6 @@ + import os + import os.path ++import sys + + import eventlet + +@@ -21,10 +22,18 @@ def test_server(self): + + def _run_test_on_client_and_server(self, client, server_thread): + f = client.makefile('rw') +- assert 'Python' in f.readline() +- f.readline() # build info +- f.readline() # help info +- assert 'InteractiveConsole' in f.readline() ++ line = f.readline() ++ print(line.strip('\r\n')) ++ assert 'Python' in line ++ if sys.version_info < (3, 10): ++ # Starting in py310, build info is included in version line ++ line = f.readline() # build info ++ print(line.strip('\r\n')) ++ line = f.readline() # help info ++ print(line.strip('\r\n')) ++ line = f.readline() ++ print(line.strip('\r\n')) ++ assert 'InteractiveConsole' in line + self.assertEqual('>>> ', f.read(4)) + f.write('print("hi")\n') + f.flush() + +From aa6720a44cda816d1ac0a183ff94ebd51b6b1e53 Mon Sep 17 00:00:00 2001 +From: Tim Burke +Date: Fri, 11 Jun 2021 13:12:36 -0700 +Subject: [PATCH 5/6] Tolerate __builtins__ being a dict (rather than module) + in is_timeout + +I'm still not sure how this happens, but somehow it does in +socket_test.test_error_is_timeout. As a result, is_timeout wouldn't get +a reference to TimeoutError, so the socket error would not be correctly +identified as a timeout. +--- + eventlet/timeout.py | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/eventlet/timeout.py b/eventlet/timeout.py +index 6e1e08f63..969fdbcbd 100644 +--- a/eventlet/timeout.py ++++ b/eventlet/timeout.py +@@ -175,5 +175,8 @@ def fun(*args, **kwargs): + + + def is_timeout(obj): +- py3err = getattr(__builtins__, 'TimeoutError', Timeout) ++ if isinstance(__builtins__, dict): # seen when running tests on py310, but HOW?? ++ py3err = __builtins__.get('TimeoutError', Timeout) ++ else: ++ py3err = getattr(__builtins__, 'TimeoutError', Timeout) + return bool(getattr(obj, 'is_timeout', False)) or isinstance(obj, py3err) + +From 900c8e195154efdae526ee7901469152141ab9d2 Mon Sep 17 00:00:00 2001 +From: Tim Burke +Date: Fri, 11 Jun 2021 13:33:00 -0700 +Subject: [PATCH 6/6] wsgi_test: Cap TLS version at 1.2 + +On py310, tests may attempt to use TLS 1.3 which apparently doesn't like +the abrupt disconnect. Instead, it would complain: + + ssl.SSLEOFError: EOF occurred in violation of protocol +--- + tests/wsgi_test.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py +index 1dd754bba..4173bcefa 100644 +--- a/tests/wsgi_test.py ++++ b/tests/wsgi_test.py +@@ -579,7 +579,8 @@ def wsgi_app(environ, start_response): + sock = eventlet.wrap_ssl( + eventlet.listen(('localhost', 0)), + certfile=certificate_file, keyfile=private_key_file, +- server_side=True) ++ server_side=True, ++ ssl_version=ssl.PROTOCOL_TLSv1_2) + server_coro = eventlet.spawn(server, sock, wsgi_app, self.logfile) + + client = eventlet.connect(('localhost', sock.getsockname()[1])) diff --git a/python-eventlet.spec b/python-eventlet.spec index 5584a46..297f911 100644 --- a/python-eventlet.spec +++ b/python-eventlet.spec @@ -4,7 +4,7 @@ Name: python-%{modname} Version: 0.31.0 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Highly concurrent networking library %if %bundle_dns License: MIT and ISC @@ -17,6 +17,13 @@ Source0: https://github.com/eventlet/%{modname}/archive/v%{version}.zip Source1: %{pypi_source dnspython 1.16.0 zip} Patch0: switch_to_python_cryptography.patch +# Since Python 3.10 MutableMapping is available in collections.abc instead of collections. +# This is already fixed in dnspython 2.0.0+, but eventlet still uses old 1.16.0. +Patch1: 0001-import-MutableMapping-from-collections.abc.patch +# Backport of upstream PR to introduce compatibility with Python 3.10 +# https://github.com/eventlet/eventlet/pull/715 +Patch715: https://github.com/eventlet/eventlet/pull/715.patch + BuildArch: noarch %description @@ -68,6 +75,7 @@ BuildRequires: python3-zmq %else %setup -n %{modname}-%{version} -q %endif +%patch -P 715 -p1 # Remove dependency on enum-compat from setup.py # enum-compat is not needed for Python 3 sed -i "/'enum-compat',/d" setup.py @@ -75,6 +83,7 @@ sed -i "/'enum-compat',/d" setup.py # We bundle last version of dns1 as eventlet does not support yet dns2 pushd dnspython-1.16.0 %patch -P 0 -p1 +%patch -P 1 -p1 grep -lRZ "dns\." dns | xargs -0 -l sed -i -e 's/\([^[a-zA-Z]\)dns\./\1eventlet\.dns\./g' grep -lRZ "^import dns$" dns | xargs -0 -l sed -i -e 's/^import\ dns$/import\ eventlet\.dns/' popd @@ -121,6 +130,10 @@ nosetests-%{python3_version} -v %doc html-3 %changelog +* Wed Jun 16 2021 Tomas Hrnciar - 0.31.0-3 +- Backport upstream patch to add compatibility of Eventlet with Python 3.10 +- Fixes: rhbz#1913291 + * Fri Jun 04 2021 Python Maint - 0.31.0-2 - Rebuilt for Python 3.10