rdobuilder 18fbd6
From beb705719827843940ecebe48ef8b8ba65d2b91c Mon Sep 17 00:00:00 2001
f9c8e3
From: Tim Burke <tim.burke@gmail.com>
f9c8e3
Date: Wed, 9 Jun 2021 14:40:13 -0700
rdobuilder 18fbd6
Subject: [PATCH 1/6] Only wrap socket.timeout on Python < 3.10
f9c8e3
f9c8e3
On py310, socket.timeout is TimeoutError, which our is_timeout() helper
f9c8e3
func already knows is a timeout.
f9c8e3
f9c8e3
Note that this doesn't get us to py310 support (not by a long shot), but
f9c8e3
it's a step along the way.
f9c8e3
f9c8e3
Closes #687
f9c8e3
---
f9c8e3
 eventlet/greenio/base.py | 5 ++++-
f9c8e3
 1 file changed, 4 insertions(+), 1 deletion(-)
f9c8e3
f9c8e3
diff --git a/eventlet/greenio/base.py b/eventlet/greenio/base.py
f9c8e3
index 2eed86966..51a7ae13e 100644
f9c8e3
--- a/eventlet/greenio/base.py
f9c8e3
+++ b/eventlet/greenio/base.py
f9c8e3
@@ -29,7 +29,10 @@
f9c8e3
 _original_socket = eventlet.patcher.original('socket').socket
f9c8e3
 
f9c8e3
 
f9c8e3
-socket_timeout = eventlet.timeout.wrap_is_timeout(socket.timeout)
f9c8e3
+if sys.version_info >= (3, 10):
f9c8e3
+    socket_timeout = socket.timeout  # Really, TimeoutError
f9c8e3
+else:
f9c8e3
+    socket_timeout = eventlet.timeout.wrap_is_timeout(socket.timeout)
f9c8e3
 
f9c8e3
 
f9c8e3
 def socket_connect(descriptor, address):
f9c8e3
rdobuilder 18fbd6
From c22a27ace90a55d14e8c60ff37f9832e76266441 Mon Sep 17 00:00:00 2001
f9c8e3
From: Tim Burke <tim.burke@gmail.com>
f9c8e3
Date: Fri, 11 Jun 2021 12:56:07 -0700
rdobuilder 18fbd6
Subject: [PATCH 2/6] Get a working greenio._open on py310
f9c8e3
rdobuilder 18fbd6
_pyio.open is now a staticmethod, which means we can't get at the
rdobuilder 18fbd6
function code to wrap it up with a new environment.
rdobuilder 18fbd6
rdobuilder 18fbd6
Instead, create a new wrapper function which swaps out FileIO for
rdobuilder 18fbd6
GreenFileIO in the function's __globals__ before calling and replaces
rdobuilder 18fbd6
it in a finally.
f9c8e3
---
rdobuilder 18fbd6
 eventlet/greenio/py3.py | 30 +++++++++++++++++++++++-------
rdobuilder 18fbd6
 1 file changed, 23 insertions(+), 7 deletions(-)
f9c8e3
f9c8e3
diff --git a/eventlet/greenio/py3.py b/eventlet/greenio/py3.py
rdobuilder 18fbd6
index 7a75b52c0..c7f0aef50 100644
f9c8e3
--- a/eventlet/greenio/py3.py
f9c8e3
+++ b/eventlet/greenio/py3.py
rdobuilder 18fbd6
@@ -2,6 +2,7 @@
rdobuilder 18fbd6
 import errno
rdobuilder 18fbd6
 import os as _original_os
rdobuilder 18fbd6
 import socket as _original_socket
rdobuilder 18fbd6
+import sys
rdobuilder 18fbd6
 from io import (
rdobuilder 18fbd6
     BufferedRandom as _OriginalBufferedRandom,
rdobuilder 18fbd6
     BufferedReader as _OriginalBufferedReader,
rdobuilder 18fbd6
@@ -19,6 +20,7 @@
rdobuilder 18fbd6
     SOCKET_BLOCKING,
rdobuilder 18fbd6
 )
rdobuilder 18fbd6
 from eventlet.hubs import notify_close, notify_opened, IOClosed, trampoline
rdobuilder 18fbd6
+from eventlet.semaphore import Semaphore
rdobuilder 18fbd6
 from eventlet.support import get_errno
rdobuilder 18fbd6
 import six
rdobuilder 18fbd6
 
rdobuilder 18fbd6
@@ -182,21 +184,35 @@ def __exit__(self, *args):
rdobuilder 18fbd6
         self.close()
rdobuilder 18fbd6
 
rdobuilder 18fbd6
 
rdobuilder 18fbd6
-_open_environment = dict(globals())
rdobuilder 18fbd6
-_open_environment.update(dict(
rdobuilder 18fbd6
+_open_patching = dict(
rdobuilder 18fbd6
     BufferedRandom=_OriginalBufferedRandom,
rdobuilder 18fbd6
     BufferedWriter=_OriginalBufferedWriter,
rdobuilder 18fbd6
     BufferedReader=_OriginalBufferedReader,
rdobuilder 18fbd6
     TextIOWrapper=_OriginalTextIOWrapper,
f9c8e3
     FileIO=GreenFileIO,
f9c8e3
     os=_original_os,
rdobuilder 18fbd6
-))
rdobuilder 18fbd6
-
rdobuilder 18fbd6
-_open = FunctionType(
f9c8e3
-    six.get_function_code(_original_pyio.open),
rdobuilder 18fbd6
-    _open_environment,
f9c8e3
 )
f9c8e3
 
rdobuilder 18fbd6
+if sys.version_info < (3, 10):
rdobuilder 18fbd6
+    _open_environment = dict(globals())
rdobuilder 18fbd6
+    _open_environment.update(_open_patching)
rdobuilder 18fbd6
+    _open = FunctionType(
rdobuilder 18fbd6
+        six.get_function_code(_original_pyio.open),
rdobuilder 18fbd6
+        _open_environment,
rdobuilder 18fbd6
+    )
rdobuilder 18fbd6
+else:
rdobuilder 18fbd6
+    _open_lock = Semaphore()
rdobuilder 18fbd6
+    _open_originals = {k: _original_pyio.open.__func__.__globals__[k]
rdobuilder 18fbd6
+                       for k in _open_patching}
rdobuilder 18fbd6
+
rdobuilder 18fbd6
+    def _open(*a, **kw):
rdobuilder 18fbd6
+        with _open_lock:
rdobuilder 18fbd6
+            try:
rdobuilder 18fbd6
+                _original_pyio.open.__func__.__globals__.update(_open_patching)
rdobuilder 18fbd6
+                return _original_pyio.open(*a, **kw)
rdobuilder 18fbd6
+            finally:
rdobuilder 18fbd6
+                _original_pyio.open.__func__.__globals__.update(_open_originals)
rdobuilder 18fbd6
+
rdobuilder 18fbd6
 
rdobuilder 18fbd6
 def GreenPipe(name, mode="r", buffering=-1, encoding=None, errors=None,
rdobuilder 18fbd6
               newline=None, closefd=True, opener=None):
f9c8e3
rdobuilder 18fbd6
From ab4f8aaa704eb56b1e15730268fffbc8724c53ea Mon Sep 17 00:00:00 2001
f9c8e3
From: Tim Burke <tim.burke@gmail.com>
f9c8e3
Date: Fri, 11 Jun 2021 13:02:52 -0700
rdobuilder 18fbd6
Subject: [PATCH 3/6] Test using eventlet.is_timeout
f9c8e3
f9c8e3
...rather than requiring an is_timeout attribute on errors.
f9c8e3
f9c8e3
TimeoutErrors (which are covered by is_timeout) can't necessarily have
f9c8e3
attributes added to them.
f9c8e3
---
f9c8e3
 tests/__init__.py | 2 +-
f9c8e3
 1 file changed, 1 insertion(+), 1 deletion(-)
f9c8e3
f9c8e3
diff --git a/tests/__init__.py b/tests/__init__.py
f9c8e3
index c0b64fd9e..188366774 100644
f9c8e3
--- a/tests/__init__.py
f9c8e3
+++ b/tests/__init__.py
f9c8e3
@@ -383,7 +383,7 @@ def run_isolated(path, prefix='tests/isolated/', **kwargs):
f9c8e3
 
f9c8e3
 def check_is_timeout(obj):
f9c8e3
     value_text = getattr(obj, 'is_timeout', '(missing)')
f9c8e3
-    assert obj.is_timeout, 'type={0} str={1} .is_timeout={2}'.format(type(obj), str(obj), value_text)
f9c8e3
+    assert eventlet.is_timeout(obj), 'type={0} str={1} .is_timeout={2}'.format(type(obj), str(obj), value_text)
f9c8e3
 
f9c8e3
 
f9c8e3
 @contextlib.contextmanager
f9c8e3
rdobuilder 18fbd6
From ceef941b6b730add07eff4a3d4da5d203e255a71 Mon Sep 17 00:00:00 2001
f9c8e3
From: Tim Burke <tim.burke@gmail.com>
f9c8e3
Date: Fri, 11 Jun 2021 13:07:09 -0700
rdobuilder 18fbd6
Subject: [PATCH 4/6] Fix backdoor tests on py310
f9c8e3
f9c8e3
Python 3.10 started including build info on the version line, so the
f9c8e3
expectation in tests had to change. Also, start printing the banner as
f9c8e3
we read it to aid in future debugging.
f9c8e3
---
rdobuilder 18fbd6
 tests/backdoor_test.py | 17 +++++++++++++----
rdobuilder 18fbd6
 1 file changed, 13 insertions(+), 4 deletions(-)
f9c8e3
f9c8e3
diff --git a/tests/backdoor_test.py b/tests/backdoor_test.py
rdobuilder 18fbd6
index 03a569259..67a817947 100644
f9c8e3
--- a/tests/backdoor_test.py
f9c8e3
+++ b/tests/backdoor_test.py
f9c8e3
@@ -1,5 +1,6 @@
f9c8e3
 import os
f9c8e3
 import os.path
f9c8e3
+import sys
f9c8e3
 
f9c8e3
 import eventlet
f9c8e3
 
rdobuilder 18fbd6
@@ -21,10 +22,18 @@ def test_server(self):
rdobuilder 18fbd6
 
f9c8e3
     def _run_test_on_client_and_server(self, client, server_thread):
f9c8e3
         f = client.makefile('rw')
rdobuilder 18fbd6
-        assert 'Python' in f.readline()
f9c8e3
-        f.readline()  # build info
rdobuilder 18fbd6
-        f.readline()  # help info
rdobuilder 18fbd6
-        assert 'InteractiveConsole' in f.readline()
rdobuilder 18fbd6
+        line = f.readline()
rdobuilder 18fbd6
+        print(line.strip('\r\n'))
rdobuilder 18fbd6
+        assert 'Python' in line
f9c8e3
+        if sys.version_info < (3, 10):
f9c8e3
+            # Starting in py310, build info is included in version line
rdobuilder 18fbd6
+            line = f.readline()  # build info
rdobuilder 18fbd6
+            print(line.strip('\r\n'))
rdobuilder 18fbd6
+        line = f.readline()  # help info
rdobuilder 18fbd6
+        print(line.strip('\r\n'))
rdobuilder 18fbd6
+        line = f.readline()
rdobuilder 18fbd6
+        print(line.strip('\r\n'))
rdobuilder 18fbd6
+        assert 'InteractiveConsole' in line
f9c8e3
         self.assertEqual('>>> ', f.read(4))
rdobuilder 18fbd6
         f.write('print("hi")\n')
rdobuilder 18fbd6
         f.flush()
f9c8e3
rdobuilder 18fbd6
From aa6720a44cda816d1ac0a183ff94ebd51b6b1e53 Mon Sep 17 00:00:00 2001
f9c8e3
From: Tim Burke <tim.burke@gmail.com>
f9c8e3
Date: Fri, 11 Jun 2021 13:12:36 -0700
rdobuilder 18fbd6
Subject: [PATCH 5/6] Tolerate __builtins__ being a dict (rather than module)
f9c8e3
 in is_timeout
f9c8e3
f9c8e3
I'm still not sure how this happens, but somehow it does in
f9c8e3
socket_test.test_error_is_timeout. As a result, is_timeout wouldn't get
f9c8e3
a reference to TimeoutError, so the socket error would not be correctly
f9c8e3
identified as a timeout.
f9c8e3
---
rdobuilder 18fbd6
 eventlet/timeout.py | 5 ++++-
rdobuilder 18fbd6
 1 file changed, 4 insertions(+), 1 deletion(-)
f9c8e3
f9c8e3
diff --git a/eventlet/timeout.py b/eventlet/timeout.py
rdobuilder 18fbd6
index 6e1e08f63..969fdbcbd 100644
f9c8e3
--- a/eventlet/timeout.py
f9c8e3
+++ b/eventlet/timeout.py
rdobuilder 18fbd6
@@ -175,5 +175,8 @@ def fun(*args, **kwargs):
f9c8e3
 
f9c8e3
 
f9c8e3
 def is_timeout(obj):
f9c8e3
-    py3err = getattr(__builtins__, 'TimeoutError', Timeout)
rdobuilder 18fbd6
+    if isinstance(__builtins__, dict):  # seen when running tests on py310, but HOW??
rdobuilder 18fbd6
+        py3err = __builtins__.get('TimeoutError', Timeout)
rdobuilder 18fbd6
+    else:
rdobuilder 18fbd6
+        py3err = getattr(__builtins__, 'TimeoutError', Timeout)
rdobuilder 18fbd6
     return bool(getattr(obj, 'is_timeout', False)) or isinstance(obj, py3err)
rdobuilder 18fbd6
rdobuilder 18fbd6
From 900c8e195154efdae526ee7901469152141ab9d2 Mon Sep 17 00:00:00 2001
rdobuilder 18fbd6
From: Tim Burke <tim.burke@gmail.com>
rdobuilder 18fbd6
Date: Fri, 11 Jun 2021 13:33:00 -0700
rdobuilder 18fbd6
Subject: [PATCH 6/6] wsgi_test: Cap TLS version at 1.2
rdobuilder 18fbd6
rdobuilder 18fbd6
On py310, tests may attempt to use TLS 1.3 which apparently doesn't like
rdobuilder 18fbd6
the abrupt disconnect. Instead, it would complain:
rdobuilder 18fbd6
rdobuilder 18fbd6
    ssl.SSLEOFError: EOF occurred in violation of protocol
rdobuilder 18fbd6
---
rdobuilder 18fbd6
 tests/wsgi_test.py | 3 ++-
rdobuilder 18fbd6
 1 file changed, 2 insertions(+), 1 deletion(-)
rdobuilder 18fbd6
rdobuilder 18fbd6
diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py
rdobuilder 18fbd6
index 1dd754bba..4173bcefa 100644
rdobuilder 18fbd6
--- a/tests/wsgi_test.py
rdobuilder 18fbd6
+++ b/tests/wsgi_test.py
rdobuilder 18fbd6
@@ -579,7 +579,8 @@ def wsgi_app(environ, start_response):
rdobuilder 18fbd6
         sock = eventlet.wrap_ssl(
rdobuilder 18fbd6
             eventlet.listen(('localhost', 0)),
rdobuilder 18fbd6
             certfile=certificate_file, keyfile=private_key_file,
rdobuilder 18fbd6
-            server_side=True)
rdobuilder 18fbd6
+            server_side=True,
rdobuilder 18fbd6
+            ssl_version=ssl.PROTOCOL_TLSv1_2)
rdobuilder 18fbd6
         server_coro = eventlet.spawn(server, sock, wsgi_app, self.logfile)
rdobuilder 18fbd6
 
rdobuilder 18fbd6
         client = eventlet.connect(('localhost', sock.getsockname()[1]))