Commit 42fdaecc authored by Jason Madden's avatar Jason Madden

Add pypy3.7 to Appveyor.

Fixes #1798.

Add test_signal for all versions.

Give up and disable test_signal on PyPy/Win. It hangs without running any tests.

Disable test__threading_vs_settrace on PyPy3/Appveyor. Somehow it fails to launch the script for unknown reasons.
parent 9338965a
......@@ -41,6 +41,13 @@ environment:
# a later point release.
# 64-bit
- PYTHON: "C:\\pypy3.7-v7.3.7-win64"
PYTHON_ID: "pypy3"
PYTHON_EXE: pypy3w
PYTHON_VERSION: "3.7.x"
PYTHON_ARCH: "64"
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
- PYTHON: "C:\\pypy2.7-v7.3.6-win64"
PYTHON_ID: "pypy"
PYTHON_EXE: pypy
......@@ -168,6 +175,12 @@ install:
}
7z x -y "${env:PYTMP}\pypy2-v7.3.6-win64.zip" -oC:\ | Out-Null;
}
elseif ("${env:PYTHON_ID}" -eq "pypy3") {
if (!(Test-Path "${env:PYTMP}\pypy3.7-v7.3.7-win64.zip")) {
(New-Object Net.WebClient).DownloadFile("https://downloads.python.org/pypy/pypy3.7-v7.3.7-win64.zip", "${env:PYTMP}\pypy3.7-v7.3.7-win64.zip");
}
7z x -y "${env:PYTMP}\pypy3.7-v7.3.7-win64.zip" -oC:\ | Out-Null;
}
elseif (-not(Test-Path($env:PYTHON))) {
& appveyor\install.ps1;
}
......@@ -198,9 +211,8 @@ cache:
build_script:
# Build the compiled extension
# Try to get some things that don't wind up in the pip cache as
# built wheels if they're built during an isolated build.
- "%CMD_IN_ENV% %PYEXE% -m pip install -U wheel cython setuptools cffi"
- "%CMD_IN_ENV% %PYEXE% -m pip install -U wheel"
- "%CMD_IN_ENV% %PYEXE% -m pip install -U setuptools"
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -m pip install -U -e .[test]
test_script:
......@@ -209,6 +221,7 @@ test_script:
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -c "import gevent.core; print(gevent.core.loop)"
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -c "import gevent; print(gevent.config.settings['resolver'].get_options())"
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -c "from gevent._compat import get_clock_info; print(get_clock_info('perf_counter'))"
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -mgevent.tests.known_failures
- if not "%GWHEEL_ONLY%"=="true" %PYEXE% -mgevent.tests --second-chance --config known_failures.py
after_test:
......
Windows: Test and provide binary wheels for PyPy3.7.
Note that there may be issues with subprocesses, signals, and it may
be slow.
......@@ -26,8 +26,8 @@ library and will install the `cffi`_ library by default on Windows.
The cffi library will become the default on all platforms in a future
release of gevent.
This version of gevent also runs on PyPy 7.0 or above. On PyPy, there
are no external dependencies.
This version of gevent also runs on PyPy 7.3.7 (7.3.6 for PyPy2) or
above. On PyPy, there are no external dependencies.
gevent is tested on Windows, macOS, and Linux, and should run on most
other Unix-like operating systems (e.g., FreeBSD, Solaris, etc.)
......@@ -35,7 +35,9 @@ other Unix-like operating systems (e.g., FreeBSD, Solaris, etc.)
.. note::
Windows is supported as a tier 2, "best effort," platform. It is
suitable for development, but not recommended for production.
suitable for development, but not recommended for production. In
particular, PyPy3 on Windows may have issues, especially with
subprocesses.
On Windows using the deprecated libev backend, gevent is
limited to a maximum of 1024 open sockets due to
......
......@@ -180,9 +180,15 @@ disabled_tests = [
# (unless signal handler raises an error) maybe it should?
'test_signal.WakeupSignalTests.test_wakeup_fd_during',
# these rely on os.read raising EINTR which never happens with gevent.os.read
'test_signal.SiginterruptTest.test_without_siginterrupt',
'test_signal.SiginterruptTest.test_siginterrupt_on',
# these rely on os.read raising EINTR which never happens with gevent.os.read
'test_signal.SiginterruptTest.test_siginterrupt_off',
# This one takes forever and relies on threading details
'test_signal.StressTest.test_stress_modifying_handlers',
# This uses an external file, and launches it. This means that it's not
# actually testing gevent because there's no monkey-patch.
'test_signal.PosixTests.test_interprocess_signal',
'test_subprocess.ProcessTestCase.test_leak_fast_process_del_killed',
'test_subprocess.ProcessTestCase.test_zombie_fast_process_del',
......@@ -680,6 +686,34 @@ if PYPY3 and TRAVIS:
'test_socket.InheritanceTest.test_SOCK_CLOEXEC',
]
if PYPY3 and WIN:
disabled_tests += [
# test_httpservers.CGIHTTPServerTestCase all seem to hang.
# There seem to be some general subprocess issues. This is
# ignored entirely from known_failures.py
# This produces:
#
# OSError: [Errno 10014] The system detected an invalid
# pointer address in attempting to use a pointer argument in
# a call
#
# When calling socket.socket(fileno=fd) when we actually
# call ``self._socket =self._gevent_sock_class()``.
'test_socket.GeneralModuleTests.test_socket_fileno',
# This doesn't respect the scope properly
#
# self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, ifindex))
# AssertionError: Tuples differ: ('ff02::1de:c0:face:8d%42', 1234, 0, 42) != ('ff02::1de:c0:face:8d', 1234, 0, 42
#
'test_socket.GeneralModuleTests.test_getaddrinfo_ipv6_scopeid_numeric',
# self.assertEqual(newsock.get_inheritable(), False)
# AssertionError: True != False
'test_socket.InheritanceTest.test_dup',
]
def _make_run_with_original(mod_name, func_name):
@contextlib.contextmanager
def with_orig():
......@@ -1412,6 +1446,8 @@ if PY310:
# We don't currently implement pipesize.
'test_subprocess.ProcessTestCase.test_pipesize_default',
'test_subprocess.ProcessTestCase.test_pipesizes',
# Unknown
'test_signal.SiginterruptTest.test_siginterrupt_off',
]
if TRAVIS:
......
......@@ -808,15 +808,39 @@ def main():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--ignore')
parser.add_argument('--discover', action='store_true')
parser.add_argument('--full', action='store_true')
parser.add_argument('--config', default='known_failures.py')
parser.add_argument("--coverage", action="store_true")
parser.add_argument("--quiet", action="store_true", default=True)
parser.add_argument(
'--discover', action='store_true',
help="Only print the tests found."
)
parser.add_argument(
'--config', default='known_failures.py',
help="The path to the config file containing "
"FAILING_TESTS, IGNORED_TESTS and RUN_ALONE. "
"Defaults to %(default)s."
)
parser.add_argument(
"--coverage", action="store_true",
help="Enable coverage recording with coverage.py."
)
# TODO: Quiet and verbose should be mutually exclusive
parser.add_argument(
"--quiet", action="store_true", default=True,
help="Be quiet. Defaults to %(default)s. Also the "
"GEVENTTEST_QUIET environment variable."
)
parser.add_argument("--verbose", action="store_false", dest='quiet')
parser.add_argument("--debug", action="store_true", default=False)
parser.add_argument("--package", default="gevent.tests")
parser.add_argument(
"--debug", action="store_true", default=False,
help="Enable debug settings. If the GEVENT_DEBUG environment variable is not set, "
"this sets it to 'debug'. This can also enable PYTHONTRACEMALLOC and the debug PYTHONMALLOC "
"allocators, if not already set. Defaults to %(default)s."
)
parser.add_argument(
"--package", default="gevent.tests",
help="Load tests from the given package. Defaults to %(default)s."
)
parser.add_argument(
"--processes", "-j", default=DEFAULT_NWORKERS, type=int,
help="Use up to the given number of parallel processes to execute tests. "
......
......@@ -80,6 +80,7 @@ class _AttrCondition(ConstantCondition):
ConstantCondition.__init__(self, getattr(sysinfo, name), name)
PYPY = _AttrCondition('PYPY')
PYPY3 = _AttrCondition('PYPY3')
PY3 = _AttrCondition('PY3')
PY2 = _AttrCondition('PY2')
OSX = _AttrCondition('OSX')
......@@ -160,12 +161,18 @@ class Multi(object):
def __init__(self):
self._conds = []
def flaky(self, reason='', when=True):
self._conds.append(Flaky(reason, when))
def flaky(self, reason='', when=True, ignore_coverage=NEVER, run_alone=NEVER):
self._conds.append(
Flaky(
reason, when=when,
ignore_coverage=ignore_coverage,
run_alone=run_alone,
)
)
return self
def ignored(self, reason='', when=True):
self._conds.append(Ignored(reason, when))
self._conds.append(Ignored(reason, when=when))
return self
def __set_name__(self, owner, name):
......@@ -279,13 +286,19 @@ class Definitions(DefinitionsBase):
test__backdoor = Flaky(when=LEAKTEST | PYPY)
test__socket_errors = Flaky(when=LEAKTEST)
test_signal = Flaky(
test_signal = Multi().flaky(
"On Travis, this very frequently fails due to timing",
when=TRAVIS & LEAKTEST,
# Partial workaround for the _testcapi issue on PyPy,
# but also because signal delivery can sometimes be slow, and this
# spawn processes of its own
run_alone=APPVEYOR,
).ignored(
"""
This fails to run a single test. It looks like just importing the module
can hang. All I see is the output from patch_all()
""",
when=APPVEYOR & PYPY3
)
test__monkey_sigchld_2 = Ignored(
......@@ -303,10 +316,22 @@ class Definitions(DefinitionsBase):
allocate SSL Context objects, either in Python 2.7 or 3.6.
There must be some library incompatibility. No point even
running them. XXX: Remember to turn this back on.
On Windows, with PyPy3.7 7.3.7, there seem to be all kind of certificate
errors.
""",
when=(PYPY & TRAVIS) | (PYPY3 & WIN)
)
test_httpservers = Ignored(
"""
All the CGI tests hang. There appear to be subprocess problems.
""",
when=PYPY & TRAVIS
when=PYPY3 & WIN
)
test__pywsgi = Ignored(
"""
XXX: Re-enable this when we can investigate more. This has
......@@ -319,16 +344,23 @@ class Definitions(DefinitionsBase):
On Appveyor 3.8.0, for some reason this takes *way* too long, about 100s, which
often goes just over the default timeout of 100s. This makes no sense.
But it also takes nearly that long in 3.7. 3.6 and earlier are much faster.
It also takes just over 100s on PyPy 3.7.
""",
when=(PYPY & TRAVIS & LIBUV) | PY380_EXACTLY,
# https://bitbucket.org/pypy/pypy/issues/2769/systemerror-unexpected-internal-exception
run_alone=(CI & LEAKTEST & PY3) | (PYPY & LIBUV),
# This often takes much longer on PyPy on CI.
options={'timeout': (CI & PYPY, 180)},
)
test_subprocess = Flaky(
test_subprocess = Multi().flaky(
"Unknown, can't reproduce locally; times out one test",
when=PYPY & PY3 & TRAVIS,
ignore_coverage=ALWAYS,
).ignored(
"Tests don't even start before the process times out.",
when=PYPY3 & WIN
)
test__threadpool = Ignored(
......@@ -388,7 +420,7 @@ class Definitions(DefinitionsBase):
off.
""",
when=COVERAGE,
ignore_coverage=ALWAYS
ignore_coverage=ALWAYS,
)
test__hub_join_timeout = Ignored(
......@@ -417,7 +449,7 @@ class Definitions(DefinitionsBase):
"""
On a heavily loaded box, these can all take upwards of 200s.
""",
when=CI & LEAKTEST
when=(CI & LEAKTEST) | (PYPY3 & APPVEYOR)
)
test_socket = RunAlone(
......
......@@ -33,13 +33,16 @@ else:
from gevent.testing.sysinfo import CFFI_BACKEND
from gevent.testing.sysinfo import RUN_COVERAGE
from gevent.testing.sysinfo import WIN
from gevent.testing.sysinfo import PYPY3
class Test(unittest.TestCase):
@unittest.skipIf(CFFI_BACKEND and RUN_COVERAGE,
"Interferes with the timing")
@unittest.skipIf(
(CFFI_BACKEND and RUN_COVERAGE) or (PYPY3 and WIN),
"Interferes with the timing; times out waiting for the child")
def test_hang(self):
# XXX: Why does PyPy3 on Win fail to kill the child? (This was before we switched
# to pypy3w; perhaps that makes a difference?)
if WIN:
from subprocess import CREATE_NEW_PROCESS_GROUP
kwargs = {'creationflags': CREATE_NEW_PROCESS_GROUP}
......@@ -63,7 +66,7 @@ else:
p.send_signal(signal_to_send)
# Wait a few seconds for child process to die. Sometimes signal delivery is delayed
# or even swallowed by Python, so send the signal a few more times if necessary
wait_seconds = 15.0
wait_seconds = 25.0
now = time.time()
midtime = now + (wait_seconds / 2.0)
endtime = time.time() + wait_seconds
......
......@@ -158,6 +158,11 @@ class TestCase(greentest.TestCase):
if greentest.OSX:
# A kernel bug in OS X sometimes results in this
LOCAL_CONN_REFUSED_ERRORS = (errno.EPROTOTYPE,)
elif greentest.WIN and greentest.PYPY3:
# We see WinError 10049: The requested address is not valid
# which is not one of the errors we get anywhere else.
# Not sure which errno constant this is?
LOCAL_CONN_REFUSED_ERRORS = (10049,)
def assertConnectionRefused(self, in_proc_server=True):
try:
......
......@@ -14,7 +14,7 @@ import sys, os, threading, time
# A deadlock-killer, to prevent the
# testsuite to hang forever
def killer():
time.sleep(0.1)
time.sleep(0.2)
sys.stdout.write('..program blocked; aborting!')
sys.stdout.flush()
os._exit(2)
......@@ -137,7 +137,15 @@ class TestTrace(unittest.TestCase):
self.assertTrue(isinstance(e, LoopExit))
def run_script(self, more_args=()):
args = [sys.executable, "-c", script]
if (
greentest.PYPY3
and greentest.RUNNING_ON_APPVEYOR
and sys.version_info[:2] == (3, 7)
):
# Somehow launching the subprocess fails with exit code 1, and
# produces no output. It's not clear why.
self.skipTest("Known to hang on AppVeyor")
args = [sys.executable, "-u", "-c", script]
args.extend(more_args)
rc = subprocess.call(args)
self.assertNotEqual(rc, 2, "interpreter was blocked")
......
import os
import signal
import subprocess
import sys
import time
import unittest
from test import support
class SIGUSR1Exception(Exception):
pass
class InterProcessSignalTests(unittest.TestCase):
def setUp(self):
self.got_signals = {'SIGHUP': 0, 'SIGUSR1': 0, 'SIGALRM': 0}
def sighup_handler(self, signum, frame):
self.got_signals['SIGHUP'] += 1
def sigusr1_handler(self, signum, frame):
self.got_signals['SIGUSR1'] += 1
raise SIGUSR1Exception
def wait_signal(self, child, signame):
if child is not None:
# This wait should be interrupted by exc_class
# (if set)
child.wait()
timeout = support.SHORT_TIMEOUT
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
if self.got_signals[signame]:
return
signal.pause()
self.fail('signal %s not received after %s seconds'
% (signame, timeout))
def subprocess_send_signal(self, pid, signame):
code = 'import os, signal; os.kill(%s, signal.%s)' % (pid, signame)
args = [sys.executable, '-I', '-c', code]
return subprocess.Popen(args)
def test_interprocess_signal(self):
# Install handlers. This function runs in a sub-process, so we
# don't worry about re-setting the default handlers.
signal.signal(signal.SIGHUP, self.sighup_handler)
signal.signal(signal.SIGUSR1, self.sigusr1_handler)
signal.signal(signal.SIGUSR2, signal.SIG_IGN)
signal.signal(signal.SIGALRM, signal.default_int_handler)
# Let the sub-processes know who to send signals to.
pid = str(os.getpid())
with self.subprocess_send_signal(pid, "SIGHUP") as child:
self.wait_signal(child, 'SIGHUP')
self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 0,
'SIGALRM': 0})
with self.assertRaises(SIGUSR1Exception):
with self.subprocess_send_signal(pid, "SIGUSR1") as child:
self.wait_signal(child, 'SIGUSR1')
self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1,
'SIGALRM': 0})
with self.subprocess_send_signal(pid, "SIGUSR2") as child:
# Nothing should happen: SIGUSR2 is ignored
child.wait()
try:
with self.assertRaises(KeyboardInterrupt):
signal.alarm(1)
self.wait_signal(None, 'SIGALRM')
self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1,
'SIGALRM': 0})
finally:
signal.alarm(0)
if __name__ == "__main__":
unittest.main()
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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