diff options
author | Brian Curtin <brian.curtin@gmail.com> | 2010-04-12 17:16:38 (GMT) |
---|---|---|
committer | Brian Curtin <brian.curtin@gmail.com> | 2010-04-12 17:16:38 (GMT) |
commit | eb24d7498f3e34586fee24209f5630a58bb1a04b (patch) | |
tree | 2618500362c3b75e9ff541971954b57d14d66a86 /Lib | |
parent | b2416e54b15d90b4a1bc917897912061830b42fc (diff) | |
download | cpython-eb24d7498f3e34586fee24209f5630a58bb1a04b.zip cpython-eb24d7498f3e34586fee24209f5630a58bb1a04b.tar.gz cpython-eb24d7498f3e34586fee24209f5630a58bb1a04b.tar.bz2 |
Port #1220212 (os.kill for Win32) to py3k.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/subprocess.py | 4 | ||||
-rw-r--r-- | Lib/test/test_os.py | 66 | ||||
-rw-r--r-- | Lib/test/win_console_handler.py | 43 | ||||
-rw-r--r-- | Lib/unittest/test/test_break.py | 2 |
4 files changed, 112 insertions, 3 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 89cf9bf..ec391cb 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -980,6 +980,10 @@ class Popen(object): """ if sig == signal.SIGTERM: self.terminate() + elif sig == signal.CTRL_C_EVENT: + os.kill(self.pid, signal.CTRL_C_EVENT) + elif sig == signal.CTRL_BREAK_EVENT: + os.kill(self.pid, signal.CTRL_BREAK_EVENT) else: raise ValueError("Only SIGTERM is supported on Windows") diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 395402b..705bdc7 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -7,9 +7,13 @@ import errno import unittest import warnings import sys +import signal +import subprocess +import time import shutil from test import support + # Tests creating TESTFN class FileTests(unittest.TestCase): def setUp(self): @@ -739,7 +743,6 @@ if sys.platform != 'win32': def test_setreuid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). - import subprocess subprocess.check_call([ sys.executable, '-c', 'import os,sys;os.setreuid(-1,-1);sys.exit(0)']) @@ -754,7 +757,6 @@ if sys.platform != 'win32': def test_setregid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). - import subprocess subprocess.check_call([ sys.executable, '-c', 'import os,sys;os.setregid(-1,-1);sys.exit(0)']) @@ -798,6 +800,63 @@ else: class Pep383Tests(unittest.TestCase): pass +@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") +class Win32KillTests(unittest.TestCase): + def _kill(self, sig, *args): + # Send a subprocess a signal (or in some cases, just an int to be + # the return value) + proc = subprocess.Popen(*args) + os.kill(proc.pid, sig) + self.assertEqual(proc.wait(), sig) + + def test_kill_sigterm(self): + # SIGTERM doesn't mean anything special, but make sure it works + self._kill(signal.SIGTERM, [sys.executable]) + + def test_kill_int(self): + # os.kill on Windows can take an int which gets set as the exit code + self._kill(100, [sys.executable]) + + def _kill_with_event(self, event, name): + # Run a script which has console control handling enabled. + proc = subprocess.Popen([sys.executable, + os.path.join(os.path.dirname(__file__), + "win_console_handler.py")], + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) + # Let the interpreter startup before we send signals. See #3137. + time.sleep(0.5) + os.kill(proc.pid, event) + # proc.send_signal(event) could also be done here. + # Allow time for the signal to be passed and the process to exit. + time.sleep(0.5) + if not proc.poll(): + # Forcefully kill the process if we weren't able to signal it. + os.kill(proc.pid, signal.SIGINT) + self.fail("subprocess did not stop on {}".format(name)) + + @unittest.skip("subprocesses aren't inheriting CTRL+C property") + def test_CTRL_C_EVENT(self): + from ctypes import wintypes + import ctypes + + # Make a NULL value by creating a pointer with no argument. + NULL = ctypes.POINTER(ctypes.c_int)() + SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler + SetConsoleCtrlHandler.argtypes = (ctypes.POINTER(ctypes.c_int), + wintypes.BOOL) + SetConsoleCtrlHandler.restype = wintypes.BOOL + + # Calling this with NULL and FALSE causes the calling process to + # handle CTRL+C, rather than ignore it. This property is inherited + # by subprocesses. + SetConsoleCtrlHandler(NULL, 0) + + self._kill_with_event(signal.CTRL_C_EVENT, "CTRL_C_EVENT") + + def test_CTRL_BREAK_EVENT(self): + self._kill_with_event(signal.CTRL_BREAK_EVENT, "CTRL_BREAK_EVENT") + + def test_main(): support.run_unittest( ArgTests, @@ -812,7 +871,8 @@ def test_main(): Win32ErrorTests, TestInvalidFD, PosixUidGidTests, - Pep383Tests + Pep383Tests, + Win32KillTests ) if __name__ == "__main__": diff --git a/Lib/test/win_console_handler.py b/Lib/test/win_console_handler.py new file mode 100644 index 0000000..17bbe1a --- /dev/null +++ b/Lib/test/win_console_handler.py @@ -0,0 +1,43 @@ +"""Script used to test os.kill on Windows, for issue #1220212 + +This script is started as a subprocess in test_os and is used to test the +CTRL_C_EVENT and CTRL_BREAK_EVENT signals, which requires a custom handler +to be written into the kill target. + +See http://msdn.microsoft.com/en-us/library/ms685049%28v=VS.85%29.aspx for a +similar example in C. +""" + +from ctypes import wintypes +import signal +import ctypes + +# Function prototype for the handler function. Returns BOOL, takes a DWORD. +HandlerRoutine = wintypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) + +def _ctrl_handler(sig): + """Handle a sig event and return 0 to terminate the process""" + if sig == signal.CTRL_C_EVENT: + pass + elif sig == signal.CTRL_BREAK_EVENT: + pass + else: + print("UNKNOWN EVENT") + return 0 + +ctrl_handler = HandlerRoutine(_ctrl_handler) + + +SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler +SetConsoleCtrlHandler.argtypes = (HandlerRoutine, wintypes.BOOL) +SetConsoleCtrlHandler.restype = wintypes.BOOL + +if __name__ == "__main__": + # Add our console control handling function with value 1 + if not SetConsoleCtrlHandler(ctrl_handler, 1): + print("Unable to add SetConsoleCtrlHandler") + exit(-1) + + # Do nothing but wait for the signal + while True: + pass diff --git a/Lib/unittest/test/test_break.py b/Lib/unittest/test/test_break.py index e3fae34..9108a2c 100644 --- a/Lib/unittest/test/test_break.py +++ b/Lib/unittest/test/test_break.py @@ -1,6 +1,7 @@ import gc import io import os +import sys import signal import weakref @@ -8,6 +9,7 @@ import unittest @unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill") +@unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows") class TestBreak(unittest.TestCase): def setUp(self): |