classification
Title: subprocess.Popen.send_signal doesn't check whether the process has terminated
Type: behavior Stage: needs patch
Components: Library (Lib) Versions: Python 3.5, Python 3.4, Python 2.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: gregory.p.smith Nosy List: astrand, brian.curtin, ezio.melotti, giampaolo.rodola, gregory.p.smith, martin.panter, milko.krachounov, python-dev, tshepang
Priority: normal Keywords: patch

Created on 2009-09-22 22:47 by milko.krachounov, last changed 2015-11-16 02:35 by gregory.p.smith. This issue is now closed.

Files
File name Uploaded Description Edit
test_subprocess.patch markon, 2009-10-15 22:09 Provided patch for test_subprocess.py in trunk/
subprocess.patch markon, 2009-10-15 22:10 Provided patch for subprocess.py in trunk/
Messages (6)
msg93022 - (view) Author: Milko Krachounov (milko.krachounov) Date: 2009-09-22 22:47
When subprocess.Popen.send_signal is called, it simply calls
os.kill(self.pid, ...) without checking whether the child has already
terminated. If the child has been terminated, and Popen.wait() or
Popen.poll() have been called, a process with PID self.pid no longer
exists, and what's worse, could belong to a totally unrelated process.

A better behavior would be to raise an exception when self.returncode is
not None. Alternatively, self.pid might be set to None after the process
has been terminated, as it is no longer meaningful. Or to another value
that would be invalid pid and invalid argument to os.kill and os.wait,
but unlike None would still evaluate to True.
msg94091 - (view) Author: Marco Buccini (markon) Date: 2009-10-15 15:04
I agree with Milko. 

However, I think Popen.send_signal should poll() before sending any
signals, without resetting any variables to zero (or None).
In this way, if you poll() before sending a signal, if the return code
is None, the child is still running. If the poll return code is
different than None, the child has been terminated, so you must not send
anything to the child process.

In this way, instead of polling before sending signals to check if the
child has been terminated, we can make Python do the work for us.

I've provided a patch. All the tests pass.
A new test has been added: test_terminate_already_terminated_child.
This method asserts `terminate()` (through `send_signal`) raises an
OSError exception. 

However, even though you try the old version of subprocess.py all the
tests pass. This happens because the method `send_signal` calls
os.kill() (or Terminate for Windows systems) that can raise an OSError
exception if the process to which it send the signal does not exist. 
`send_signal` has been fixed to be a safer method, so that it raises an
OSError exception if the child has been terminated.
msg107411 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) Date: 2010-06-09 19:48
I totally agree this should be fixed (there was a similar issue for psutil: http://code.google.com/p/psutil/issues/detail?id=58) but communicate() and wait() should be affected in the same manner.

Probably it would make sense to add an is_running() method and decorate send_signal(), kill(), communicate() and wait() methods with a function which makes sure that the process is still running, otherwise raises an exception instead.

Maybe it would also makes sense to provide a brand new NoSuchProcess exception class.
msg225483 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2014-08-18 06:05
It seems to me that raising OSError isn’t quite right. Either the error is more of an internal programmer error, like how writing to a closed file object raises ValueError, or there should not be an error, like when the process has terminated but has not been reaped, or when closing an already-closed file.
msg254704 - (view) Author: Roundup Robot (python-dev) Date: 2015-11-16 02:31
New changeset 6bd3c8623bb2 by Gregory P. Smith in branch '3.4':
Fix issue #6973: When we know a subprocess.Popen process has died, do
https://hg.python.org/cpython/rev/6bd3c8623bb2

New changeset bc907c76f054 by Gregory P. Smith in branch '3.5':
Fix issue #6973: When we know a subprocess.Popen process has died, do
https://hg.python.org/cpython/rev/bc907c76f054

New changeset e51c46d12ec1 by Gregory P. Smith in branch 'default':
Fix issue #6973: When we know a subprocess.Popen process has died, do
https://hg.python.org/cpython/rev/e51c46d12ec1
msg254705 - (view) Author: Gregory P. Smith (gregory.p.smith) * (Python committer) Date: 2015-11-16 02:35
I chose to go with the "there should not be an error" approach similar to how Python deals with multiple file closes.  Both are technically programming errors but the point of Python is make life easier.

Reasoning?  I don't want someone to ever think they could depend on getting an exception from send_signal/terminate/kill in this "process has already died and someone has called wait or poll to get its return code" situation as that is unreliable and potentially impacts other innocent processes.
History
Date User Action Args
2015-11-16 02:35:11gregory.p.smithsetstatus: open -> closed
resolution: fixed
messages: + msg254705
2015-11-16 02:32:00python-devsetnosy: + python-dev
messages: + msg254704
2015-11-15 22:42:57gregory.p.smithsetassignee: gregory.p.smith

nosy: + gregory.p.smith
2015-11-15 22:42:28gregory.p.smithlinkissue17131 superseder
2014-08-18 06:05:41martin.pantersetcomponents: + Library (Lib), - Interpreter Core
2014-08-18 06:05:10martin.pantersetnosy: + martin.panter
messages: + msg225483
components: + Interpreter Core, - Library (Lib)
2014-08-15 10:08:07tshepangsetversions: + Python 3.5, - Python 3.3
2014-08-15 10:07:22tshepangsetnosy: + tshepang

versions: - Python 3.2
2013-01-14 21:09:42markonsetnosy: - markon
2012-09-26 17:06:23ezio.melottisetversions: + Python 2.7, Python 3.2, Python 3.3, Python 3.4, - Python 2.6, Python 3.1
2010-06-09 19:48:47giampaolo.rodolasetnosy: + brian.curtin, giampaolo.rodola, astrand
messages: + msg107411
2009-10-15 22:10:20markonsetfiles: + subprocess.patch
2009-10-15 22:09:58markonsetfiles: + test_subprocess.patch
2009-10-15 22:09:24markonsetfiles: - test_subprocess.patch
2009-10-15 22:09:21markonsetfiles: - subprocess.patch
2009-10-15 21:31:40markonsetfiles: + subprocess.patch
2009-10-15 21:31:18markonsetfiles: - subprocess.patch
2009-10-15 15:08:13markonsetfiles: + test_subprocess.patch
2009-10-15 15:07:29markonsetfiles: - test_subprocess.patch
2009-10-15 15:05:16markonsetfiles: + test_subprocess.patch
2009-10-15 15:04:35markonsetfiles: + subprocess.patch

nosy: + markon
messages: + msg94091

keywords: + patch
2009-10-15 11:28:18ezio.melottisetpriority: normal
nosy: + ezio.melotti

stage: needs patch
2009-09-22 22:47:47milko.krachounovcreate