https://github.com/python/cpython/commit/bc3972908b47a66bb2ac3b8a38a202ad5e309714
commit: bc3972908b47a66bb2ac3b8a38a202ad5e309714
branch: 3.14
author: Dongjun Shin <[email protected]>
committer: corona10 <[email protected]>
date: 2026-08-17T14:32:58+09:00
summary:

[3.14] gh-153970: Fix str() of CalledProcessError when returncode is not an 
integer (gh-153971) (gh-155924)

CalledProcessError.__str__() fell through to a branch that formats the
return code with %d, which raises TypeError when returncode is None.
(cherry picked from commit 7c653e2540cbe4180efb3bd83b63865a1f675650)

Co-authored-by: Vyron Vasileiadis <[email protected]>

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 66a3d6a484a8a8f..4be045d50d54b44 100644
--- a/Doc/library/subprocess.rst
+++ b/Doc/library/subprocess.rst
@@ -239,8 +239,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 52b7b7117703d71..1ff47af3dde7532 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 '%s' died with %r." % (
                         self.cmd, signal.Signals(-self.returncode))
@@ -151,8 +151,8 @@ def __str__(self):
                 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)
+            return (f"Command '{self.cmd}' 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 d13f0ec2522abdc..65a3d321f44d117 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -2415,6 +2415,16 @@ def test_CalledProcessError_str_non_zero(self):
         error_string = str(err)
         self.assertIn("non-zero exit status 2.", error_string)
 
+        # 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 000000000000000..def943b46b59420
--- /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]

Reply via email to