Skip to content

Commit 7c653e2

Browse files
authored
gh-153970: Fix str() of CalledProcessError when returncode is not an integer (#153971)
CalledProcessError.__str__() fell through to a branch that formats the return code with %d, which raises TypeError when returncode is None.
1 parent 5e0c502 commit 7c653e2

4 files changed

Lines changed: 18 additions & 5 deletions

File tree

Doc/library/subprocess.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ underlying :class:`Popen` interface can be used directly.
236236

237237
.. attribute:: returncode
238238

239-
Exit status of the child process. If the process exited due to a
240-
signal, this will be the negative signal number.
239+
Exit status of the child process, an integer. If the process
240+
exited due to a signal, this will be the negative signal number.
241241

242242
.. attribute:: cmd
243243

Lib/subprocess.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,16 +143,16 @@ def __init__(self, returncode, cmd, output=None, stderr=None):
143143
self.stderr = stderr
144144

145145
def __str__(self):
146-
if self.returncode and self.returncode < 0:
146+
if isinstance(self.returncode, int) and self.returncode < 0:
147147
try:
148148
return "Command %r died with %r." % (
149149
self.cmd, signal.Signals(-self.returncode))
150150
except ValueError:
151151
return "Command %r died with unknown signal %d." % (
152152
self.cmd, -self.returncode)
153153
else:
154-
return "Command %r returned non-zero exit status %d." % (
155-
self.cmd, self.returncode)
154+
return (f"Command {self.cmd!r} returned non-zero "
155+
f"exit status {self.returncode}.")
156156

157157
@property
158158
def stdout(self):

Lib/test/test_subprocess.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2449,6 +2449,16 @@ def test_CalledProcessError_str(self):
24492449
err = subprocess.CalledProcessError(-9876543, "fake cmd")
24502450
self.assertEqual(str(err), "Command 'fake cmd' died with unknown signal 9876543.")
24512451

2452+
# returncode which is not an integer, which happens for example when
2453+
# Popen is mocked: str() must not fail
2454+
for returncode in (None, "2", 2.5, [2]):
2455+
with self.subTest(returncode=returncode):
2456+
err = subprocess.CalledProcessError(returncode, "fake cmd")
2457+
self.assertEqual(
2458+
str(err),
2459+
f"Command 'fake cmd' returned non-zero "
2460+
f"exit status {returncode}.")
2461+
24522462
def test_preexec(self):
24532463
# DISCLAIMER: Setting environment variables is *not* a good use
24542464
# of a preexec_fn. This is merely a test.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Calling :func:`str` on a :exc:`subprocess.CalledProcessError` no longer
2+
raises :exc:`TypeError` when its :attr:`!returncode` is not an integer, such
3+
as ``None``.

0 commit comments

Comments
 (0)