1 Based on https://github.com/gevent/gevent/commit/8224e81425762ad21d3b63ffb9cc0a0c2d789704.patch
2 and https://src.fedoraproject.org/rpms/python-gevent/raw/master/f/0001-code-replace.patch
4 From e6e2959184909c6292e0135b709282cd5f96065b Mon Sep 17 00:00:00 2001
5 From: Jason Madden <jamadden@gmail.com>
6 Date: Fri, 6 Sep 2019 16:29:58 -0500
7 Subject: [PATCH 1/9] Basic support for CPython 3.8.0b4.
9 Still needs the specific networking test classes added, but all the basics pass for me. Lets see about CI.
11 .travis.yml | 11 +++++++
13 examples/webproxy.py | 7 +++-
14 pyproject.toml | 5 +--
15 scripts/install.sh | 3 ++
16 src/gevent/__semaphore.pxd | 2 +-
17 src/gevent/_compat.py | 2 +-
18 src/gevent/_socket2.py | 1 +
19 src/gevent/_socketcommon.py | 12 +++++--
20 src/gevent/monkey.py | 46 +++++++++++++++++++++++++--
21 src/gevent/os.py | 29 ++++++++++++++---
22 src/gevent/subprocess.py | 32 +++++++++++++++++++
23 src/gevent/testing/testrunner.py | 17 +++++++---
24 src/gevent/tests/test__core_fork.py | 7 ++--
25 src/gevent/tests/test__threading_2.py | 26 +++++++++------
26 src/gevent/thread.py | 6 ++++
27 src/gevent/threading.py | 46 ++++++++++-----------------
28 17 files changed, 196 insertions(+), 60 deletions(-)
30 #diff --git a/.travis.yml b/.travis.yml
31 #index bf307df95..05dc24b12 100644
34 #@@ -42,6 +42,7 @@ env:
35 # - TRAVIS_PYTHON_VERSION=3.5
36 # - TRAVIS_PYTHON_VERSION=3.6
37 # - TRAVIS_PYTHON_VERSION=3.7
38 #+ - TRAVIS_PYTHON_VERSION=3.8
39 # - TRAVIS_PYTHON_VERSION=pypy2.7
40 # - TRAVIS_PYTHON_VERSION=pypy3.6
41 # - TRAVIS_PYTHON_VERSION=2.7 GEVENTSETUP_EMBED=0
42 #@@ -144,6 +145,8 @@ jobs:
46 #+ - <<: *build-gevent
47 #+ env: TRAVIS_PYTHON_VERSION=3.8
49 # env: TRAVIS_PYTHON_VERSION=3.5
51 #@@ -278,6 +281,14 @@ jobs:
52 # env: TRAVIS_PYTHON_VERSION=3.7
56 #+ - <<: *test-libuv-jobs
57 #+ env: TRAVIS_PYTHON_VERSION=3.8
59 #+ - <<: *test-libev-jobs
60 #+ env: TRAVIS_PYTHON_VERSION=3.8
64 # # 2.7, no-embed. Run the tests that exercise the libraries we
66 #diff --git a/CHANGES.rst b/CHANGES.rst
67 #index 52b5e1c6f..53f4398d2 100644
71 # - Add an ``--module`` option to ``gevent.monkey`` allowing to run a Python
72 # module rather than a script. See :pr:`1440`.
74 #+- Add support for CPython 3.8.0b4.
76 #+- Improve the way joining the main thread works on Python 3.
81 diff --git a/examples/webproxy.py b/examples/webproxy.py
82 index e381d6bd4..de1eaf8d5 100755
83 --- a/examples/webproxy.py
84 +++ b/examples/webproxy.py
89 -from cgi import escape
92 + from cgi import escape
94 + # Python 3.8 removed this API
95 + from html import escape
99 #diff --git a/pyproject.toml b/pyproject.toml
100 #index 2b611b9a4..f611ef972 100644
101 #--- a/pyproject.toml
102 #+++ b/pyproject.toml
103 #@@ -12,8 +12,9 @@ requires = [
104 # # 0.28 is faster, and (important!) lets us specify the target module
105 # # name to be created so that we can have both foo.py and _foo.so
106 # # at the same time. 0.29 fixes some issues with Python 3.7,
107 #- # and adds the 3str mode for transition to Python 3.
108 #- "Cython >= 0.29.7",
109 #+ # and adds the 3str mode for transition to Python 3. 0.29.12+ is
110 #+ # required for Python 3.8
111 #+ "Cython >= 0.29.13",
112 # # See version requirements in setup.py
113 # "cffi >= 1.12.3 ; platform_python_implementation == 'CPython'",
114 # # Python 3.7 requires at least 0.4.14, which is ABI incompatible with earlier
115 #diff --git a/scripts/install.sh b/scripts/install.sh
116 #index 94bd53058..ef8a6dfbd 100755
117 #--- a/scripts/install.sh
118 #+++ b/scripts/install.sh
119 #@@ -99,6 +99,9 @@ for var in "$@"; do
121 # install 3.7.2 python3.7 3.7.d
124 #+ install 3.8.0b4 python3.8 3.8.d
127 # install pypy2.7-7.1.0 pypy2.7 pypy2.7.d
129 diff --git a/src/gevent/__semaphore.pxd b/src/gevent/__semaphore.pxd
130 index dc9f11c68..e46778ed7 100644
131 --- a/src/gevent/__semaphore.pxd
132 +++ b/src/gevent/__semaphore.pxd
133 @@ -13,7 +13,7 @@ cdef class Semaphore(AbstractLinkable):
135 cpdef _start_notify(self)
136 cpdef int wait(self, object timeout=*) except -1000
137 - cpdef bint acquire(self, int blocking=*, object timeout=*) except -1000
138 + cpdef bint acquire(self, bint blocking=*, object timeout=*) except -1000
139 cpdef __enter__(self)
140 cpdef __exit__(self, object t, object v, object tb)
142 diff --git a/src/gevent/_compat.py b/src/gevent/_compat.py
143 index 56391459e..e8f5ed4ba 100644
144 --- a/src/gevent/_compat.py
145 +++ b/src/gevent/_compat.py
147 PYPY = hasattr(sys, 'pypy_version_info')
148 WIN = sys.platform.startswith("win")
149 LINUX = sys.platform.startswith('linux')
150 -OSX = sys.platform == 'darwin'
151 +OSX = MAC = sys.platform == 'darwin'
154 PURE_PYTHON = PYPY or os.getenv('PURE_PYTHON')
155 diff --git a/src/gevent/_socket2.py b/src/gevent/_socket2.py
156 index 15f470893..7c2f35f3e 100644
157 --- a/src/gevent/_socket2.py
158 +++ b/src/gevent/_socket2.py
161 # Our import magic sadly makes this warning useless
162 # pylint: disable=undefined-variable
165 from gevent import _socketcommon
166 from gevent._util import copy_globals
167 #diff --git a/src/gevent/_socketcommon.py b/src/gevent/_socketcommon.py
168 #index cd12ac1a9..87b9a4f8a 100644
169 #--- a/src/gevent/_socketcommon.py
170 #+++ b/src/gevent/_socketcommon.py
172 # __imports__.extend(__py3_imports__)
177 # from gevent._hub_local import get_hub_noargs as get_hub
178 # from gevent._compat import string_types, integer_types, PY3
179 #+from gevent._compat import PY38
180 #+from gevent._compat import WIN as is_windows
181 #+from gevent._compat import OSX as is_macos
182 # from gevent._util import copy_globals
184 #-is_windows = sys.platform == 'win32'
185 #-is_macos = sys.platform == 'darwin'
187 #+ __imports__.extend([
189 #+ 'has_dualstack_ipv6',
192 # # pylint:disable=no-name-in-module,unused-import
194 diff --git a/src/gevent/monkey.py b/src/gevent/monkey.py
195 index 5dbfe3f78..f862f879c 100644
196 --- a/src/gevent/monkey.py
197 +++ b/src/gevent/monkey.py
198 @@ -668,6 +668,22 @@ def join(timeout=None):
199 raise RuntimeError("Cannot join current thread")
200 if thread_greenlet is not None and thread_greenlet.dead:
202 + # You may ask: Why not call thread_greenlet.join()?
203 + # Well, in the one case we actually have a greenlet, it's the
204 + # low-level greenlet.greenlet object for the main thread, which
205 + # doesn't have a join method.
207 + # You may ask: Why not become the main greenlet's *parent*
208 + # so you can get notified when it finishes? Because you can't
209 + # create a greenlet cycle (the current greenlet is a descendent
210 + # of the parent), and nor can you set a greenlet's parent to None,
211 + # so there can only ever be one greenlet with a parent of None: the main
212 + # greenlet, the one we need to watch.
214 + # You may ask: why not swizzle out the problematic lock on the main thread
215 + # into a gevent friendly lock? Well, the interpreter actually depends on that
216 + # for the main thread in threading._shutdown; see below.
218 if not thread.is_alive():
221 @@ -700,8 +716,34 @@ def join(timeout=None):
222 if orig_current_thread == threading_mod.main_thread():
223 main_thread = threading_mod.main_thread()
224 _greenlet = main_thread._greenlet = greenlet.getcurrent()
226 - main_thread.join = make_join_func(main_thread, _greenlet)
227 + main_thread.__real_tstate_lock = main_thread._tstate_lock
229 + # The interpreter will call threading._shutdown
230 + # when the main thread exits and is about to
231 + # go away. It is called *in* the main thread. This
232 + # is a perfect place to notify other greenlets that
233 + # the main thread is done. We do this by overriding the
234 + # lock of the main thread during operation, and only restoring
235 + # it to the native blocking version at shutdown time
236 + # (the interpreter also has a reference to this lock in a
237 + # C data structure).
238 + main_thread._tstate_lock = threading_mod.Lock()
239 + main_thread._tstate_lock.acquire()
240 + orig_shutdown = threading_mod._shutdown
242 + # Release anyone trying to join() me,
243 + # and let us switch to them.
244 + if main_thread._tstate_lock:
245 + main_thread._tstate_lock.release()
246 + from gevent import sleep
248 + # The only truly blocking native shutdown lock to
249 + # acquire should be our own (hopefully)
250 + main_thread._tstate_lock = main_thread.__real_tstate_lock
251 + main_thread.__real_tstate_lock = None
254 + threading_mod._shutdown = _shutdown
256 # Patch up the ident of the main thread to match. This
257 # matters if threading was imported before monkey-patching
258 diff --git a/src/gevent/os.py b/src/gevent/os.py
259 index 3980f320c..4dd66abd8 100644
260 --- a/src/gevent/os.py
261 +++ b/src/gevent/os.py
262 @@ -385,6 +385,12 @@ def waitpid(pid, options):
263 # we're not watching it
264 return _waitpid(pid, options)
266 + def _watch_child(pid, callback=None, loop=None, ref=False):
267 + loop = loop or get_hub().loop
268 + watcher = loop.child(pid, ref=ref)
269 + _watched_children[pid] = watcher
270 + watcher.start(_on_child, watcher, callback)
272 def fork_and_watch(callback=None, loop=None, ref=False, fork=fork_gevent):
274 Fork a child process and start a child watcher for it in the parent process.
275 @@ -413,10 +419,7 @@ def fork_and_watch(callback=None, loop=None, ref=False, fork=fork_gevent):
279 - loop = loop or get_hub().loop
280 - watcher = loop.child(pid, ref=ref)
281 - _watched_children[pid] = watcher
282 - watcher.start(_on_child, watcher, callback)
283 + _watch_child(pid, callback, loop, ref)
286 __extensions__.append('fork_and_watch')
287 @@ -474,6 +477,23 @@ def forkpty(*args, **kwargs):
288 # take any args to match fork_and_watch
289 return forkpty_and_watch(*args, **kwargs)
290 __implements__.append("waitpid")
292 + if hasattr(os, 'posix_spawn'):
293 + _raw_posix_spawn = os.posix_spawn
294 + _raw_posix_spawnp = os.posix_spawnp
296 + def posix_spawn(*args, **kwargs):
297 + pid = _raw_posix_spawn(*args, **kwargs)
301 + def posix_spawnp(*args, **kwargs):
302 + pid = _raw_posix_spawnp(*args, **kwargs)
306 + __implements__.append("posix_spawn")
307 + __implements__.append("posix_spawnp")
311 @@ -503,6 +523,7 @@ def forkpty():
313 __implements__.remove('fork')
316 __imports__ = copy_globals(os, globals(),
317 names_to_ignore=__implements__ + __extensions__,
318 dunder_names_to_keep=())
319 #diff --git a/src/gevent/subprocess.py b/src/gevent/subprocess.py
320 #index a9894f1ab..9b4d13cd2 100644
321 #--- a/src/gevent/subprocess.py
322 #+++ b/src/gevent/subprocess.py
324 # from gevent._compat import PY35
325 # from gevent._compat import PY36
326 # from gevent._compat import PY37
327 #+from gevent._compat import PY38
328 # from gevent._compat import reraise
329 # from gevent._compat import fspath
330 # from gevent._compat import fsencode
332 # __implements__.append("_posixsubprocess")
333 # _posixsubprocess = None
336 #+ # Using os.posix_spawn() to start subprocesses
337 #+ # bypasses our child watchers on certain operating systems,
338 #+ # and with certain library versions. Possibly the right
339 #+ # fix is to monkey-patch os.posix_spawn like we do os.fork?
340 #+ # These have no effect, they're just here to match the stdlib.
341 #+ # TODO: When available, given a monkey patch on them, I think
342 #+ # we ought to be able to use them if the stdlib has identified them
344 #+ __implements__.extend([
345 #+ '_use_posix_spawn',
346 #+ '_USE_POSIX_SPAWN'
349 #+ def _use_posix_spawn():
352 #+ _USE_POSIX_SPAWN = False
354 # # Some symbols we define that we expect to export;
355 # # useful for static analysis
356 # PIPE = "PIPE should be imported"
357 #@@ -1720,3 +1740,15 @@ def run(*popenargs, **kwargs):
358 # raise _with_stdout_stderr(CalledProcessError(retcode, process.args, stdout), stderr)
360 # return CompletedProcess(process.args, retcode, stdout, stderr)
362 #+def _gevent_did_monkey_patch(*_args):
363 #+ # Beginning on 3.8 on Mac, the 'spawn' method became the default
364 #+ # start method. That doesn't fire fork watchers and we can't
365 #+ # easily patch to make it do so: multiprocessing uses the private
366 #+ # c accelerated _subprocess module to implement this. Instead we revert
367 #+ # back to using fork.
368 #+ from gevent._compat import MAC
370 #+ import multiprocessing
371 #+ if hasattr(multiprocessing, 'set_start_method'):
372 #+ multiprocessing.set_start_method('fork', force=True)
373 diff --git a/src/gevent/testing/testrunner.py b/src/gevent/testing/testrunner.py
374 index 3c1c711f8..7a3f8c6bb 100644
375 --- a/src/gevent/testing/testrunner.py
376 +++ b/src/gevent/testing/testrunner.py
377 @@ -480,17 +480,26 @@ def _setup_environ(debug=False):
378 if 'GEVENT_DEBUG' not in os.environ and debug:
379 os.environ['GEVENT_DEBUG'] = 'debug'
381 - if 'PYTHONTRACEMALLOC' not in os.environ:
382 + if 'PYTHONTRACEMALLOC' not in os.environ and debug:
383 + # This slows the tests down quite a bit. Reserve
385 os.environ['PYTHONTRACEMALLOC'] = '10'
387 if 'PYTHONDEVMODE' not in os.environ:
389 + # Python 3.7 and above.
390 os.environ['PYTHONDEVMODE'] = '1'
392 - if 'PYTHONMALLOC' not in os.environ:
394 + if 'PYTHONMALLOC' not in os.environ and debug:
395 + # Python 3.6 and above.
396 + # This slows the tests down some, but
397 + # can detect memory corruption. Unfortunately
398 + # it can also be flaky, especially in pre-release
399 + # versions of Python (e.g., lots of crashes on Python 3.8b4).
400 os.environ['PYTHONMALLOC'] = 'debug'
402 + if sys.version_info.releaselevel != 'final' and not debug:
403 + os.environ['PYTHONMALLOC'] = 'default'
404 + os.environ['PYTHONDEVMODE'] = ''
408 diff --git a/src/gevent/tests/test__core_fork.py b/src/gevent/tests/test__core_fork.py
409 index fee42f547..5717cf7d9 100644
410 --- a/src/gevent/tests/test__core_fork.py
411 +++ b/src/gevent/tests/test__core_fork.py
413 from __future__ import print_function
415 gevent.monkey.patch_all()
420 import multiprocessing
423 +from gevent._compat import MAC
425 hub = gevent.get_hub()
428 @@ -46,6 +48,7 @@ def test():
429 if __name__ == '__main__':
430 # Must call for Windows to fork properly; the fork can't be in the top-level
431 multiprocessing.freeze_support()
433 # fork watchers weren't firing in multi-threading processes.
434 # This test is designed to prove that they are.
435 # However, it fails on Windows: The fork watcher never runs!
436 diff --git a/src/gevent/tests/test__threading_2.py b/src/gevent/tests/test__threading_2.py
437 index b425c88a3..cb7f8b91e 100644
438 --- a/src/gevent/tests/test__threading_2.py
439 +++ b/src/gevent/tests/test__threading_2.py
441 from test.support import verbose
443 from test.test_support import verbose
451 from gevent.tests import lock_tests
454 # A trivial mutable counter.
456 def skipDueToHang(cls):
457 @@ -132,7 +133,7 @@ def test_various_ops(self):
458 print('waiting for all tasks to complete')
461 - self.assertFalse(t.is_alive())
462 + self.assertFalse(t.is_alive(), t.__dict__)
463 if hasattr(t, 'ident'):
464 self.assertNotEqual(t.ident, 0)
465 self.assertFalse(t.ident is None)
466 @@ -351,28 +352,33 @@ def test_join_nondaemon_on_shutdown(self):
468 # Raising SystemExit skipped threading._shutdown
470 - p = subprocess.Popen([sys.executable, "-W", "ignore", "-c", """if 1:
474 from time import sleep
479 # As a non-daemon thread we SHOULD wake up and nothing
480 # should be torn down yet
481 - print("Woke up, sleep function is: %%r" %% sleep)
482 + print("Woke up, sleep function is: %%s.%%s" %% (sleep.__module__, sleep.__name__))
484 threading.Thread(target=child).start()
488 + p = subprocess.Popen([sys.executable, "-W", "ignore", "-c", script],
489 stdout=subprocess.PIPE,
490 stderr=subprocess.PIPE)
491 stdout, stderr = p.communicate()
492 stdout = stdout.strip()
493 stdout = stdout.decode('utf-8')
494 stderr = stderr.decode('utf-8')
495 - assert re.match('^Woke up, sleep function is: <.*?sleep.*?>$', stdout), repr(stdout)
496 - stderr = re.sub(r"^\[\d+ refs\]", "", stderr, re.MULTILINE).strip()
500 + 'Woke up, sleep function is: gevent.hub.sleep',
503 # On Python 2, importing pkg_resources tends to result in some 'ImportWarning'
504 # being printed to stderr about packages missing __init__.py; the -W ignore is...
506 @@ -410,7 +416,7 @@ def __init__(self, should_raise):
507 self.should_raise = should_raise
508 self.thread = threading.Thread(target=self._run,
510 - kwargs={'yet_another': self})
511 + kwargs={'_yet_another': self})
514 def _run(self, _other_ref, _yet_another):
515 @@ -463,7 +469,7 @@ def test_1_join_on_shutdown(self):
516 t = threading.Thread(target=joiningfunc,
517 args=(threading.current_thread(),))
523 self._run_and_join(script)
524 #diff --git a/src/gevent/thread.py b/src/gevent/thread.py
525 #index a1fe41813..14428df3f 100644
526 #--- a/src/gevent/thread.py
527 #+++ b/src/gevent/thread.py
532 #+ if sys.version_info[:2] >= (3, 8):
533 #+ # We can't actually produce a value that "may be used
534 #+ # to identify this particular thread system-wide", right?
535 #+ # Even if we could, I imagine people will want to pass this to
536 #+ # non-Python (native) APIs, so we shouldn't mess with it.
537 #+ __imports__.append('get_native_id')
540 # error = __thread__.error
541 diff --git a/src/gevent/threading.py b/src/gevent/threading.py
542 index c845fd221..64d989584 100644
543 --- a/src/gevent/threading.py
544 +++ b/src/gevent/threading.py
545 @@ -160,41 +160,25 @@ def main_native_thread():
547 # XXX: Issue 18808 breaks us on Python 3.4+.
548 # Thread objects now expect a callback from the interpreter itself
549 - # (threadmodule.c:release_sentinel). Because this never happens
550 + # (threadmodule.c:release_sentinel) when the C-level PyThreadState
551 + # object is being deallocated. Because this never happens
552 # when a greenlet exits, join() and friends will block forever.
553 - # The solution below involves capturing the greenlet when it is
554 - # started and deferring the known broken methods to it.
555 + # Fortunately this is easy to fix: just ensure that the allocation of the
556 + # lock, _set_sentinel, creates a *gevent* lock, and release it when
557 + # we're done. The main _shutdown code is in Python and deals with
560 class Thread(__threading__.Thread):
563 - def is_alive(self):
564 - return bool(self._greenlet)
568 def _set_tstate_lock(self):
569 - self._greenlet = getcurrent()
573 - super(Thread, self).run()
575 - # avoid ref cycles, but keep in __dict__ so we can
576 - # distinguish the started/never-started case
577 - self._greenlet = None
578 - self._stop() # mark as finished
580 - def join(self, timeout=None):
581 - if '_greenlet' not in self.__dict__:
582 - raise RuntimeError("Cannot join an inactive thread")
583 - if self._greenlet is None:
585 - self._greenlet.join(timeout=timeout)
587 - def _wait_for_tstate_lock(self, *args, **kwargs):
588 - # pylint:disable=arguments-differ
589 - raise NotImplementedError()
590 + super(Thread, self)._set_tstate_lock()
591 + greenlet = getcurrent()
592 + greenlet.rawlink(self.__greenlet_finished)
594 + def __greenlet_finished(self, _):
595 + if self._tstate_lock:
596 + self._tstate_lock.release()
599 __implements__.append('Thread')
601 @@ -203,6 +187,8 @@ class Timer(Thread, __threading__.Timer): # pylint:disable=abstract-method,inher
603 __implements__.append('Timer')
605 + _set_sentinel = allocate_lock
606 + __implements__.append('_set_sentinel')
607 # The main thread is patched up with more care
608 # in _gevent_will_monkey_patch
611 From 632d3c9990f7db27f76245895bb4c2717818c5d5 Mon Sep 17 00:00:00 2001
612 From: Jason Madden <jamadden@gmail.com>
613 Date: Fri, 6 Sep 2019 18:26:42 -0500
614 Subject: [PATCH 2/9] Add the 3.8 test cases.
617 src/gevent/_ssl3.py | 14 +
618 src/gevent/monkey.py | 37 +-
619 src/gevent/testing/patched_tests_setup.py | 36 +-
620 src/gevent/testing/sysinfo.py | 3 +
621 src/greentest/3.8/allsans.pem | 81 +
622 src/greentest/3.8/badcert.pem | 36 +
623 src/greentest/3.8/badkey.pem | 40 +
624 src/greentest/3.8/capath/4e1295a3.0 | 14 +
625 src/greentest/3.8/capath/5ed36f99.0 | 41 +
626 src/greentest/3.8/capath/6e88d7b8.0 | 14 +
627 src/greentest/3.8/capath/99d0fa06.0 | 41 +
628 src/greentest/3.8/capath/b1930218.0 | 26 +
629 src/greentest/3.8/capath/ceff1710.0 | 26 +
630 src/greentest/3.8/ffdh3072.pem | 41 +
631 src/greentest/3.8/idnsans.pem | 169 +
632 src/greentest/3.8/keycert.passwd.pem | 68 +
633 src/greentest/3.8/keycert.pem | 66 +
634 src/greentest/3.8/keycert2.pem | 66 +
635 src/greentest/3.8/keycert3.pem | 164 +
636 src/greentest/3.8/keycert4.pem | 164 +
637 src/greentest/3.8/keycertecc.pem | 106 +
638 src/greentest/3.8/nokia.pem | 31 +
639 src/greentest/3.8/nullbytecert.pem | 90 +
640 src/greentest/3.8/nullcert.pem | 0
641 src/greentest/3.8/pycacert.pem | 99 +
642 src/greentest/3.8/pycakey.pem | 40 +
643 src/greentest/3.8/revocation.crl | 14 +
644 src/greentest/3.8/secp384r1.pem | 7 +
645 .../3.8/selfsigned_pythontestdotnet.pem | 34 +
646 src/greentest/3.8/ssl_cert.pem | 26 +
647 src/greentest/3.8/ssl_key.passwd.pem | 42 +
648 src/greentest/3.8/ssl_key.pem | 40 +
649 src/greentest/3.8/talos-2019-0758.pem | 22 +
650 src/greentest/3.8/test_asyncore.py | 834 +++
651 src/greentest/3.8/test_ftplib.py | 1093 +++
652 src/greentest/3.8/test_httplib.py | 1977 ++++++
653 src/greentest/3.8/test_select.py | 81 +
654 src/greentest/3.8/test_selectors.py | 564 ++
655 src/greentest/3.8/test_smtpd.py | 1013 +++
656 src/greentest/3.8/test_socket.py | 6278 +++++++++++++++++
657 src/greentest/3.8/test_ssl.py | 4652 ++++++++++++
658 src/greentest/3.8/test_subprocess.py | 3330 +++++++++
659 src/greentest/3.8/test_threading.py | 1382 ++++
660 src/greentest/3.8/test_wsgiref.py | 867 +++
661 src/greentest/3.8/version | 1 +
662 45 files changed, 23756 insertions(+), 14 deletions(-)
663 create mode 100644 src/greentest/3.8/allsans.pem
664 create mode 100644 src/greentest/3.8/badcert.pem
665 create mode 100644 src/greentest/3.8/badkey.pem
666 create mode 100644 src/greentest/3.8/capath/4e1295a3.0
667 create mode 100644 src/greentest/3.8/capath/5ed36f99.0
668 create mode 100644 src/greentest/3.8/capath/6e88d7b8.0
669 create mode 100644 src/greentest/3.8/capath/99d0fa06.0
670 create mode 100644 src/greentest/3.8/capath/b1930218.0
671 create mode 100644 src/greentest/3.8/capath/ceff1710.0
672 create mode 100644 src/greentest/3.8/ffdh3072.pem
673 create mode 100644 src/greentest/3.8/idnsans.pem
674 create mode 100644 src/greentest/3.8/keycert.passwd.pem
675 create mode 100644 src/greentest/3.8/keycert.pem
676 create mode 100644 src/greentest/3.8/keycert2.pem
677 create mode 100644 src/greentest/3.8/keycert3.pem
678 create mode 100644 src/greentest/3.8/keycert4.pem
679 create mode 100644 src/greentest/3.8/keycertecc.pem
680 create mode 100644 src/greentest/3.8/nokia.pem
681 create mode 100644 src/greentest/3.8/nullbytecert.pem
682 create mode 100644 src/greentest/3.8/nullcert.pem
683 create mode 100644 src/greentest/3.8/pycacert.pem
684 create mode 100644 src/greentest/3.8/pycakey.pem
685 create mode 100644 src/greentest/3.8/revocation.crl
686 create mode 100644 src/greentest/3.8/secp384r1.pem
687 create mode 100644 src/greentest/3.8/selfsigned_pythontestdotnet.pem
688 create mode 100644 src/greentest/3.8/ssl_cert.pem
689 create mode 100644 src/greentest/3.8/ssl_key.passwd.pem
690 create mode 100644 src/greentest/3.8/ssl_key.pem
691 create mode 100644 src/greentest/3.8/talos-2019-0758.pem
692 create mode 100644 src/greentest/3.8/test_asyncore.py
693 create mode 100644 src/greentest/3.8/test_ftplib.py
694 create mode 100644 src/greentest/3.8/test_httplib.py
695 create mode 100644 src/greentest/3.8/test_select.py
696 create mode 100644 src/greentest/3.8/test_selectors.py
697 create mode 100644 src/greentest/3.8/test_smtpd.py
698 create mode 100644 src/greentest/3.8/test_socket.py
699 create mode 100644 src/greentest/3.8/test_ssl.py
700 create mode 100644 src/greentest/3.8/test_subprocess.py
701 create mode 100644 src/greentest/3.8/test_threading.py
702 create mode 100644 src/greentest/3.8/test_wsgiref.py
703 create mode 100644 src/greentest/3.8/version
706 From e349789fff94418ff363c3d7ba8d4cf4a5bcd798 Mon Sep 17 00:00:00 2001
707 From: Jason Madden <jamadden@gmail.com>
708 Date: Fri, 6 Sep 2019 18:27:34 -0500
709 Subject: [PATCH 3/9] Fix lint.
712 src/gevent/tests/test__core_fork.py | 1 -
713 1 file changed, 1 deletion(-)
715 diff --git a/src/gevent/tests/test__core_fork.py b/src/gevent/tests/test__core_fork.py
716 index 5717cf7d9..20dc2a4d1 100644
717 --- a/src/gevent/tests/test__core_fork.py
718 +++ b/src/gevent/tests/test__core_fork.py
720 import multiprocessing
723 -from gevent._compat import MAC
725 hub = gevent.get_hub()
728 From 5c5d2bc660b599256734a9f5c9e58c79ec4f1b1b Mon Sep 17 00:00:00 2001
729 From: Jason Madden <jamadden@gmail.com>
730 Date: Fri, 6 Sep 2019 18:28:24 -0500
731 Subject: [PATCH 4/9] Fix the other place that refs the version in .travis.yml.
735 1 file changed, 2 insertions(+), 2 deletions(-)
737 #diff --git a/.travis.yml b/.travis.yml
738 #index 05dc24b12..c956f6ae7 100644
741 #@@ -284,10 +284,10 @@ jobs:
743 # - <<: *test-libuv-jobs
744 # env: TRAVIS_PYTHON_VERSION=3.8
747 # - <<: *test-libev-jobs
748 # env: TRAVIS_PYTHON_VERSION=3.8
749 #- name: libev-cffi36
750 #+ name: libev-cffi38
753 # # 2.7, no-embed. Run the tests that exercise the libraries we
755 From 0a45f740f3afce22c88fd7f9dbe5e30aa7d95336 Mon Sep 17 00:00:00 2001
756 From: Jason Madden <jamadden@gmail.com>
757 Date: Fri, 6 Sep 2019 18:48:09 -0500
758 Subject: [PATCH 5/9] Fix false detection in test__all__.
761 .travis.yml | 7 +++++--
762 src/gevent/subprocess.py | 45 ++++++++++++++++++++++++----------------
763 2 files changed, 32 insertions(+), 20 deletions(-)
765 #diff --git a/.travis.yml b/.travis.yml
766 #index c956f6ae7..f676bb6e7 100644
769 #@@ -198,9 +198,12 @@ jobs:
770 # # We only need to do this on one version, and it should be Python 3, because
771 # # pylint has stopped updating for Python 2.
773 #- # We need pylint, since above we're not installing a requirements file
774 #+ # We need pylint, since above we're not installing a
775 #+ # requirements file. Code added to _ssl3.SSLContext for Python
776 #+ # 3.8 triggers an infinite recursion bug in pylint 2.3.1/astroid 2.2.5
777 #+ # unless we disable inference.
778 # install: pip install pylint
779 #- script: python -m pylint --rcfile=.pylintrc gevent
780 #+ script: python -m pylint --limit-inference-results=1 --rcfile=.pylintrc gevent
781 # env: TRAVIS_PYTHON_VERSION=3.7
784 #diff --git a/src/gevent/subprocess.py b/src/gevent/subprocess.py
785 #index 9b4d13cd2..ed22d863b 100644
786 #--- a/src/gevent/subprocess.py
787 #+++ b/src/gevent/subprocess.py
789 # __implements__.append("_posixsubprocess")
790 # _posixsubprocess = None
793 #- # Using os.posix_spawn() to start subprocesses
794 #- # bypasses our child watchers on certain operating systems,
795 #- # and with certain library versions. Possibly the right
796 #- # fix is to monkey-patch os.posix_spawn like we do os.fork?
797 #- # These have no effect, they're just here to match the stdlib.
798 #- # TODO: When available, given a monkey patch on them, I think
799 #- # we ought to be able to use them if the stdlib has identified them
801 #- __implements__.extend([
802 #- '_use_posix_spawn',
803 #- '_USE_POSIX_SPAWN'
806 #- def _use_posix_spawn():
809 #- _USE_POSIX_SPAWN = False
811 # # Some symbols we define that we expect to export;
812 # # useful for static analysis
813 #@@ -183,6 +165,33 @@ def _use_posix_spawn():
814 # 'CREATE_BREAKAWAY_FROM_JOB'
818 #+ # Using os.posix_spawn() to start subprocesses
819 #+ # bypasses our child watchers on certain operating systems,
820 #+ # and with certain library versions. Possibly the right
821 #+ # fix is to monkey-patch os.posix_spawn like we do os.fork?
822 #+ # These have no effect, they're just here to match the stdlib.
823 #+ # TODO: When available, given a monkey patch on them, I think
824 #+ # we ought to be able to use them if the stdlib has identified them
826 #+ __implements__.extend([
827 #+ '_use_posix_spawn',
830 #+ def _use_posix_spawn():
833 #+ _USE_POSIX_SPAWN = False
835 #+ if __subprocess__._USE_POSIX_SPAWN:
836 #+ __implements__.extend([
837 #+ '_USE_POSIX_SPAWN',
840 #+ __imports__.extend([
841 #+ '_USE_POSIX_SPAWN',
844 # actually_imported = copy_globals(__subprocess__, globals(),
845 # only_names=__imports__,
846 # ignore_missing_names=True)
848 From 412d59d0889bf0d1221c52d902a98a7648f0a829 Mon Sep 17 00:00:00 2001
849 From: Jason Madden <jamadden@gmail.com>
850 Date: Sat, 7 Sep 2019 07:04:09 -0500
851 Subject: [PATCH 6/9] Hmm, why is test__core giving fits on py38/libuv/travis?
852 Can't reproduce locally.
856 src/gevent/libuv/_corecffi_source.c | 16 +++++++++-------
857 2 files changed, 10 insertions(+), 7 deletions(-)
859 #diff --git a/appveyor.yml b/appveyor.yml
860 #index 3e5f84f78..ee660e472 100644
863 #@@ -11,6 +11,7 @@ environment:
864 # # too often we get failures to resolve DNS names or failures
865 # # to connect on AppVeyor.
866 # GEVENTTEST_USE_RESOURCES: "-network"
867 #+ PYTHONTRACEMALLOC: 10
871 diff --git a/src/gevent/libuv/_corecffi_source.c b/src/gevent/libuv/_corecffi_source.c
872 index 83fe82ee9..3b4b8d156 100644
873 --- a/src/gevent/libuv/_corecffi_source.c
874 +++ b/src/gevent/libuv/_corecffi_source.c
875 @@ -150,32 +150,34 @@ static void _gevent_fs_poll_callback3(void* handlep, int status, const uv_stat_t
877 static void gevent_uv_walk_callback_close(uv_handle_t* handle, void* arg)
879 - if( handle && !uv_is_closing(handle) ) {
880 - uv_close(handle, NULL);
882 + if( handle && !uv_is_closing(handle) ) {
883 + uv_close(handle, NULL);
887 static void gevent_close_all_handles(uv_loop_t* loop)
890 uv_walk(loop, gevent_uv_walk_callback_close, NULL);
894 static void gevent_zero_timer(uv_timer_t* handle)
896 - memset(handle, 0, sizeof(uv_timer_t));
897 + memset(handle, 0, sizeof(uv_timer_t));
900 static void gevent_zero_check(uv_check_t* handle)
902 - memset(handle, 0, sizeof(uv_check_t));
903 + memset(handle, 0, sizeof(uv_check_t));
906 static void gevent_zero_prepare(uv_prepare_t* handle)
908 - memset(handle, 0, sizeof(uv_prepare_t));
909 + memset(handle, 0, sizeof(uv_prepare_t));
912 static void gevent_zero_loop(uv_loop_t* handle)
914 - memset(handle, 0, sizeof(uv_loop_t));
915 + memset(handle, 0, sizeof(uv_loop_t));
918 From e52ac513ef66dba47f5312f1870fe2e5b020cb19 Mon Sep 17 00:00:00 2001
919 From: Jason Madden <jamadden@gmail.com>
920 Date: Sat, 7 Sep 2019 07:44:37 -0500
921 Subject: [PATCH 7/9] Disable that failing test on 3.8b4 for now.
924 src/gevent/tests/test__core.py | 7 +++++++
925 src/gevent/tests/test__socket.py | 21 ++++++++++-----------
926 2 files changed, 17 insertions(+), 11 deletions(-)
928 diff --git a/src/gevent/tests/test__core.py b/src/gevent/tests/test__core.py
929 index 1816e0f89..c538ee7aa 100644
930 --- a/src/gevent/tests/test__core.py
931 +++ b/src/gevent/tests/test__core.py
933 from __future__ import absolute_import, print_function, division
937 import gevent.testing as greentest
939 from gevent import core
940 @@ -128,6 +129,12 @@ def destroyOne(self, loop):
941 "See https://ci.appveyor.com/project/denik/gevent/build/1.0.1380/job/lrlvid6mkjtyrhn5#L1103 "
942 "It has also timed out, but only on Appveyor CPython 3.6; local CPython 3.6 does not. "
943 "See https://ci.appveyor.com/project/denik/gevent/build/1.0.1414/job/yn7yi8b53vtqs8lw#L1523")
945 + greentest.LIBUV and greentest.RUNNING_ON_TRAVIS and sys.version_info == (3, 8, 0, 'beta', 4),
946 + "Crashes on 3.8.0b4 on TravisCI. "
947 + "(https://travis-ci.org/gevent/gevent/jobs/582031266#L215) "
948 + "Unable to reproduce locally so far on macOS."
950 class TestWatchersDefaultDestroyed(TestWatchers):
953 #diff --git a/src/gevent/tests/test__socket.py b/src/gevent/tests/test__socket.py
954 #index 48f6ee3ed..4a670cdb0 100644
955 #--- a/src/gevent/tests/test__socket.py
956 #+++ b/src/gevent/tests/test__socket.py
958 # from gevent import monkey; monkey.patch_all()
967 #-import gevent.testing as greentest
968 # from functools import wraps
970 #+from gevent import get_hub
971 #+import gevent.testing as greentest
973 # from gevent.testing import six
974 # from gevent.testing import LARGE_TIMEOUT
975 # from gevent.testing import support
976 #@@ -34,8 +34,7 @@ def errors_are_fatal(*args, **kwargs):
978 # return target(*args, **kwargs)
979 # except: # pylint:disable=bare-except
980 #- traceback.print_exc()
982 #+ get_hub().throw(*sys.exc_info())
984 # _Thread.__init__(self, target=errors_are_fatal, **kwargs)
986 #@@ -91,18 +90,17 @@ def _test_sendall(self, data, match_data=None, client_method='sendall',
988 # def accept_and_read():
992 # conn, _ = self.listener.accept()
993 # r = conn.makefile(mode='rb')
994 # read_data.append(r.read())
997 #- except: # pylint:disable=bare-except
998 #- server_exc_info.append(sys.exc_info())
1002 #- self.listener.close()
1003 #+ for f in (conn, r, self.listener):
1004 #+ if f is not None:
1007 # server = Thread(target=accept_and_read)
1008 # client = self.create_connection(**client_args)
1009 #@@ -114,9 +112,10 @@ def accept_and_read():
1013 #+ assert not server.is_alive()
1014 # if match_data is None:
1015 # match_data = self.long_data
1016 #- self.assertEqual(read_data[0], match_data)
1017 #+ self.assertEqual(read_data, [match_data])
1019 # if server_exc_info:
1020 # six.reraise(*server_exc_info[0])
1022 From 271e58658f7cc839dcde74239c66d1d396d675b7 Mon Sep 17 00:00:00 2001
1023 From: Jason Madden <jamadden@gmail.com>
1024 Date: Sat, 7 Sep 2019 09:02:12 -0500
1025 Subject: [PATCH 8/9] Whoops, fix Python 2.
1028 src/gevent/tests/test__greenness.py | 23 +++++++++++++----------
1029 src/gevent/tests/test__socket.py | 13 ++++++++-----
1030 src/gevent/tests/test__ssl.py | 3 ++-
1031 3 files changed, 23 insertions(+), 16 deletions(-)
1033 diff --git a/src/gevent/tests/test__greenness.py b/src/gevent/tests/test__greenness.py
1034 index da4fc948e..71392971f 100644
1035 --- a/src/gevent/tests/test__greenness.py
1036 +++ b/src/gevent/tests/test__greenness.py
1038 import gevent.testing as greentest
1042 -except ImportError:
1043 from urllib import request as urllib2
1045 - import BaseHTTPServer
1046 -except ImportError:
1047 from http import server as BaseHTTPServer
1048 + from http.server import SimpleHTTPRequestHandler
1049 +except ImportError:
1052 + import BaseHTTPServer
1053 + from SimpleHTTPServer import SimpleHTTPRequestHandler
1057 from gevent.testing import params
1058 @@ -47,7 +49,8 @@ class TestGreenness(greentest.TestCase):
1060 server_address = params.DEFAULT_BIND_ADDR_TUPLE
1061 BaseHTTPServer.BaseHTTPRequestHandler.protocol_version = "HTTP/1.0"
1062 - self.httpd = BaseHTTPServer.HTTPServer(server_address, BaseHTTPServer.BaseHTTPRequestHandler)
1063 + self.httpd = BaseHTTPServer.HTTPServer(server_address,
1064 + SimpleHTTPRequestHandler)
1065 self.httpd.request_count = 0
1068 @@ -62,10 +65,10 @@ def test_urllib2(self):
1069 server = gevent.spawn(self.serve)
1071 port = self.httpd.socket.getsockname()[1]
1072 - with self.assertRaises(urllib2.HTTPError) as exc:
1073 - urllib2.urlopen('http://127.0.0.1:%s' % port)
1074 - self.assertEqual(exc.exception.code, 501)
1076 + rsp = urllib2.urlopen('http://127.0.0.1:%s' % port)
1080 self.assertEqual(self.httpd.request_count, 1)
1083 #diff --git a/src/gevent/tests/test__socket.py b/src/gevent/tests/test__socket.py
1084 #index 4a670cdb0..195ef1af5 100644
1085 #--- a/src/gevent/tests/test__socket.py
1086 #+++ b/src/gevent/tests/test__socket.py
1089 # from functools import wraps
1091 #-from gevent import get_hub
1092 #+from gevent import getcurrent
1093 # import gevent.testing as greentest
1095 # from gevent.testing import six
1096 #@@ -34,7 +34,7 @@ def errors_are_fatal(*args, **kwargs):
1098 # return target(*args, **kwargs)
1099 # except: # pylint:disable=bare-except
1100 #- get_hub().throw(*sys.exc_info())
1101 #+ getcurrent().parent.throw(*sys.exc_info())
1103 # _Thread.__init__(self, target=errors_are_fatal, **kwargs)
1105 #@@ -95,13 +95,16 @@ def accept_and_read():
1106 # conn, _ = self.listener.accept()
1107 # r = conn.makefile(mode='rb')
1108 # read_data.append(r.read())
1112 #- for f in (conn, r, self.listener):
1113 #+ # Order matters. On Python 2, if we close the
1114 #+ # connection before closing the makefile,
1115 #+ # test__ssl fails because the underlying socket
1116 #+ # has been deleted.
1117 #+ for f in (r, conn, self.listener):
1122 # server = Thread(target=accept_and_read)
1123 # client = self.create_connection(**client_args)
1125 #diff --git a/src/gevent/tests/test__ssl.py b/src/gevent/tests/test__ssl.py
1126 #index 97b70bd98..537e70773 100644
1127 #--- a/src/gevent/tests/test__ssl.py
1128 #+++ b/src/gevent/tests/test__ssl.py
1130 #+from __future__ import print_function, division, absolute_import
1131 # from gevent import monkey; monkey.patch_all()
1135 # import gevent.testing as greentest
1136 # # Be careful not to have TestTCP as a bare attribute in this module,
1137 # # even aliased, to avoid running duplicate tests
1138 #-import test__socket
1139 #+from gevent.tests import test__socket
1144 From 158ccf26ff07af1c0737307f67ed118e7a1d6ac1 Mon Sep 17 00:00:00 2001
1145 From: Jason Madden <jamadden@gmail.com>
1146 Date: Sat, 7 Sep 2019 09:27:19 -0500
1147 Subject: [PATCH 9/9] Implement verify_client_post_handshake; appveyor has TLS
1148 1.3 now so those tests are running there.
1152 src/gevent/_ssl3.py | 5 +++++
1153 src/gevent/tests/known_failures.py | 9 +++++++++
1154 src/gevent/tests/test__socket.py | 3 ++-
1155 4 files changed, 18 insertions(+), 1 deletion(-)
1157 #diff --git a/CHANGES.rst b/CHANGES.rst
1158 #index 53f4398d2..a4ab7a7ab 100644
1163 # - Improve the way joining the main thread works on Python 3.
1165 #+- Implement ``SSLSocket.verify_client_post_handshake()`` when available.
1167 # 1.5a1 (2019-05-02)
1168 # ==================
1170 diff --git a/src/gevent/_ssl3.py b/src/gevent/_ssl3.py
1171 index d3de81034..29988cff0 100644
1172 --- a/src/gevent/_ssl3.py
1173 +++ b/src/gevent/_ssl3.py
1174 @@ -678,6 +678,11 @@ def get_channel_binding(self, cb_type="tls-unique"):
1176 return self._sslobj.tls_unique_cb()
1178 + def verify_client_post_handshake(self):
1179 + # Only present in 3.7.1+; an attributeerror is alright
1181 + return self._sslobj.verify_client_post_handshake()
1182 + raise ValueError("No SSL wrapper around " + str(self))
1184 # Python does not support forward declaration of types
1185 SSLContext.sslsocket_class = SSLSocket
1186 diff --git a/src/gevent/tests/known_failures.py b/src/gevent/tests/known_failures.py
1187 index eb1db0da1..d5f38ba91 100644
1188 --- a/src/gevent/tests/known_failures.py
1189 +++ b/src/gevent/tests/known_failures.py
1190 @@ -351,6 +351,15 @@
1191 'test__example_webproxy.py',
1195 + # Strange failures sometimes, but only on Python 3.7, reporting
1196 + # "ConnectionAbortedError: [WinError 10053] An established
1197 + # connection was aborted by the software in your host machine"
1198 + # when we've done no such thing. Try running not in parallel
1201 + 'test__server.py',
1205 if APPVEYOR or TRAVIS:
1206 #diff --git a/src/gevent/tests/test__socket.py b/src/gevent/tests/test__socket.py
1207 #index 195ef1af5..4976d5767 100644
1208 #--- a/src/gevent/tests/test__socket.py
1209 #+++ b/src/gevent/tests/test__socket.py
1210 #@@ -29,12 +29,13 @@ class Thread(_Thread):
1212 # def __init__(self, **kwargs):
1213 # target = kwargs.pop('target')
1214 #+ caller = getcurrent()
1216 # def errors_are_fatal(*args, **kwargs):
1218 # return target(*args, **kwargs)
1219 # except: # pylint:disable=bare-except
1220 #- getcurrent().parent.throw(*sys.exc_info())
1221 #+ caller.throw(*sys.exc_info())
1223 # _Thread.__init__(self, target=errors_are_fatal, **kwargs)
1225 --- gevent-1.4.0/src/gevent/thread.py.orig 2019-01-04 12:51:44.000000000 +0100
1226 +++ gevent-1.4.0/src/gevent/thread.py 2020-01-03 21:14:08.922889769 +0100
1231 + if sys.version_info[:2] >= (3, 8):
1232 + __imports__.append('get_native_id')
1233 error = __thread__.error
1234 from gevent._compat import PY3
1235 from gevent._compat import PYPY
1236 --- gevent-1.4.0/src/gevent/_tblib.py.orig 2019-01-04 12:51:44.000000000 +0100
1237 +++ gevent-1.4.0/src/gevent/_tblib.py 2020-01-03 21:25:28.152543399 +0100
1240 f_code = current.tb_frame.f_code
1241 code = compile('\n' * (current.tb_lineno - 1) + 'raise __traceback_maker', current.tb_frame.f_code.co_filename, 'exec')
1243 + if hasattr(code, "replace"):
1244 + # Python 3.8 and newer
1245 + code = code.replace(co_argcount=0, co_freevars=(), co_cellvars=())
1248 0, code.co_kwonlyargcount,
1249 --- gevent-1.4.0/src/gevent/_socketcommon.py.orig 2019-01-04 12:51:44.000000000 +0100
1250 +++ gevent-1.4.0/src/gevent/_socketcommon.py 2020-01-06 15:00:51.236688228 +0100
1252 from gevent._compat import string_types, integer_types, PY3
1253 from gevent._util import copy_globals
1255 +if sys.version_info[:2] >= (3, 8):
1256 + __imports__.extend([
1258 + 'has_dualstack_ipv6'
1261 is_windows = sys.platform == 'win32'
1262 is_macos = sys.platform == 'darwin'