Issue #27167: Clarify the subprocess.CalledProcessError error message text

when the child process died due to a signal.
üst 287e6876
...@@ -372,9 +372,11 @@ class SubprocessError(Exception): pass ...@@ -372,9 +372,11 @@ class SubprocessError(Exception): pass
class CalledProcessError(SubprocessError): class CalledProcessError(SubprocessError):
"""This exception is raised when a process run by check_call() or """Raised when a check_call() or check_output() process returns non-zero.
check_output() returns a non-zero exit status.
The exit status will be stored in the returncode attribute; The exit status will be stored in the returncode attribute, negative
if it represents a signal number.
check_output() will also store the output in the output attribute. check_output() will also store the output in the output attribute.
""" """
def __init__(self, returncode, cmd, output=None, stderr=None): def __init__(self, returncode, cmd, output=None, stderr=None):
...@@ -384,7 +386,16 @@ class CalledProcessError(SubprocessError): ...@@ -384,7 +386,16 @@ class CalledProcessError(SubprocessError):
self.stderr = stderr self.stderr = stderr
def __str__(self): def __str__(self):
return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) if self.returncode and self.returncode < 0:
try:
return "Command '%s' died with %r." % (
self.cmd, signal.Signals(-self.returncode))
except ValueError:
return "Command '%s' died with unknown signal %d." % (
self.cmd, -self.returncode)
else:
return "Command '%s' returned non-zero exit status %d." % (
self.cmd, self.returncode)
@property @property
def stdout(self): def stdout(self):
......
...@@ -1427,6 +1427,25 @@ class POSIXProcessTestCase(BaseTestCase): ...@@ -1427,6 +1427,25 @@ class POSIXProcessTestCase(BaseTestCase):
p.wait() p.wait()
self.assertEqual(-p.returncode, signal.SIGABRT) self.assertEqual(-p.returncode, signal.SIGABRT)
def test_CalledProcessError_str_signal(self):
err = subprocess.CalledProcessError(-int(signal.SIGABRT), "fake cmd")
error_string = str(err)
# We're relying on the repr() of the signal.Signals intenum to provide
# the word signal, the signal name and the numeric value.
self.assertIn("signal", error_string.lower())
self.assertIn("SIGABRT", error_string)
self.assertIn(str(signal.SIGABRT), error_string)
def test_CalledProcessError_str_unknown_signal(self):
err = subprocess.CalledProcessError(-9876543, "fake cmd")
error_string = str(err)
self.assertIn("unknown signal 9876543.", error_string)
def test_CalledProcessError_str_non_zero(self):
err = subprocess.CalledProcessError(2, "fake cmd")
error_string = str(err)
self.assertIn("non-zero exit status 2.", error_string)
def test_preexec(self): def test_preexec(self):
# DISCLAIMER: Setting environment variables is *not* a good use # DISCLAIMER: Setting environment variables is *not* a good use
# of a preexec_fn. This is merely a test. # of a preexec_fn. This is merely a test.
......
...@@ -22,6 +22,9 @@ Core and Builtins ...@@ -22,6 +22,9 @@ Core and Builtins
Library Library
------- -------
- Issue #27167: Clarify the subprocess.CalledProcessError error message text
when the child process died due to a signal.
- Issue #25931: Don't define socketserver.Forking* names on platforms such - Issue #25931: Don't define socketserver.Forking* names on platforms such
as Windows that do not support os.fork(). as Windows that do not support os.fork().
......
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