https://github.com/python/cpython/commit/7c653e2540cbe4180efb3bd83b63865a1f675650
commit: 7c653e2540cbe4180efb3bd83b63865a1f675650
branch: main
author: Vyron Vasileiadis <[email protected]>
committer: vstinner <[email protected]>
date: 2026-08-07T14:19:07+02:00
summary:
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.
files:
A Misc/NEWS.d/next/Library/2026-07-22-12-00-00.gh-issue-153970.Kq7Wn3.rst
M Doc/library/subprocess.rst
M Lib/subprocess.py
M Lib/test/test_subprocess.py
diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst
index fe64daa3291d67..2a31213560c92d 100644
--- a/Doc/library/subprocess.rst
+++ b/Doc/library/subprocess.rst
@@ -236,8 +236,8 @@ underlying :class:`Popen` interface can be used directly.
.. attribute:: returncode
- Exit status of the child process. If the process exited due to a
- signal, this will be the negative signal number.
+ Exit status of the child process, an integer. If the process
+ exited due to a signal, this will be the negative signal number.
.. attribute:: cmd
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 054860a19c74b6..a14fede00c391c 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -143,7 +143,7 @@ def __init__(self, returncode, cmd, output=None,
stderr=None):
self.stderr = stderr
def __str__(self):
- if self.returncode and self.returncode < 0:
+ if isinstance(self.returncode, int) and self.returncode < 0:
try:
return "Command %r died with %r." % (
self.cmd, signal.Signals(-self.returncode))
@@ -151,8 +151,8 @@ def __str__(self):
return "Command %r died with unknown signal %d." % (
self.cmd, -self.returncode)
else:
- return "Command %r returned non-zero exit status %d." % (
- self.cmd, self.returncode)
+ return (f"Command {self.cmd!r} returned non-zero "
+ f"exit status {self.returncode}.")
@property
def stdout(self):
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 4fd14d98b0324c..d1840e97d0f2f7 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -2449,6 +2449,16 @@ def test_CalledProcessError_str(self):
err = subprocess.CalledProcessError(-9876543, "fake cmd")
self.assertEqual(str(err), "Command 'fake cmd' died with unknown
signal 9876543.")
+ # returncode which is not an integer, which happens for example when
+ # Popen is mocked: str() must not fail
+ for returncode in (None, "2", 2.5, [2]):
+ with self.subTest(returncode=returncode):
+ err = subprocess.CalledProcessError(returncode, "fake cmd")
+ self.assertEqual(
+ str(err),
+ f"Command 'fake cmd' returned non-zero "
+ f"exit status {returncode}.")
+
def test_preexec(self):
# DISCLAIMER: Setting environment variables is *not* a good use
# of a preexec_fn. This is merely a test.
diff --git
a/Misc/NEWS.d/next/Library/2026-07-22-12-00-00.gh-issue-153970.Kq7Wn3.rst
b/Misc/NEWS.d/next/Library/2026-07-22-12-00-00.gh-issue-153970.Kq7Wn3.rst
new file mode 100644
index 00000000000000..def943b46b5942
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-22-12-00-00.gh-issue-153970.Kq7Wn3.rst
@@ -0,0 +1,3 @@
+Calling :func:`str` on a :exc:`subprocess.CalledProcessError` no longer
+raises :exc:`TypeError` when its :attr:`!returncode` is not an integer, such
+as ``None``.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]