From 1f9a8354002dbb29f38bfbd4273ce62e40ac80ba Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 11 Mar 2012 19:29:12 +0100 Subject: Issue #14252: Fix subprocess.Popen.terminate() to not raise an error under Windows when the child process has already exited. --- Lib/subprocess.py | 12 +++++++- Lib/test/test_subprocess.py | 67 +++++++++++++++++++++++++++++++++++++++++++++ Misc/NEWS | 3 ++ PC/_subprocess.c | 1 + 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 017f58d..179f41a 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1075,7 +1075,17 @@ class Popen(object): def terminate(self): """Terminates the process """ - _subprocess.TerminateProcess(self._handle, 1) + try: + _subprocess.TerminateProcess(self._handle, 1) + except OSError as e: + # ERROR_ACCESS_DENIED (winerror 5) is received when the + # process already died. + if e.winerror != 5: + raise + rc = _subprocess.GetExitCodeProcess(self._handle) + if rc == _subprocess.STILL_ACTIVE: + raise + self.returncode = rc kill = terminate diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index fb0b834..6150e88 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -989,6 +989,27 @@ class POSIXProcessTestCase(BaseTestCase): getattr(p, method)(*args) return p + def _kill_dead_process(self, method, *args): + # Do not inherit file handles from the parent. + # It should fix failures on some platforms. + p = subprocess.Popen([sys.executable, "-c", """if 1: + import sys, time + sys.stdout.write('x\\n') + sys.stdout.flush() + """], + close_fds=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + # Wait for the interpreter to be completely initialized before + # sending any signal. + p.stdout.read(1) + # The process should end after this + time.sleep(1) + # This shouldn't raise even though the child is now dead + getattr(p, method)(*args) + p.communicate() + def test_send_signal(self): p = self._kill_process('send_signal', signal.SIGINT) _, stderr = p.communicate() @@ -1007,6 +1028,18 @@ class POSIXProcessTestCase(BaseTestCase): self.assertStderrEqual(stderr, b'') self.assertEqual(p.wait(), -signal.SIGTERM) + def test_send_signal_dead(self): + # Sending a signal to a dead process + self._kill_dead_process('send_signal', signal.SIGINT) + + def test_kill_dead(self): + # Killing a dead process + self._kill_dead_process('kill') + + def test_terminate_dead(self): + # Terminating a dead process + self._kill_dead_process('terminate') + def check_close_std_fds(self, fds): # Issue #9905: test that subprocess pipes still work properly with # some standard fds closed @@ -1568,6 +1601,31 @@ class Win32ProcessTestCase(BaseTestCase): returncode = p.wait() self.assertNotEqual(returncode, 0) + def _kill_dead_process(self, method, *args): + p = subprocess.Popen([sys.executable, "-c", """if 1: + import sys, time + sys.stdout.write('x\\n') + sys.stdout.flush() + sys.exit(42) + """], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + self.addCleanup(p.stdout.close) + self.addCleanup(p.stderr.close) + self.addCleanup(p.stdin.close) + # Wait for the interpreter to be completely initialized before + # sending any signal. + p.stdout.read(1) + # The process should end after this + time.sleep(1) + # This shouldn't raise even though the child is now dead + getattr(p, method)(*args) + _, stderr = p.communicate() + self.assertStderrEqual(stderr, b'') + rc = p.wait() + self.assertEqual(rc, 42) + def test_send_signal(self): self._kill_process('send_signal', signal.SIGTERM) @@ -1577,6 +1635,15 @@ class Win32ProcessTestCase(BaseTestCase): def test_terminate(self): self._kill_process('terminate') + def test_send_signal_dead(self): + self._kill_dead_process('send_signal', signal.SIGTERM) + + def test_kill_dead(self): + self._kill_dead_process('kill') + + def test_terminate_dead(self): + self._kill_dead_process('terminate') + # The module says: # "NB This only works (and is only relevant) for UNIX." diff --git a/Misc/NEWS b/Misc/NEWS index f2d90fb..5d03fee 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,9 @@ Core and Builtins Library ------- +- Issue #14252: Fix subprocess.Popen.terminate() to not raise an error under + Windows when the child process has already exited. + - Issue #14195: An issue that caused weakref.WeakSet instances to incorrectly return True for a WeakSet instance 'a' in both 'a < a' and 'a > a' has been fixed. diff --git a/PC/_subprocess.c b/PC/_subprocess.c index 2338f30..f9a79a7 100644 --- a/PC/_subprocess.c +++ b/PC/_subprocess.c @@ -684,6 +684,7 @@ PyInit__subprocess() 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); + defint(d, "STILL_ACTIVE", STILL_ACTIVE); return m; } -- cgit v0.12 option> Tcl is a high-level, general-purpose, interpreted, dynamic programming language. It was designed with the goal of being very simple but powerful.
summaryrefslogtreecommitdiffstats
path: root/doc/unset.n
blob: 2cfc63ef9bda82c7166846f5405761600e277b17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
'\"
'\" Copyright (c) 1993 The Regents of the University of California.
'\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
'\" Copyright (c) 2000 Ajuba Solutions.
'\"
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
.TH unset n 8.4 Tcl "Tcl Built-In Commands"
.so man.macros
.BS
'\" Note:  do not modify the .SH NAME line immediately below!
.SH NAME
unset \- Delete variables
.SH SYNOPSIS
\fBunset \fR?\fB\-nocomplain\fR? ?\fB\-\-\fR? ?\fIname name name ...\fR?
.BE
.SH DESCRIPTION
.PP
This command removes one or more variables.
Each \fIname\fR is a variable name, specified in any of the
ways acceptable to the \fBset\fR command.
If a \fIname\fR refers to an element of an array then that
element is removed without affecting the rest of the array.
If a \fIname\fR consists of an array name with no parenthesized
index, then the entire array is deleted.
The \fBunset\fR command returns an empty string as result.
If \fB\-nocomplain\fR is specified as the first argument, any possible
errors are suppressed.  The option may not be abbreviated, in order to
disambiguate it from possible variable names.  The option \fB\-\-\fR
indicates the end of the options, and should be used if you wish to
remove a variable with the same name as any of the options.
If an error occurs during variable deletion, any variables after the named one
causing the error are not
deleted.  An error can occur when the named variable does not exist, or the
name refers to an array element but the variable is a scalar, or the name
refers to a variable in a non-existent namespace.
.SH EXAMPLE
.PP
Create an array containing a mapping from some numbers to their
squares and remove the array elements for non-prime numbers:
.PP
.CS
array set squares {
    1 1    6 36
    2 4    7 49
    3 9    8 64
    4 16   9 81
    5 25  10 100
}

puts "The squares are:"
parray squares

\fBunset\fR squares(1) squares(4) squares(6)
\fBunset\fR squares(8) squares(9) squares(10)

puts "The prime squares are:"
parray squares
.CE
.SH "SEE ALSO"
set(n), trace(n), upvar(n)
.SH KEYWORDS
remove, variable
'\" Local Variables:
'\" mode: nroff
'\" fill-column: 78
'\" End: