Send signal *sig* to the process *pid*. Constants for the specific signals
available on the host platform are defined in the :mod:`signal` module.
- Availability: Unix.
+
+ Windows: The :data:`signal.CTRL_C_EVENT` and
+ :data:`signal.CTRL_BREAK_EVENT` signals are special signals which can
+ only be sent to console processes which share a common console window,
+ e.g., some subprocesses. Any other value for *sig* will cause the process
+ to be unconditionally killed by the TerminateProcess API, and the exit code
+ will be set to *sig*. The Windows version of :func:`kill` additionally takes
+ process handles to be killed.
.. function:: killpg(pgid, sig)
the system are defined by this module.
+.. data:: CTRL_C_EVENT
+
+ The signal corresponding to the CTRL+C keystroke event.
+
+ Availability: Windows.
+
+
+.. data:: CTRL_BREAK_EVENT
+
+ The signal corresponding to the CTRL+BREAK keystroke event.
+
+ Availability: Windows.
+
+
.. data:: NSIG
One more than the number of the highest signal number.
.. note::
- On Windows only SIGTERM is supported so far. It's an alias for
- :meth:`terminate`.
+ On Windows, SIGTERM is an alias for :meth:`terminate`. CTRL_C_EVENT and
+ CTRL_BREAK_EVENT can be sent to processes started with a `creationflags`
+ parameter which includes `CREATE_NEW_PROCESS_GROUP`.
.. versionadded:: 2.6
"""
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")
import unittest
import warnings
import sys
+import signal
+import subprocess
+import time
from test import test_support
warnings.filterwarnings("ignore", "tempnam", RuntimeWarning, __name__)
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)'])
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)'])
class PosixUidGidTests(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.1)
+ 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.1)
+ 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():
test_support.run_unittest(
FileTests,
URandomTests,
Win32ErrorTests,
TestInvalidFD,
- PosixUidGidTests
+ PosixUidGidTests,
+ Win32KillTests
)
if __name__ == "__main__":
--- /dev/null
+"""Script used to test os.kill on Windows, for issue #1220212\r
+\r
+This script is started as a subprocess in test_os and is used to test the\r
+CTRL_C_EVENT and CTRL_BREAK_EVENT signals, which requires a custom handler\r
+to be written into the kill target.\r
+\r
+See http://msdn.microsoft.com/en-us/library/ms685049%28v=VS.85%29.aspx for a\r
+similar example in C.\r
+"""\r
+\r
+from ctypes import wintypes\r
+import signal\r
+import ctypes\r
+\r
+# Function prototype for the handler function. Returns BOOL, takes a DWORD.\r
+HandlerRoutine = wintypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD)\r
+\r
+def _ctrl_handler(sig):\r
+ """Handle a sig event and return 0 to terminate the process"""\r
+ if sig == signal.CTRL_C_EVENT:\r
+ pass\r
+ elif sig == signal.CTRL_BREAK_EVENT:\r
+ pass\r
+ else:\r
+ print("UNKNOWN EVENT")\r
+ return 0\r
+\r
+ctrl_handler = HandlerRoutine(_ctrl_handler)\r
+\r
+\r
+SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler\r
+SetConsoleCtrlHandler.argtypes = (HandlerRoutine, wintypes.BOOL)\r
+SetConsoleCtrlHandler.restype = wintypes.BOOL\r
+\r
+if __name__ == "__main__":\r
+ # Add our console control handling function with value 1\r
+ if not SetConsoleCtrlHandler(ctrl_handler, 1):\r
+ print("Unable to add SetConsoleCtrlHandler")\r
+ exit(-1)\r
+\r
+ # Do nothing but wait for the signal\r
+ input()\r
import gc
import os
+import sys
import signal
import weakref
@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):
}
#endif
+#ifdef MS_WINDOWS
+PyDoc_STRVAR(win32_kill__doc__,
+"kill(pid, sig)\n\n\
+Kill a process with a signal.");
+
+static PyObject *
+win32_kill(PyObject *self, PyObject *args)
+{
+ PyObject *result, handle_obj;
+ DWORD pid, sig, err;
+ HANDLE handle;
+
+ if (!PyArg_ParseTuple(args, "kk:kill", &pid, &sig))
+ return NULL;
+
+ /* Console processes which share a common console can be sent CTRL+C or
+ CTRL+BREAK events, provided they handle said events. */
+ if (sig == CTRL_C_EVENT || sig == CTRL_BREAK_EVENT) {
+ if (GenerateConsoleCtrlEvent(sig, pid) == 0) {
+ err = GetLastError();
+ PyErr_SetFromWindowsErr(err);
+ }
+ else
+ Py_RETURN_NONE;
+ }
+
+ /* If the signal is outside of what GenerateConsoleCtrlEvent can use,
+ attempt to open and terminate the process. */
+ handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
+ if (handle == NULL) {
+ err = GetLastError();
+ return PyErr_SetFromWindowsErr(err);
+ }
+
+ if (TerminateProcess(handle, sig) == 0) {
+ err = GetLastError();
+ result = PyErr_SetFromWindowsErr(err);
+ } else {
+ Py_INCREF(Py_None);
+ result = Py_None;
+ }
+
+ CloseHandle(handle);
+ return result;
+}
+#endif /* MS_WINDOWS */
+
#ifdef HAVE_PLOCK
#ifdef HAVE_SYS_LOCK_H
{"popen3", win32_popen3, METH_VARARGS},
{"popen4", win32_popen4, METH_VARARGS},
{"startfile", win32_startfile, METH_VARARGS, win32_startfile__doc__},
+ {"kill", win32_kill, METH_VARARGS, win32_kill__doc__},
#else
#if defined(PYOS_OS2) && defined(PYCC_GCC)
{"popen2", os2emx_popen2, METH_VARARGS},
#include "intrcheck.h"
#ifdef MS_WINDOWS
+#include <Windows.h>
#ifdef HAVE_PROCESS_H
#include <process.h>
#endif
PyDict_SetItemString(d, "ItimerError", ItimerError);
#endif
+#ifdef CTRL_C_EVENT
+ x = PyInt_FromLong(CTRL_C_EVENT);
+ PyDict_SetItemString(d, "CTRL_C_EVENT", x);
+ Py_DECREF(x);
+#endif
+
+#ifdef CTRL_BREAK_EVENT
+ x = PyInt_FromLong(CTRL_BREAK_EVENT);
+ PyDict_SetItemString(d, "CTRL_BREAK_EVENT", x);
+ Py_DECREF(x);
+#endif
+
if (!PyErr_Occurred())
return;
defint(d, "INFINITE", INFINITE);
defint(d, "WAIT_OBJECT_0", WAIT_OBJECT_0);
defint(d, "CREATE_NEW_CONSOLE", CREATE_NEW_CONSOLE);
+ defint(d, "CREATE_NEW_PROCESS_GROUP", CREATE_NEW_PROCESS_GROUP);
}