Commit c15c88c1 authored by Victor Stinner's avatar Victor Stinner

Issue #12494: Close pipes and kill process on error in subprocess functions

On error, call(), check_call(), check_output() and getstatusoutput() functions
of the subprocess module now kill the process, read its status (to avoid
zombis) and close pipes.
parent 3391e644
...@@ -464,10 +464,10 @@ def call(*popenargs, timeout=None, **kwargs): ...@@ -464,10 +464,10 @@ def call(*popenargs, timeout=None, **kwargs):
retcode = call(["ls", "-l"]) retcode = call(["ls", "-l"])
""" """
p = Popen(*popenargs, **kwargs) with Popen(*popenargs, **kwargs) as p:
try: try:
return p.wait(timeout=timeout) return p.wait(timeout=timeout)
except TimeoutExpired: except:
p.kill() p.kill()
p.wait() p.wait()
raise raise
...@@ -514,13 +514,17 @@ def check_output(*popenargs, timeout=None, **kwargs): ...@@ -514,13 +514,17 @@ def check_output(*popenargs, timeout=None, **kwargs):
""" """
if 'stdout' in kwargs: if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.') raise ValueError('stdout argument not allowed, it will be overridden.')
process = Popen(*popenargs, stdout=PIPE, **kwargs) with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
try: try:
output, unused_err = process.communicate(timeout=timeout) output, unused_err = process.communicate(timeout=timeout)
except TimeoutExpired: except TimeoutExpired:
process.kill() process.kill()
output, unused_err = process.communicate() output, unused_err = process.communicate()
raise TimeoutExpired(process.args, timeout, output=output) raise TimeoutExpired(process.args, timeout, output=output)
except:
process.kill()
process.wait()
raise
retcode = process.poll() retcode = process.poll()
if retcode: if retcode:
raise CalledProcessError(retcode, process.args, output=output) raise CalledProcessError(retcode, process.args, output=output)
...@@ -618,11 +622,19 @@ def getstatusoutput(cmd): ...@@ -618,11 +622,19 @@ def getstatusoutput(cmd):
>>> subprocess.getstatusoutput('/bin/junk') >>> subprocess.getstatusoutput('/bin/junk')
(256, 'sh: /bin/junk: not found') (256, 'sh: /bin/junk: not found')
""" """
pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r') with os.popen('{ ' + cmd + '; } 2>&1', 'r') as pipe:
try:
text = pipe.read() text = pipe.read()
sts = pipe.close() sts = pipe.close()
if sts is None: sts = 0 except:
if text[-1:] == '\n': text = text[:-1] process = pipe._proc
process.kill()
process.wait()
raise
if sts is None:
sts = 0
if text[-1:] == '\n':
text = text[:-1]
return sts, text return sts, text
......
...@@ -271,6 +271,10 @@ Core and Builtins ...@@ -271,6 +271,10 @@ Core and Builtins
Library Library
------- -------
- Issue #12494: On error, call(), check_call(), check_output() and
getstatusoutput() functions of the subprocess module now kill the process,
read its status (to avoid zombis) and close pipes.
- Issue #12720: Expose low-level Linux extended file attribute functions in os. - Issue #12720: Expose low-level Linux extended file attribute functions in os.
- Issue #10946: The distutils commands bdist_dumb, bdist_wininst and bdist_msi - Issue #10946: The distutils commands bdist_dumb, bdist_wininst and bdist_msi
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment