You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

92 lines
3.5 KiB
Python

5 years ago
'''Test for GitHub issues 461 and 471.
When moving to Python 3, handling of KeyboardInterrupt exceptions caused
by a Ctrl-C raised an exception while printing the traceback for a
greenlet preventing the process from exiting. This test tests for proper
handling of KeyboardInterrupt.
'''
import sys
if sys.argv[1:] == ['subprocess']: # pragma: no cover
import gevent
def task():
sys.stdout.write('ready\n')
sys.stdout.flush()
gevent.sleep(30)
try:
gevent.spawn(task).get()
except KeyboardInterrupt:
pass
sys.exit(0)
else:
import signal
from subprocess import Popen, PIPE
import time
import unittest
import gevent.testing as greentest
from gevent.testing.sysinfo import CFFI_BACKEND
from gevent.testing.sysinfo import RUN_COVERAGE
from gevent.testing.sysinfo import WIN
class Test(unittest.TestCase):
@unittest.skipIf(CFFI_BACKEND and RUN_COVERAGE,
"Interferes with the timing")
def test_hang(self):
if WIN:
from subprocess import CREATE_NEW_PROCESS_GROUP
kwargs = {'creationflags': CREATE_NEW_PROCESS_GROUP}
else:
kwargs = {}
p = Popen([sys.executable, __file__, 'subprocess'], stdout=PIPE, **kwargs)
line = p.stdout.readline()
if not isinstance(line, str):
line = line.decode('ascii')
# Windows needs the \n in the string to write (because of buffering), but
# because of newline handling it doesn't make it through the read; whereas
# it does on other platforms. Universal newlines is broken on Py3, so the best
# thing to do is to strip it
line = line.strip()
self.assertEqual(line, 'ready')
# On Windows, we have to send the CTRL_BREAK_EVENT (which seems to terminate the process); SIGINT triggers
# "ValueError: Unsupported signal: 2". The CTRL_C_EVENT is ignored on Python 3 (but not Python 2).
# So this test doesn't test much on Windows.
signal_to_send = signal.SIGINT if not WIN else getattr(signal, 'CTRL_BREAK_EVENT')
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
now = time.time()
midtime = now + (wait_seconds / 2.0)
endtime = time.time() + wait_seconds
while time.time() < endtime:
if p.poll() is not None:
break
if time.time() > midtime:
p.send_signal(signal_to_send)
midtime = endtime + 1 # only once
time.sleep(0.1)
else:
# Kill unresponsive child and exit with error 1
p.terminate()
p.wait()
raise AssertionError("Failed to wait for child")
# If we get here, it's because we caused the process to exit; it
# didn't hang. Under Windows, however, we have to use CTRL_BREAK_EVENT,
# which has an arbitrary returncode depending on versions (so does CTRL_C_EVENT
# on Python 2). We still
# count this as success.
self.assertEqual(p.returncode if not WIN else 0, 0)
p.stdout.close()
if __name__ == '__main__':
greentest.main()