mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 19:34:08 +00:00 
			
		
		
		
	Port #1220212 (os.kill for Win32) to py3k.
This commit is contained in:
		
							parent
							
								
									b2416e54b1
								
							
						
					
					
						commit
						eb24d7498f
					
				
					 10 changed files with 200 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -1491,7 +1491,14 @@ written in Python, such as a mail server's external command delivery program.
 | 
			
		|||
 | 
			
		||||
   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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,6 +74,20 @@ The variables defined in the :mod:`signal` module are:
 | 
			
		|||
   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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -373,8 +373,9 @@ Instances of the :class:`Popen` class have the following methods:
 | 
			
		|||
 | 
			
		||||
   .. 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`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. method:: Popen.terminate()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										43
									
								
								Lib/test/win_console_handler.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								Lib/test/win_console_handler.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4171,6 +4171,53 @@ posix_killpg(PyObject *self, PyObject *args)
 | 
			
		|||
}
 | 
			
		||||
#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
 | 
			
		||||
| 
						 | 
				
			
			@ -7200,6 +7247,7 @@ static PyMethodDef posix_methods[] = {
 | 
			
		|||
#endif /* HAVE_PLOCK */
 | 
			
		||||
#ifdef MS_WINDOWS
 | 
			
		||||
	{"startfile",	win32_startfile, METH_VARARGS, win32_startfile__doc__},
 | 
			
		||||
	{"kill",    win32_kill, METH_VARARGS, win32_kill__doc__},
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAVE_SETUID
 | 
			
		||||
	{"setuid",	posix_setuid, METH_VARARGS, posix_setuid__doc__},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@
 | 
			
		|||
#include "intrcheck.h"
 | 
			
		||||
 | 
			
		||||
#ifdef MS_WINDOWS
 | 
			
		||||
#include <Windows.h>
 | 
			
		||||
#ifdef HAVE_PROCESS_H
 | 
			
		||||
#include <process.h>
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -805,6 +806,18 @@ PyInit_signal(void)
 | 
			
		|||
    	PyDict_SetItemString(d, "ItimerError", ItimerError);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CTRL_C_EVENT
 | 
			
		||||
    x = PyLong_FromLong(CTRL_C_EVENT);
 | 
			
		||||
    PyDict_SetItemString(d, "CTRL_C_EVENT", x);
 | 
			
		||||
    Py_DECREF(x);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CTRL_BREAK_EVENT
 | 
			
		||||
    x = PyLong_FromLong(CTRL_BREAK_EVENT);
 | 
			
		||||
    PyDict_SetItemString(d, "CTRL_BREAK_EVENT", x);
 | 
			
		||||
    Py_DECREF(x);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (PyErr_Occurred()) {
 | 
			
		||||
	    Py_DECREF(m);
 | 
			
		||||
	    m = NULL;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -599,5 +599,7 @@ PyInit__subprocess()
 | 
			
		|||
	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);
 | 
			
		||||
 | 
			
		||||
	return m;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue