Commit 4a30909d authored by Jason Madden's avatar Jason Madden

Fix built-in gevent tests for Python 3.10

parent 1557a367
......@@ -3,3 +3,5 @@ Add support for Python 3.10.
As part of this, the minimum required greenlet version was increased
to 1.1.0 (on CPython), and the minimum version of Cython needed to
build gevent from a source checkout is 3.0a9.
Note that the dnspython resolver is not available on Python 3.10.
......@@ -71,7 +71,10 @@ supported.
| | |
| | |
+-------+-------+
|3.5.x | 20.9.0|
| | |
| | |
+-------+-------+
Installation
============
......@@ -145,11 +148,13 @@ events
will be removed in gevent 21.0.
dnspython
Enables the new pure-Python resolver, backed by `dnspython
Enables a pure-Python resolver, backed by `dnspython
<https://pypi.org/project/dnspython>`_. On Python 2, this also
includes `idna <https://pypi.org/project/idna>`_. They can be
installed with the ``dnspython`` extra.
.. note:: This is not compatible with Python 3.10 or dnspython 2.
monitor
Enhancements to gevent's self-monitoring capabilities. This
includes the `psutil <https://pypi.org/project/psutil>`_ library
......
......@@ -299,8 +299,10 @@ del _to_cythonize
## Extras
EXTRA_DNSPYTHON = [
'dnspython >= 1.16.0, < 2.0',
'idna',
# We're not currently compatible with 2.0, and dnspython 1.x isn't
# compatible weth Python 3.10 because of the removal of ``collections.MutableMapping``.
'dnspython >= 1.16.0, < 2.0; python_version < "3.10"',
'idna; python_version < "3.10"',
]
EXTRA_EVENTS = [
# No longer does anything, but the extra must stay around
......
......@@ -82,6 +82,7 @@ from .sysinfo import RUNNING_ON_CI
from .sysinfo import RESOLVER_NOT_SYSTEM
from .sysinfo import RESOLVER_DNSPYTHON
from .sysinfo import RESOLVER_ARES
from .sysinfo import resolver_dnspython_available
from .sysinfo import EXPECT_POOR_TIMER_RESOLUTION
......@@ -107,6 +108,7 @@ from .skipping import skipOnPurePython
from .skipping import skipWithCExtensions
from .skipping import skipOnLibuvOnTravisOnCPython27
from .skipping import skipOnPy37
from .skipping import skipOnPy310
from .skipping import skipOnPy3
from .skipping import skipWithoutResource
from .skipping import skipWithoutExternalNetwork
......
......@@ -48,6 +48,7 @@ skipOnPyPyOnWindows = _do_not_skip
skipOnPy2 = unittest.skip if sysinfo.PY2 else _do_not_skip
skipOnPy3 = unittest.skip if sysinfo.PY3 else _do_not_skip
skipOnPy37 = unittest.skip if sysinfo.PY37 else _do_not_skip
skipOnPy310 = unittest.skip if sysinfo.PY310 else _do_not_skip
skipOnPurePython = unittest.skip if sysinfo.PURE_PYTHON else _do_not_skip
skipWithCExtensions = unittest.skip if not sysinfo.PURE_PYTHON else _do_not_skip
......
......@@ -66,6 +66,7 @@ PY36 = None
PY37 = None
PY38 = None
PY39 = None
PY310 = None
NON_APPLICABLE_SUFFIXES = ()
if sys.version_info[0] >= 3:
......@@ -83,6 +84,8 @@ if sys.version_info[0] >= 3:
PY38 = True
if sys.version_info[1] >= 9:
PY39 = True
if sys.version_info[1] >= 10:
PY310 = True
elif sys.version_info[0] == 2:
# Any python 2
......@@ -188,3 +191,12 @@ def libev_supports_linux_iouring():
from platform import release
return system() == 'Linux' and LooseVersion(release() or '0') >= LooseVersion('5.3')
def resolver_dnspython_available():
# Try hard not to leave around junk we don't have to.
import pkg_resources
try:
pkg_resources.get_distribution('dnspython')
except pkg_resources.DistributionNotFound:
return False
return True
......@@ -246,7 +246,7 @@ def start(command, quiet=False, **kwargs):
if timeout is not None:
t = get_original('threading', 'Timer')(timeout, kill, args=(popen, ))
popen.timer = t
t.setDaemon(True)
t.daemon = True
t.start()
popen.timer = t
return popen
......
......@@ -146,6 +146,8 @@ class TestPeriodicMonitoringThread(greentest.TestCase):
super(TestPeriodicMonitoringThread, self).setUp()
self.monitor_thread = gevent.config.monitor_thread
gevent.config.monitor_thread = True
from gevent.monkey import get_original
self.lock = get_original('threading', 'Lock')()
self.monitor_fired = 0
self.monitored_hubs = set()
self._reset_hub()
......@@ -162,9 +164,10 @@ class TestPeriodicMonitoringThread(greentest.TestCase):
super(TestPeriodicMonitoringThread, self).tearDown()
def _monitor(self, hub):
self.monitor_fired += 1
if self.monitored_hubs is not None:
self.monitored_hubs.add(hub)
with self.lock:
self.monitor_fired += 1
if self.monitored_hubs is not None:
self.monitored_hubs.add(hub)
def test_config(self):
self.assertEqual(0.1, gevent.config.max_blocking_time)
......@@ -177,7 +180,7 @@ class TestPeriodicMonitoringThread(greentest.TestCase):
def monitor_cond(_hub):
cond.acquire()
cond.notifyAll()
cond.notify_all()
cond.release()
if kill:
# Only run once. Especially helpful on PyPy, where
......@@ -245,15 +248,29 @@ class TestPeriodicMonitoringThread(greentest.TestCase):
threadpool = hub.threadpool
worker_hub = threadpool.apply(get_hub)
stream = worker_hub.exception_stream = NativeStrIO()
assert hub is not worker_hub
stream = NativeStrIO()
# It does not have a monitoring thread yet
self.assertIsNone(worker_hub.periodic_monitoring_thread)
# So switch to it and give it one.
threadpool.apply(gevent.sleep, (0.01,))
self.assertIsNotNone(worker_hub.periodic_monitoring_thread)
worker_monitor = worker_hub.periodic_monitoring_thread
worker_monitor.add_monitoring_function(self._monitor, 0.1)
# So switch to it and give it one by letting it run.
# XXX: Python 3.10 appears to have made some changes in the memory model.
# Specifically, reading values from the background that are set in the
# background hub *from this thread* is flaky. It takes them awhile to show up.
# Really, that's correct and expected from a standard C point of view, as we
# don't insert any memory barriers or things like that. It just always used to
# work in the past. So now, rather than read them directly, we need to read them
# from the background thread itself. The same, apparently, goes for
# writing.
# Need to figure out what exactly the change was.
def task():
get_hub().exception_stream = stream
gevent.sleep(0.01)
mon = get_hub().periodic_monitoring_thread
mon.add_monitoring_function(self._monitor, 0.1)
return mon
worker_monitor = threadpool.apply(task)
self.assertIsNotNone(worker_monitor)
return worker_hub, stream, worker_monitor
......@@ -272,8 +289,9 @@ class TestPeriodicMonitoringThread(greentest.TestCase):
# We did run the monitor in the worker thread, but it
# did NOT report itself blocked by the worker thread sitting there.
self.assertIn(worker_hub, self.monitored_hubs)
self.assertEqual(stream.getvalue(), '')
with self.lock:
self.assertIn(worker_hub, self.monitored_hubs)
self.assertEqual(stream.getvalue(), '')
@greentest.ignores_leakcheck
def test_blocking_threadpool_thread_one_greenlet(self):
......@@ -295,8 +313,9 @@ class TestPeriodicMonitoringThread(greentest.TestCase):
# We did run the monitor in the worker thread, but it
# did NOT report itself blocked by the worker thread
self.assertIn(worker_hub, self.monitored_hubs)
self.assertEqual(stream.getvalue(), '')
with self.lock:
self.assertIn(worker_hub, self.monitored_hubs)
self.assertEqual(stream.getvalue(), '')
@greentest.ignores_leakcheck
......
......@@ -14,6 +14,8 @@ import os
from gevent import testing as greentest
@unittest.skipUnless(greentest.resolver_dnspython_available(),
"dnspython not available")
class TestDnsPython(unittest.TestCase):
def _run_one(self, mod_name):
......
......@@ -98,6 +98,7 @@ class TestSSL(test__socket.TestTCP):
# raise
@greentest.ignores_leakcheck
@greentest.skipOnPy310("No longer raises SSLError")
def test_empty_send(self):
# Issue 719
# Sending empty bytes with the 'send' method raises
......
......@@ -13,7 +13,7 @@ import threading
def helper():
threading.currentThread()
threading.current_thread()
gevent.sleep(0.2)
......
......@@ -145,13 +145,13 @@ class ThreadTests(unittest.TestCase):
# The ident still must work for the main thread and dummy threads,
# as must the repr and str.
t = threading.currentThread()
t = threading.current_thread()
self.assertFalse(t.ident is None)
str(t)
repr(t)
def f():
t = threading.currentThread()
t = threading.current_thread()
ident.append(t.ident)
str(t)
repr(t)
......
......@@ -25,7 +25,7 @@ t.start()
def trace(frame, event, arg):
if threading is not None:
threading.currentThread()
threading.current_thread()
return trace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment