Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-eventlet for openSUSE:Factory 
checked in at 2021-12-23 17:53:27
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-eventlet (Old)
 and      /work/SRC/openSUSE:Factory/.python-eventlet.new.2520 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-eventlet"

Thu Dec 23 17:53:27 2021 rev:39 rq:942015 version:0.33.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-eventlet/python-eventlet.changes  
2021-11-21 23:51:45.814333891 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-eventlet.new.2520/python-eventlet.changes    
    2021-12-23 17:53:29.835709737 +0100
@@ -1,0 +2,15 @@
+Thu Dec 16 20:11:12 UTC 2021 - Ben Greiner <c...@bnavigator.de>
+
+- update to 0.33.0
+  * green.thread: unlocked Lock().release() should raise exception,
+    returned True
+  * wsgi: Don???t break HTTP framing during 100-continue handling
+  * Python 3.10 partial support
+  * greendns: Create a DNS resolver lazily rather than on import
+  * ssl: GreenSSLContext minimum_version and maximum_version
+    setters
+- Refresh denosing patches: merge remove_nose.patch and
+  remove_nose_part_2.patch into denose-eventlet.patch
+- Refresh newdnspython.patch
+
+-------------------------------------------------------------------

Old:
----
  eventlet-0.32.0.tar.gz
  remove_nose.patch
  remove_nose_part_2.patch

New:
----
  denose-eventlet.patch
  eventlet-0.33.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-eventlet.spec ++++++
--- /var/tmp/diff_new_pack.Hx8NKw/_old  2021-12-23 17:53:31.279710632 +0100
+++ /var/tmp/diff_new_pack.Hx8NKw/_new  2021-12-23 17:53:31.283710635 +0100
@@ -17,8 +17,9 @@
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
+%bcond_without python2
 Name:           python-eventlet
-Version:        0.32.0
+Version:        0.33.0
 Release:        0
 Summary:        Concurrent networking library for Python
 License:        MIT
@@ -27,13 +28,11 @@
 Source:         
https://files.pythonhosted.org/packages/source/e/eventlet/eventlet-%{version}.tar.gz
 # PATCH-FEATURE-UPSTREAM remove_nose.patch gh#eventlet/eventlet#638 
mc...@suse.com
 # Removes dependency on nose
-Patch0:         remove_nose.patch
+Patch0:         denose-eventlet.patch
 # PATCH-FIX-UPSTREAM newdnspython.patch mc...@suse.com -- patch is from 
gh#rthalley/dnspython#519, discussion in gh#eventlet/eventlet#638
 Patch1:         newdnspython.patch
-# Really remove the dependency on nose
-Patch3:         remove_nose_part_2.patch
 BuildRequires:  %{python_module setuptools}
-%if 0%{?suse_version} < 1550
+%if %{with python2}
 BuildRequires:  python2-monotonic >= 1.4
 %endif
 BuildRequires:  fdupes
@@ -95,6 +94,8 @@
 skiptests+=" or test_dns_methods_are_green or test_noraise_dns_tcp"
 # These are flaky inside the OBS environment
 skiptests+=" or test_fork_after_monkey_patch or test_send_1k_req_rep or 
test_cpu_usage_after_bind"
+# tracebacks in denosed suite with pytest inside obs presumably work different 
than when upstream is running nose?
+skiptests+=" or test_leakage_from_tracebacks"
 
 # Unknown Python 3.6 specific errors
 # TypeError: _wrap_socket() argument 1 must be _socket.socket, not SSLSocket
@@ -104,6 +105,10 @@
 %if %python3_version_nodots == 36
 python3_skiptests+="$python36_skiptests"
 %endif
+# https://github.com/eventlet/eventlet/issues/730
+python310_skiptests+=" or test_patcher_existing_locks_locked"
+# https://github.com/eventlet/eventlet/issues/739
+python310_skiptests+=" or test_017_ssl_zeroreturnerror"
 # no subdir recursion 
https://github.com/eventlet/eventlet/issues/638#issuecomment-676085599
 %pytest -o norecursedirs="tests/*" -k "not ($skiptests ${$python_skiptests})" 
${$python_pytest_param}
 

++++++ denose-eventlet.patch ++++++
Index: eventlet-0.33.0/setup.py
===================================================================
--- eventlet-0.33.0.orig/setup.py
+++ eventlet-0.33.0/setup.py
@@ -27,7 +27,7 @@ setuptools.setup(
             'README.rst'
         )
     ).read(),
-    test_suite='nose.collector',
+    test_suite='tests',
     classifiers=[
         "Development Status :: 4 - Beta",
         "Intended Audience :: Developers",
Index: eventlet-0.33.0/eventlet.egg-info/SOURCES.txt
===================================================================
--- eventlet-0.33.0.orig/eventlet.egg-info/SOURCES.txt
+++ eventlet-0.33.0/eventlet.egg-info/SOURCES.txt
@@ -175,7 +175,6 @@ tests/greenthread_test.py
 tests/hub_test.py
 tests/mock.py
 tests/mysqldb_test.py
-tests/nosewrapper.py
 tests/openssl_test.py
 tests/os_test.py
 tests/parse_results.py
@@ -275,4 +274,4 @@ tests/stdlib/test_threading_local.py
 tests/stdlib/test_timeout.py
 tests/stdlib/test_urllib.py
 tests/stdlib/test_urllib2.py
-tests/stdlib/test_urllib2_localnet.py
\ No newline at end of file
+tests/stdlib/test_urllib2_localnet.py
Index: eventlet-0.33.0/tests/greenio_test.py
===================================================================
--- eventlet-0.33.0.orig/tests/greenio_test.py
+++ eventlet-0.33.0/tests/greenio_test.py
@@ -9,8 +9,6 @@ import socket as _orig_sock
 import sys
 import tempfile
 
-from nose.tools import eq_
-
 import eventlet
 from eventlet import event, greenio, debug
 from eventlet.hubs import get_hub
@@ -39,7 +37,7 @@ def expect_socket_timeout(function, *arg
         raise AssertionError("socket.timeout not raised")
     except socket.timeout as e:
         assert hasattr(e, 'args')
-        eq_(e.args[0], 'timed out')
+        assert e.args[0] == 'timed out'
 
 
 def min_buf_size():
@@ -674,8 +672,8 @@ class TestGreenSocket(tests.LimitedTestC
         sender.sendto(b'second', 0, address)
 
         sender_address = ('127.0.0.1', sender.getsockname()[1])
-        eq_(receiver.recvfrom(1024), (b'first', sender_address))
-        eq_(receiver.recvfrom(1024), (b'second', sender_address))
+        assert receiver.recvfrom(1024) == (b'first', sender_address)
+        assert receiver.recvfrom(1024) == (b'second', sender_address)
 
 
 def test_get_fileno_of_a_socket_works():
Index: eventlet-0.33.0/tests/nosewrapper.py
===================================================================
--- eventlet-0.33.0.orig/tests/nosewrapper.py
+++ eventlet-0.33.0/tests/nosewrapper.py
@@ -1,20 +1,13 @@
 """ This script simply gets the paths correct for testing eventlet with the
 hub extension for Nose."""
-import nose
 from os.path import dirname, realpath, abspath
 import sys
+import unittest
 
 
 parent_dir = dirname(dirname(realpath(abspath(__file__))))
 if parent_dir not in sys.path:
     sys.path.insert(0, parent_dir)
 
-# hudson does a better job printing the test results if the exit value is 0
-zero_status = '--force-zero-status'
-if zero_status in sys.argv:
-    sys.argv.remove(zero_status)
-    launch = nose.run
-else:
-    launch = nose.main
-
-launch(argv=sys.argv)
+if __name__ == '__main__':
+    unittest.main()
Index: eventlet-0.33.0/tests/__init__.py
===================================================================
--- eventlet-0.33.0.orig/tests/__init__.py
+++ eventlet-0.33.0/tests/__init__.py
@@ -20,7 +20,7 @@ import sys
 import unittest
 import warnings
 
-from nose.plugins.skip import SkipTest
+from unittest import SkipTest
 
 import eventlet
 from eventlet import tpool
@@ -223,7 +223,6 @@ class LimitedTestCase(unittest.TestCase)
 def check_idle_cpu_usage(duration, allowed_part):
     if resource is None:
         # TODO: use https://code.google.com/p/psutil/
-        from nose.plugins.skip import SkipTest
         raise SkipTest('CPU usage testing not supported (`import resource` 
failed)')
 
     r1 = resource.getrusage(resource.RUSAGE_SELF)
Index: eventlet-0.33.0/tests/dagpool_test.py
===================================================================
--- eventlet-0.33.0.orig/tests/dagpool_test.py
+++ eventlet-0.33.0/tests/dagpool_test.py
@@ -5,7 +5,6 @@
 @brief  Test DAGPool class
 """
 
-from nose.tools import *
 import eventlet
 from eventlet.dagpool import DAGPool, Collision, PropagateError
 import six
@@ -13,8 +12,8 @@ from contextlib import contextmanager
 import itertools
 
 
-# Not all versions of nose.tools.assert_raises() support the usage in this
-# module, but it's straightforward enough to code that explicitly.
+# Not all versions of assert_raises() support the usage in this module,
+# but it's straightforward enough to code that explicitly.
 @contextmanager
 def assert_raises(exc):
     """exc is an exception class"""
@@ -163,7 +162,7 @@ class Capture(object):
                     # a set. Make a set containing its elements.
                     setlist.append(set(subseq))
         # Now that we've massaged 'sequence' into 'setlist', compare.
-        assert_equal(self.sequence, setlist)
+        assert self.sequence == setlist
 
 
 # ****************************************************************************
@@ -191,14 +190,14 @@ def test_init():
         with check_no_suspend():
             results = pool.waitall()
         # with no spawn() or post(), waitall() returns preload data
-        assert_equals(results, dict(a=1, b=2, c=3))
+        assert results == dict(a=1, b=2, c=3)
 
         # preload sequence of pairs
         pool = DAGPool([("d", 4), ("e", 5), ("f", 6)])
         # this must not hang
         with check_no_suspend():
             results = pool.waitall()
-        assert_equals(results, dict(d=4, e=5, f=6))
+        assert results == dict(d=4, e=5, f=6)
 
 
 def test_wait_each_empty():
@@ -216,10 +215,10 @@ def test_wait_each_preload():
         with check_no_suspend():
             # wait_each() may deliver in arbitrary order; collect into a dict
             # for comparison
-            assert_equals(dict(pool.wait_each("abc")), dict(a=1, b=2, c=3))
+            assert dict(pool.wait_each("abc")) == dict(a=1, b=2, c=3)
 
             # while we're at it, test wait() for preloaded keys
-            assert_equals(pool.wait("bc"), dict(b=2, c=3))
+            assert pool.wait("bc") == dict(b=2, c=3)
 
 
 def post_each(pool, capture):
@@ -257,7 +256,7 @@ def test_wait_posted():
     eventlet.spawn(post_each, pool, capture)
     gotten = pool.wait("bcdefg")
     capture.add("got all")
-    assert_equals(gotten,
+    assert (gotten ==
                   dict(b=2, c=3,
                        d="dval", e="eval",
                        f="fval", g="gval"))
@@ -285,7 +284,7 @@ def test_spawn_collision_spawn():
     pool = DAGPool()
     pool.spawn("a", (), lambda key, results: "aval")
     # hasn't yet even started
-    assert_equals(pool.get("a"), None)
+    assert pool.get("a") == None
     with assert_raises(Collision):
         # Attempting to spawn again with same key should collide even if the
         # first spawned greenthread hasn't yet had a chance to run.
@@ -293,7 +292,7 @@ def test_spawn_collision_spawn():
     # now let the spawned eventlet run
     eventlet.sleep(0)
     # should have finished
-    assert_equals(pool.get("a"), "aval")
+    assert pool.get("a") == "aval"
     with assert_raises(Collision):
         # Attempting to spawn with same key collides even when the greenthread
         # has completed.
@@ -324,60 +323,60 @@ def test_spawn_multiple():
     capture.step()
     # but none of them has yet produced a result
     for k in "defgh":
-        assert_equals(pool.get(k), None)
-    assert_equals(set(pool.keys()), set("abc"))
-    assert_equals(dict(pool.items()), dict(a=1, b=2, c=3))
-    assert_equals(pool.running(), 5)
-    assert_equals(set(pool.running_keys()), set("defgh"))
-    assert_equals(pool.waiting(), 1)
-    assert_equals(pool.waiting_for(), dict(h=set("defg")))
-    assert_equals(pool.waiting_for("d"), set())
-    assert_equals(pool.waiting_for("c"), set())
+        assert pool.get(k) == None
+    assert set(pool.keys()) == set("abc")
+    assert dict(pool.items()) == dict(a=1, b=2, c=3)
+    assert pool.running() == 5
+    assert set(pool.running_keys()) == set("defgh")
+    assert pool.waiting() == 1
+    assert pool.waiting_for() == dict(h=set("defg"))
+    assert pool.waiting_for("d") == set()
+    assert pool.waiting_for("c") == set()
     with assert_raises(KeyError):
         pool.waiting_for("j")
-    assert_equals(pool.waiting_for("h"), set("defg"))
+    assert pool.waiting_for("h") == set("defg")
 
     # let one of the upstream greenthreads complete
     events["f"].send("fval")
     spin()
     capture.step()
-    assert_equals(pool.get("f"), "fval")
-    assert_equals(set(pool.keys()), set("abcf"))
-    assert_equals(dict(pool.items()), dict(a=1, b=2, c=3, f="fval"))
-    assert_equals(pool.running(), 4)
-    assert_equals(set(pool.running_keys()), set("degh"))
-    assert_equals(pool.waiting(), 1)
-    assert_equals(pool.waiting_for("h"), set("deg"))
+    assert pool.get("f") == "fval"
+    assert set(pool.keys()) == set("abcf")
+    assert dict(pool.items()) == dict(a=1, b=2, c=3, f="fval")
+    assert pool.running() == 4
+    assert set(pool.running_keys()) == set("degh")
+    assert pool.waiting() == 1
+    assert pool.waiting_for("h") == set("deg")
 
     # now two others
     events["e"].send("eval")
     events["g"].send("gval")
     spin()
     capture.step()
-    assert_equals(pool.get("e"), "eval")
-    assert_equals(pool.get("g"), "gval")
-    assert_equals(set(pool.keys()), set("abcefg"))
-    assert_equals(dict(pool.items()),
+    assert pool.get("e") == "eval"
+    assert pool.get("g") == "gval"
+    assert set(pool.keys()) == set("abcefg")
+    assert (dict(pool.items()) ==
                   dict(a=1, b=2, c=3, e="eval", f="fval", g="gval"))
-    assert_equals(pool.running(), 2)
-    assert_equals(set(pool.running_keys()), set("dh"))
-    assert_equals(pool.waiting(), 1)
-    assert_equals(pool.waiting_for("h"), set("d"))
+    assert pool.running() == 2
+    assert set(pool.running_keys()) == set("dh")
+    assert pool.waiting() == 1
+    assert pool.waiting_for("h") == set("d")
 
     # last one
     events["d"].send("dval")
     # make sure both pool greenthreads get a chance to run
     spin()
     capture.step()
-    assert_equals(pool.get("d"), "dval")
-    assert_equals(set(pool.keys()), set("abcdefgh"))
-    assert_equals(dict(pool.items()),
+    assert pool.get("d") == "dval"
+    assert set(pool.keys()) == set("abcdefgh")
+    assert (dict(pool.items()) ==
                   dict(a=1, b=2, c=3,
                        d="dval", e="eval", f="fval", g="gval", h="hval"))
-    assert_equals(pool.running(), 0)
-    assert_false(pool.running_keys())
-    assert_equals(pool.waiting(), 0)
-    assert_equals(pool.waiting_for("h"), set())
+    assert pool.running() == 0
+    assert not pool.running_keys()
+    assert pool.waiting() == 0
+    assert pool.waiting_for("h") == set()
 
     capture.validate([
         ["h got b", "h got c"],
@@ -432,13 +431,13 @@ def test_spawn_many():
     spin()
     # verify that e completed (also that post(key) within greenthread
     # overrides implicit post of return value, which would be None)
-    assert_equals(pool.get("e"), "e")
+    assert pool.get("e") == "e"
 
     # With the dependency graph shown above, it is not guaranteed whether b or
     # c will complete first. Handle either case.
     sequence = capture.sequence[:]
     sequence[1:3] = [set([sequence[1].pop(), sequence[2].pop()])]
-    assert_equals(sequence,
+    assert (sequence ==
                   [set(["a done"]),
                    set(["b done", "c done"]),
                    set(["d done"]),
@@ -466,7 +465,7 @@ def test_wait_each_all():
     for pos in range(len(keys)):
         # next value from wait_each()
         k, v = next(each)
-        assert_equals(k, keys[pos])
+        assert k == keys[pos]
         # advance every pool greenlet as far as it can go
         spin()
         # everything from keys[:pos+1] should have a value by now
@@ -494,7 +493,7 @@ def test_kill():
     pool.kill("a")
     # didn't run
     spin()
-    assert_equals(pool.get("a"), None)
+    assert pool.get("a") == None
     # killing it forgets about it
     with assert_raises(KeyError):
         pool.kill("a")
@@ -505,7 +504,7 @@ def test_kill():
     with assert_raises(KeyError):
         pool.kill("a")
     # verify it ran to completion
-    assert_equals(pool.get("a"), 2)
+    assert pool.get("a") == 2
 
 
 def test_post_collision_preload():
@@ -533,7 +532,7 @@ def test_post_collision_spawn():
     pool.kill("a")
     # now we can post
     pool.post("a", 3)
-    assert_equals(pool.get("a"), 3)
+    assert pool.get("a") == 3
 
     pool = DAGPool()
     pool.spawn("a", (), lambda key, result: 4)
@@ -553,10 +552,10 @@ def test_post_replace():
     pool = DAGPool()
     pool.post("a", 1)
     pool.post("a", 2, replace=True)
-    assert_equals(pool.get("a"), 2)
-    assert_equals(dict(pool.wait_each("a")), dict(a=2))
-    assert_equals(pool.wait("a"), dict(a=2))
-    assert_equals(pool["a"], 2)
+    assert pool.get("a") == 2
+    assert dict(pool.wait_each("a")) == dict(a=2)
+    assert pool.wait("a") == dict(a=2)
+    assert pool["a"] == 2
 
 
 def waitfor(capture, pool, key):
@@ -598,14 +597,14 @@ def test_waitall_exc():
     try:
         pool.waitall()
     except PropagateError as err:
-        assert_equals(err.key, "a")
+        assert err.key == "a"
         assert isinstance(err.exc, BogusError), \
             "exc attribute is {0}, not BogusError".format(err.exc)
-        assert_equals(str(err.exc), "bogus")
+        assert str(err.exc) == "bogus"
         msg = str(err)
-        assert_in("PropagateError(a)", msg)
-        assert_in("BogusError", msg)
-        assert_in("bogus", msg)
+        assert "PropagateError(a)" in msg
+        assert "BogusError" in msg
+        assert "bogus" in msg
 
 
 def test_propagate_exc():
@@ -616,20 +615,20 @@ def test_propagate_exc():
     try:
         pool["c"]
     except PropagateError as errc:
-        assert_equals(errc.key, "c")
+        assert errc.key == "c"
         errb = errc.exc
-        assert_equals(errb.key, "b")
+        assert errb.key == "b"
         erra = errb.exc
-        assert_equals(erra.key, "a")
+        assert erra.key == "a"
         assert isinstance(erra.exc, BogusError), \
             "exc attribute is {0}, not BogusError".format(erra.exc)
-        assert_equals(str(erra.exc), "bogus")
+        assert str(erra.exc) == "bogus"
         msg = str(errc)
-        assert_in("PropagateError(a)", msg)
-        assert_in("PropagateError(b)", msg)
-        assert_in("PropagateError(c)", msg)
-        assert_in("BogusError", msg)
-        assert_in("bogus", msg)
+        assert "PropagateError(a)" in msg
+        assert "PropagateError(b)" in msg
+        assert "PropagateError(c)" in msg
+        assert "BogusError" in msg
+        assert "bogus" in msg
 
 
 def test_wait_each_exc():
@@ -681,13 +680,13 @@ def test_post_get_exc():
             pass
 
     # wait_each_success() filters
-    assert_equals(dict(pool.wait_each_success()), dict(a=bogua))
-    assert_equals(dict(pool.wait_each_success("ab")), dict(a=bogua))
-    assert_equals(dict(pool.wait_each_success("a")), dict(a=bogua))
-    assert_equals(dict(pool.wait_each_success("b")), {})
+    assert dict(pool.wait_each_success()) == dict(a=bogua)
+    assert dict(pool.wait_each_success("ab")) == dict(a=bogua)
+    assert dict(pool.wait_each_success("a")) == dict(a=bogua)
+    assert dict(pool.wait_each_success("b")) == {}
 
     # wait_each_exception() filters the other way
-    assert_equals(dict(pool.wait_each_exception()), dict(b=bogub))
-    assert_equals(dict(pool.wait_each_exception("ab")), dict(b=bogub))
-    assert_equals(dict(pool.wait_each_exception("a")), {})
-    assert_equals(dict(pool.wait_each_exception("b")), dict(b=bogub))
+    assert dict(pool.wait_each_exception()) == dict(b=bogub)
+    assert dict(pool.wait_each_exception("ab")) == dict(b=bogub)
+    assert dict(pool.wait_each_exception("a")) == {}
+    assert dict(pool.wait_each_exception("b")) == dict(b=bogub)

++++++ eventlet-0.32.0.tar.gz -> eventlet-0.33.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/NEWS new/eventlet-0.33.0/NEWS
--- old/eventlet-0.32.0/NEWS    2021-09-01 12:49:47.000000000 +0200
+++ new/eventlet-0.33.0/NEWS    2021-11-16 20:59:32.000000000 +0100
@@ -1,3 +1,11 @@
+0.33.0
+======
+* green.thread: unlocked Lock().release() should raise exception, returned 
True https://github.com/eventlet/eventlet/issues/697
+* wsgi: Don't break HTTP framing during 100-continue handling 
https://github.com/eventlet/eventlet/pull/578
+* Python 3.10 partial support https://github.com/eventlet/eventlet/pull/715
+* greendns: Create a DNS resolver lazily rather than on import 
https://github.com/eventlet/eventlet/issues/462
+* ssl: GreenSSLContext minimum_version and maximum_version setters 
https://github.com/eventlet/eventlet/issues/726
+
 0.32.0
 ======
 * greendns: compatibility with dnspython v2 
https://github.com/eventlet/eventlet/pull/722
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/PKG-INFO new/eventlet-0.33.0/PKG-INFO
--- old/eventlet-0.32.0/PKG-INFO        2021-09-01 12:54:37.135779000 +0200
+++ new/eventlet-0.33.0/PKG-INFO        2021-11-16 21:00:11.708561400 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: eventlet
-Version: 0.32.0
+Version: 0.33.0
 Summary: Highly concurrent networking library
 Home-page: http://eventlet.net
 Author: Linden Lab
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/__init__.py 
new/eventlet-0.33.0/eventlet/__init__.py
--- old/eventlet-0.32.0/eventlet/__init__.py    2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/eventlet/__init__.py    2021-11-16 20:59:32.000000000 
+0100
@@ -8,7 +8,7 @@
         DeprecationWarning,
     )
 
-version_info = (0, 32, 0)
+version_info = (0, 33, 0)
 __version__ = '.'.join(map(str, version_info))
 # This is to make Debian packaging easier, it ignores import
 # errors of greenlet so that the packager can still at least
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/green/ssl.py 
new/eventlet-0.33.0/eventlet/green/ssl.py
--- old/eventlet-0.32.0/eventlet/green/ssl.py   2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/eventlet/green/ssl.py   2021-11-16 20:59:32.000000000 
+0100
@@ -465,6 +465,16 @@
             def verify_mode(self, value):
                 super(_original_sslcontext, 
_original_sslcontext).verify_mode.__set__(self, value)
 
+            if hasattr(_original_sslcontext, "maximum_version"):
+                @_original_sslcontext.maximum_version.setter
+                def maximum_version(self, value):
+                    super(_original_sslcontext, 
_original_sslcontext).maximum_version.__set__(self, value)
+
+            if hasattr(_original_sslcontext, "minimum_version"):
+                @_original_sslcontext.minimum_version.setter
+                def minimum_version(self, value):
+                    super(_original_sslcontext, 
_original_sslcontext).minimum_version.__set__(self, value)
+
     SSLContext = GreenSSLContext
 
     if hasattr(__ssl, 'create_default_context'):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/green/thread.py 
new/eventlet-0.33.0/eventlet/green/thread.py
--- old/eventlet-0.32.0/eventlet/green/thread.py        2021-09-01 
12:49:47.000000000 +0200
+++ new/eventlet-0.33.0/eventlet/green/thread.py        2021-11-16 
20:59:32.000000000 +0100
@@ -3,15 +3,16 @@
 import six
 from eventlet.support import greenlets as greenlet
 from eventlet import greenthread
-from eventlet.semaphore import Semaphore as LockType
+from eventlet.lock import Lock
 import sys
 
 
 __patched__ = ['get_ident', 'start_new_thread', 'start_new', 'allocate_lock',
                'allocate', 'exit', 'interrupt_main', 'stack_size', '_local',
-               'LockType', '_count']
+               'LockType', 'Lock', '_count']
 
 error = __thread.error
+LockType = Lock
 __threadcount = 0
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/greenio/base.py 
new/eventlet-0.33.0/eventlet/greenio/base.py
--- old/eventlet-0.32.0/eventlet/greenio/base.py        2021-09-01 
12:49:47.000000000 +0200
+++ new/eventlet-0.33.0/eventlet/greenio/base.py        2021-11-16 
20:59:32.000000000 +0100
@@ -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):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/greenio/py3.py 
new/eventlet-0.33.0/eventlet/greenio/py3.py
--- old/eventlet-0.32.0/eventlet/greenio/py3.py 2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/eventlet/greenio/py3.py 2021-11-16 20:59:32.000000000 
+0100
@@ -191,9 +191,12 @@
     FileIO=GreenFileIO,
     os=_original_os,
 ))
+if hasattr(_original_pyio, 'text_encoding'):
+    _open_environment['text_encoding'] = _original_pyio.text_encoding
 
+_pyio_open = getattr(_original_pyio.open, '__wrapped__', _original_pyio.open)
 _open = FunctionType(
-    six.get_function_code(_original_pyio.open),
+    six.get_function_code(_pyio_open),
     _open_environment,
 )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/lock.py 
new/eventlet-0.33.0/eventlet/lock.py
--- old/eventlet-0.32.0/eventlet/lock.py        1970-01-01 01:00:00.000000000 
+0100
+++ new/eventlet-0.33.0/eventlet/lock.py        2021-11-16 20:59:32.000000000 
+0100
@@ -0,0 +1,28 @@
+from eventlet.semaphore import Semaphore
+
+
+class Lock(Semaphore):
+
+    """A lock.
+    This is API-compatible with :class:`threading.Lock`.
+
+    It is a context manager, and thus can be used in a with block::
+
+      lock = Lock()
+      with lock:
+        do_some_stuff()
+    """
+
+    def release(self, blocking=True):
+        """Modify behaviour vs :class:`Semaphore` to raise a RuntimeError
+        exception if the value is greater than zero. This corrects behaviour
+        to realign with :class:`threading.Lock`.
+        """
+        if self.counter > 0:
+            raise RuntimeError("release unlocked lock")
+
+        return super(Lock, self).release(blocking=blocking)
+
+    def _at_fork_reinit(self):
+        self.counter = 1
+        self._waiters.clear()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/semaphore.py 
new/eventlet-0.33.0/eventlet/semaphore.py
--- old/eventlet-0.32.0/eventlet/semaphore.py   2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/eventlet/semaphore.py   2021-11-16 20:59:32.000000000 
+0100
@@ -39,7 +39,6 @@
         if value < 0:
             msg = 'Semaphore() expect value >= 0, actual: 
{0}'.format(repr(value))
             raise ValueError(msg)
-        self._original_value = value
         self.counter = value
         self._waiters = collections.deque()
 
@@ -52,10 +51,6 @@
         params = (self.__class__.__name__, self.counter, len(self._waiters))
         return '<%s c=%s _w[%s]>' % params
 
-    def _at_fork_reinit(self):
-        self.counter = self._original_value
-        self._waiters.clear()
-
     def locked(self):
         """Returns true if a call to acquire would block.
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/support/greendns.py 
new/eventlet-0.33.0/eventlet/support/greendns.py
--- old/eventlet-0.32.0/eventlet/support/greendns.py    2021-09-01 
12:49:47.000000000 +0200
+++ new/eventlet-0.33.0/eventlet/support/greendns.py    2021-11-16 
20:59:32.000000000 +0100
@@ -322,7 +322,21 @@
         """
         self._hosts = hosts_resolver
         self._filename = filename
-        self.clear()
+        # NOTE(dtantsur): we cannot create a resolver here since this code is
+        # executed on eventlet import. In an environment without DNS, creating
+        # a Resolver will fail making eventlet unusable at all. See
+        # https://github.com/eventlet/eventlet/issues/736 for details.
+        self._cached_resolver = None
+
+    @property
+    def _resolver(self):
+        if self._cached_resolver is None:
+            self.clear()
+        return self._cached_resolver
+
+    @_resolver.setter
+    def _resolver(self, value):
+        self._cached_resolver = value
 
     def clear(self):
         self._resolver = dns.resolver.Resolver(filename=self._filename)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/timeout.py 
new/eventlet-0.33.0/eventlet/timeout.py
--- old/eventlet-0.32.0/eventlet/timeout.py     2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/eventlet/timeout.py     2021-11-16 20:59:32.000000000 
+0100
@@ -174,6 +174,11 @@
     return fun
 
 
+if isinstance(__builtins__, dict):  # seen when running tests on py310, but 
HOW??
+    _timeout_err = __builtins__.get('TimeoutError', Timeout)
+else:
+    _timeout_err = getattr(__builtins__, 'TimeoutError', Timeout)
+
+
 def is_timeout(obj):
-    py3err = getattr(__builtins__, 'TimeoutError', Timeout)
-    return bool(getattr(obj, 'is_timeout', False)) or isinstance(obj, py3err)
+    return bool(getattr(obj, 'is_timeout', False)) or isinstance(obj, 
_timeout_err)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet/wsgi.py 
new/eventlet-0.33.0/eventlet/wsgi.py
--- old/eventlet-0.32.0/eventlet/wsgi.py        2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/eventlet/wsgi.py        2021-11-16 20:59:32.000000000 
+0100
@@ -134,8 +134,12 @@
         # Reinitialize chunk_length (expect more data)
         self.chunk_length = -1
 
+    @property
+    def should_send_hundred_continue(self):
+        return self.wfile is not None and not 
self.is_hundred_continue_response_sent
+
     def _do_read(self, reader, length=None):
-        if self.wfile is not None and not 
self.is_hundred_continue_response_sent:
+        if self.should_send_hundred_continue:
             # 100 Continue response
             self.send_hundred_continue_response()
             self.is_hundred_continue_response_sent = True
@@ -152,7 +156,7 @@
         return read
 
     def _chunked_read(self, rfile, length=None, use_readline=False):
-        if self.wfile is not None and not 
self.is_hundred_continue_response_sent:
+        if self.should_send_hundred_continue:
             # 100 Continue response
             self.send_hundred_continue_response()
             self.is_hundred_continue_response_sent = True
@@ -464,9 +468,11 @@
         start = time.time()
         headers_set = []
         headers_sent = []
+        # Grab the request input now; app may try to replace it in the environ
+        request_input = self.environ['eventlet.input']
         # Push the headers-sent state into the Input so it won't send a
         # 100 Continue response if we've already started a response.
-        self.environ['wsgi.input'].headers_sent = headers_sent
+        request_input.headers_sent = headers_sent
 
         wfile = self.wfile
         result = None
@@ -563,10 +569,15 @@
                 result = self.application(self.environ, start_response)
 
                 # Set content-length if possible
-                if headers_set and \
-                        not headers_sent and hasattr(result, '__len__') and \
-                        'Content-Length' not in [h for h, _v in 
headers_set[1]]:
-                    headers_set[1].append(('Content-Length', str(sum(map(len, 
result)))))
+                if headers_set and not headers_sent and hasattr(result, 
'__len__'):
+                    # We've got a complete final response
+                    if 'Content-Length' not in [h for h, _v in headers_set[1]]:
+                        headers_set[1].append(('Content-Length', 
str(sum(map(len, result)))))
+                    if request_input.should_send_hundred_continue:
+                        # We've got a complete final response, and never sent 
a 100 Continue.
+                        # There's no chance we'll need to read the body as we 
stream out the
+                        # response, so we can be nice and send a Connection: 
close header.
+                        self.close_connection = 1
 
                 towrite = []
                 towrite_size = 0
@@ -607,11 +618,22 @@
         finally:
             if hasattr(result, 'close'):
                 result.close()
-            request_input = self.environ['eventlet.input']
+            if request_input.should_send_hundred_continue:
+                # We just sent the final response, no 100 Continue. Client may 
or
+                # may not have started to send a body, and if we keep the 
connection
+                # open we've seen clients either
+                #   * send a body, then start a new request
+                #   * skip the body and go straight to a new request
+                # Looks like the most broadly compatible option is to close the
+                # connection and let the client retry.
+                # https://curl.se/mail/lib-2004-08/0002.html
+                # Note that we likely *won't* send a Connection: close header 
at this point
+                self.close_connection = 1
+
             if (request_input.chunked_input or
                     request_input.position < (request_input.content_length or 
0)):
-                # Read and discard body if there was no pending 100-continue
-                if not request_input.wfile and self.close_connection == 0:
+                # Read and discard body if connection is going to be reused
+                if self.close_connection == 0:
                     try:
                         request_input.discard()
                     except ChunkReadError as e:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet.egg-info/PKG-INFO 
new/eventlet-0.33.0/eventlet.egg-info/PKG-INFO
--- old/eventlet-0.32.0/eventlet.egg-info/PKG-INFO      2021-09-01 
12:54:36.000000000 +0200
+++ new/eventlet-0.33.0/eventlet.egg-info/PKG-INFO      2021-11-16 
21:00:11.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: eventlet
-Version: 0.32.0
+Version: 0.33.0
 Summary: Highly concurrent networking library
 Home-page: http://eventlet.net
 Author: Linden Lab
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/eventlet.egg-info/SOURCES.txt 
new/eventlet-0.33.0/eventlet.egg-info/SOURCES.txt
--- old/eventlet-0.32.0/eventlet.egg-info/SOURCES.txt   2021-09-01 
12:54:36.000000000 +0200
+++ new/eventlet-0.33.0/eventlet.egg-info/SOURCES.txt   2021-11-16 
21:00:11.000000000 +0100
@@ -52,6 +52,7 @@
 eventlet/event.py
 eventlet/greenpool.py
 eventlet/greenthread.py
+eventlet/lock.py
 eventlet/patcher.py
 eventlet/pools.py
 eventlet/queue.py
@@ -233,6 +234,7 @@
 tests/isolated/patcher_threadpoolexecutor.py
 tests/isolated/regular_file_readall.py
 tests/isolated/socket_resolve_green.py
+tests/isolated/ssl_context_version_setters.py
 tests/isolated/subprocess_exception_identity.py
 tests/isolated/subprocess_patched_communicate.py
 tests/isolated/tpool_exception_leak.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/setup.cfg 
new/eventlet-0.33.0/setup.cfg
--- old/eventlet-0.32.0/setup.cfg       2021-09-01 12:54:37.137286000 +0200
+++ new/eventlet-0.33.0/setup.cfg       2021-11-16 21:00:11.710361700 +0100
@@ -1,5 +1,5 @@
 [metadata]
-description-file = README.rst
+description_file = README.rst
 
 [wheel]
 universal = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/tests/__init__.py 
new/eventlet-0.33.0/tests/__init__.py
--- old/eventlet-0.32.0/tests/__init__.py       2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/tests/__init__.py       2021-11-16 20:59:32.000000000 
+0100
@@ -383,7 +383,7 @@
 
 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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/tests/backdoor_test.py 
new/eventlet-0.33.0/tests/backdoor_test.py
--- old/eventlet-0.32.0/tests/backdoor_test.py  2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/tests/backdoor_test.py  2021-11-16 20:59:32.000000000 
+0100
@@ -1,5 +1,6 @@
 import os
 import os.path
+import sys
 
 import eventlet
 
@@ -22,7 +23,9 @@
     def _run_test_on_client_and_server(self, client, server_thread):
         f = client.makefile('rw')
         assert 'Python' in f.readline()
-        f.readline()  # build info
+        if sys.version_info < (3, 10):
+            # Starting in py310, build info is included in version line
+            f.readline()  # build info
         f.readline()  # help info
         assert 'InteractiveConsole' in f.readline()
         self.assertEqual('>>> ', f.read(4))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/tests/greendns_test.py 
new/eventlet-0.33.0/tests/greendns_test.py
--- old/eventlet-0.32.0/tests/greendns_test.py  2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/tests/greendns_test.py  2021-11-16 20:59:32.000000000 
+0100
@@ -297,8 +297,11 @@
 
     def test_clear(self):
         rp = greendns.ResolverProxy()
+        assert rp._cached_resolver is None
         resolver = rp._resolver
+        assert resolver is not None
         rp.clear()
+        assert rp._resolver is not None
         assert rp._resolver != resolver
 
     def _make_mock_hostsresolver(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/eventlet-0.32.0/tests/isolated/ssl_context_version_setters.py 
new/eventlet-0.33.0/tests/isolated/ssl_context_version_setters.py
--- old/eventlet-0.32.0/tests/isolated/ssl_context_version_setters.py   
1970-01-01 01:00:00.000000000 +0100
+++ new/eventlet-0.33.0/tests/isolated/ssl_context_version_setters.py   
2021-11-16 20:59:32.000000000 +0100
@@ -0,0 +1,12 @@
+__test__ = False
+
+if __name__ == "__main__":
+    import eventlet
+    eventlet.monkey_patch()
+    import ssl
+
+    context = ssl.create_default_context()
+    context.minimum_version = ssl.TLSVersion.TLSv1_2
+    context.maximum_version = ssl.TLSVersion.TLSv1_2
+
+    print("pass")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/tests/semaphore_test.py 
new/eventlet-0.33.0/tests/semaphore_test.py
--- old/eventlet-0.32.0/tests/semaphore_test.py 2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/tests/semaphore_test.py 2021-11-16 20:59:32.000000000 
+0100
@@ -42,27 +42,6 @@
         sem = eventlet.Semaphore()
         self.assertRaises(ValueError, sem.acquire, blocking=False, timeout=1)
 
-    def test_reinit(self):
-        # py39+ expects locks to have a _at_fork_reinit() method; since we
-        # patch in Semaphores in eventlet.green.thread, they need it, too
-        sem = eventlet.Semaphore()
-        sem.acquire()
-        sem._at_fork_reinit()
-        self.assertEqual(sem.acquire(blocking=False), True)
-        self.assertEqual(sem.acquire(blocking=False), False)
-
-        sem = eventlet.Semaphore(0)
-        sem.release()
-        sem._at_fork_reinit()
-        self.assertEqual(sem.acquire(blocking=False), False)
-
-        sem = eventlet.Semaphore(2)
-        sem.acquire()
-        sem._at_fork_reinit()
-        self.assertEqual(sem.acquire(blocking=False), True)
-        self.assertEqual(sem.acquire(blocking=False), True)
-        self.assertEqual(sem.acquire(blocking=False), False)
-
 
 def test_semaphore_contention():
     g_mutex = eventlet.Semaphore()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/tests/ssl_test.py 
new/eventlet-0.33.0/tests/ssl_test.py
--- old/eventlet-0.32.0/tests/ssl_test.py       2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/tests/ssl_test.py       2021-11-16 20:59:32.000000000 
+0100
@@ -395,3 +395,7 @@
         client.send(b"check_hostname works")
         client.recv(64)
         server_coro.wait()
+
+    @tests.skip_if(sys.version_info < (3, 7))
+    def test_context_version_setters(self):
+        tests.run_isolated("ssl_context_version_setters.py")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/tests/thread_test.py 
new/eventlet-0.33.0/tests/thread_test.py
--- old/eventlet-0.32.0/tests/thread_test.py    2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/tests/thread_test.py    2021-11-16 20:59:32.000000000 
+0100
@@ -5,6 +5,7 @@
 from eventlet import corolocal
 from eventlet import event
 from eventlet import greenthread
+from eventlet import patcher
 from eventlet.green import thread
 import six
 
@@ -99,3 +100,26 @@
         gc.collect()
         # at this point all our coros have terminated
         self.assertEqual(len(refs), 1)
+
+
+def test_compat_lock_release():
+    # https://github.com/eventlet/eventlet/issues/697
+    for mod in (patcher.original("threading"), thread):
+        try:
+            mod.Lock().release()
+        except RuntimeError as e:
+            # python3
+            assert "release unlocked lock" in str(e).lower(), str((mod, e))
+        except thread.error as e:
+            # python2.7
+            assert "release unlocked lock" in str(e).lower(), str((mod, e))
+
+
+def test_reinit():
+    # py39+ expects locks to have a _at_fork_reinit() method
+    # https://github.com/eventlet/eventlet/pull/721#pullrequestreview-769377850
+    lk = thread.Lock()
+    lk.acquire()
+    lk._at_fork_reinit()
+    assert lk.acquire(blocking=False)
+    assert not lk.acquire(blocking=False)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eventlet-0.32.0/tests/wsgi_test.py 
new/eventlet-0.33.0/tests/wsgi_test.py
--- old/eventlet-0.32.0/tests/wsgi_test.py      2021-09-01 12:49:47.000000000 
+0200
+++ new/eventlet-0.33.0/tests/wsgi_test.py      2021-11-16 20:59:32.000000000 
+0100
@@ -608,6 +608,57 @@
         self.assertEqual('keep-alive', result2.headers_lower['connection'])
         sock.close()
 
+    def test_018b_http_10_keepalive_framing(self):
+        # verify that if an http/1.0 client sends connection: keep-alive
+        # that we don't mangle the request framing if the app doesn't read the 
request
+        def app(environ, start_response):
+            resp_body = {
+                '/1': b'first response',
+                '/2': b'second response',
+                '/3': b'third response',
+            }.get(environ['PATH_INFO'])
+            if resp_body is None:
+                resp_body = 'Unexpected path: ' + environ['PATH_INFO']
+                if six.PY3:
+                    resp_body = resp_body.encode('latin1')
+            # Never look at wsgi.input!
+            start_response('200 OK', [('Content-type', 'text/plain')])
+            return [resp_body]
+
+        self.site.application = app
+        sock = eventlet.connect(self.server_addr)
+        req_body = b'GET /tricksy HTTP/1.1\r\n'
+        body_len = str(len(req_body)).encode('ascii')
+
+        sock.sendall(b'PUT /1 HTTP/1.0\r\nHost: localhost\r\nConnection: 
keep-alive\r\n'
+                     b'Content-Length: ' + body_len + b'\r\n\r\n' + req_body)
+        result1 = read_http(sock)
+        self.assertEqual(b'first response', result1.body)
+        self.assertEqual(result1.headers_original.get('Connection'), 
'keep-alive')
+
+        sock.sendall(b'PUT /2 HTTP/1.0\r\nHost: localhost\r\nConnection: 
keep-alive\r\n'
+                     b'Content-Length: ' + body_len + b'\r\nExpect: 
100-continue\r\n\r\n')
+        # Client may have a short timeout waiting on that 100 Continue
+        # and basically immediately send its body
+        sock.sendall(req_body)
+        result2 = read_http(sock)
+        self.assertEqual(b'second response', result2.body)
+        self.assertEqual(result2.headers_original.get('Connection'), 'close')
+
+        sock.sendall(b'PUT /3 HTTP/1.0\r\nHost: localhost\r\nConnection: 
close\r\n\r\n')
+        with self.assertRaises(ConnectionClosed):
+            read_http(sock)
+        sock.close()
+
+        # retry
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'PUT /3 HTTP/1.0\r\nHost: localhost\r\nConnection: 
close\r\n\r\n')
+        result3 = read_http(sock)
+        self.assertEqual(b'third response', result3.body)
+        self.assertEqual(result3.headers_original.get('Connection'), 'close')
+
+        sock.close()
+
     def test_019_fieldstorage_compat(self):
         def use_fieldstorage(environ, start_response):
             cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
@@ -756,9 +807,23 @@
                  b'Expect: 100-continue\r\n\r\n')
         fd.flush()
         result = read_http(sock)
+        # No "100 Continue" -- straight to final response
         self.assertEqual(result.status, 'HTTP/1.1 417 Expectation Failed')
         self.assertEqual(result.body, b'failure')
+        self.assertEqual(result.headers_original.get('Connection'), 'close')
+        # Client may still try to send the body
+        fd.write(b'x' * 25)
+        fd.flush()
+        # But if they keep using this socket, it's going to close on them 
eventually
+        fd.write(b'x' * 25)
+        with self.assertRaises(socket.error) as caught:
+            fd.flush()
+        self.assertEqual(caught.exception.errno, errno.EPIPE)
+        sock.close()
 
+        sock = eventlet.connect(self.server_addr)
+        fd = sock.makefile('rwb')
+        # If we send the "100 Continue", we can pipeline requests through the 
one connection
         for expect_value in ('100-continue', '100-Continue'):
             fd.write(
                 'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 7\r\n'
@@ -767,6 +832,8 @@
             header_lines = []
             while True:
                 line = fd.readline()
+                if not line:
+                    raise ConnectionClosed
                 if line == b'\r\n':
                     break
                 else:
@@ -775,11 +842,14 @@
             header_lines = []
             while True:
                 line = fd.readline()
+                if not line:
+                    raise ConnectionClosed
                 if line == b'\r\n':
                     break
                 else:
                     header_lines.append(line)
             assert header_lines[0].startswith(b'HTTP/1.1 200 OK')
+            assert 'Connection: close' not in header_lines
             assert fd.read(7) == b'testing'
         fd.close()
         sock.close()
@@ -806,6 +876,14 @@
         result = read_http(sock)
         self.assertEqual(result.status, 'HTTP/1.1 417 Expectation Failed')
         self.assertEqual(result.body, b'failure')
+        self.assertEqual(result.headers_original.get('Connection'), 'close')
+        # At this point, the client needs to either kill the connection or 
send the bytes
+        # because even though the server sent the response without reading the 
body,
+        # it has no way of knowing whether the client already started sending 
or not
+        sock.close()
+        sock = eventlet.connect(self.server_addr)
+        fd = sock.makefile('rwb')
+
         fd.write(
             b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 7\r\n'
             b'Expect: 100-continue\r\n\r\ntesting')

++++++ newdnspython.patch ++++++
--- /var/tmp/diff_new_pack.Hx8NKw/_old  2021-12-23 17:53:31.427710724 +0100
+++ /var/tmp/diff_new_pack.Hx8NKw/_new  2021-12-23 17:53:31.431710726 +0100
@@ -1,9 +1,9 @@
-Index: eventlet-0.32.0/eventlet/support/greendns.py
+Index: eventlet-0.33.0/eventlet/support/greendns.py
 ===================================================================
---- eventlet-0.32.0.orig/eventlet/support/greendns.py
-+++ eventlet-0.32.0/eventlet/support/greendns.py
-@@ -325,7 +325,7 @@ class ResolverProxy(object):
-         self.clear()
+--- eventlet-0.33.0.orig/eventlet/support/greendns.py
++++ eventlet-0.33.0/eventlet/support/greendns.py
+@@ -339,7 +339,7 @@ class ResolverProxy(object):
+         self._cached_resolver = value
  
      def clear(self):
 -        self._resolver = dns.resolver.Resolver(filename=self._filename)
@@ -11,11 +11,11 @@
          self._resolver.cache = dns.resolver.LRUCache()
  
      def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
-Index: eventlet-0.32.0/tests/greendns_test.py
+Index: eventlet-0.33.0/tests/greendns_test.py
 ===================================================================
---- eventlet-0.32.0.orig/tests/greendns_test.py
-+++ eventlet-0.32.0/tests/greendns_test.py
-@@ -885,7 +885,7 @@ class TinyDNSTests(tests.LimitedTestCase
+--- eventlet-0.33.0.orig/tests/greendns_test.py
++++ eventlet-0.33.0/tests/greendns_test.py
+@@ -888,7 +888,7 @@ class TinyDNSTests(tests.LimitedTestCase
          # https://github.com/eventlet/eventlet/issues/499
          # None means we don't want the server to find the IP
          with tests.dns_tcp_server(None) as dnsaddr:
@@ -24,7 +24,7 @@
              resolver.nameservers = [dnsaddr[0]]
              resolver.nameserver_ports[dnsaddr[0]] = dnsaddr[1]
  
-@@ -896,7 +896,7 @@ class TinyDNSTests(tests.LimitedTestCase
+@@ -899,7 +899,7 @@ class TinyDNSTests(tests.LimitedTestCase
          # https://github.com/eventlet/eventlet/issues/499
          expected_ip = "192.168.1.1"
          with tests.dns_tcp_server(expected_ip) as dnsaddr:

Reply via email to