Skip to content

Commit b35c379

Browse files
BenjyWienerBenjy Wienerblurb-it[bot]
authored
gh-151485: Fix command quoting in subprocess.CalledProcessError.__str__ (#151486)
CalledProcessError previously formatted cmd as `"... '%s' ..."`. This lead to unbalanced quoting when cmd contains single-quotes or, more commonly, when cmd is a list. This change updates the relevant format strings to use %r instead. Co-authored-by: Benjy Wiener <benjywiener@gmail.com> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
1 parent 15696a6 commit b35c379

3 files changed

Lines changed: 22 additions & 21 deletions

File tree

Lib/subprocess.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,13 @@ def __init__(self, returncode, cmd, output=None, stderr=None):
145145
def __str__(self):
146146
if self.returncode and self.returncode < 0:
147147
try:
148-
return "Command '%s' died with %r." % (
148+
return "Command %r died with %r." % (
149149
self.cmd, signal.Signals(-self.returncode))
150150
except ValueError:
151-
return "Command '%s' died with unknown signal %d." % (
151+
return "Command %r died with unknown signal %d." % (
152152
self.cmd, -self.returncode)
153153
else:
154-
return "Command '%s' returned non-zero exit status %d." % (
154+
return "Command %r returned non-zero exit status %d." % (
155155
self.cmd, self.returncode)
156156

157157
@property

Lib/test/test_subprocess.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,26 +2428,26 @@ def test_run_abort(self):
24282428
p.wait()
24292429
self.assertEqual(-p.returncode, signal.SIGABRT)
24302430

2431-
def test_CalledProcessError_str_signal(self):
2431+
def test_CalledProcessError_str(self):
2432+
# command string
2433+
err = subprocess.CalledProcessError(2, "fake cmd")
2434+
self.assertEqual(str(err), "Command 'fake cmd' returned non-zero exit status 2.")
2435+
2436+
# command string with a single-quote
2437+
err = subprocess.CalledProcessError(2, "fake ' cmd")
2438+
self.assertEqual(str(err), 'Command "fake \' cmd" returned non-zero exit status 2.')
2439+
2440+
# command list
2441+
err = subprocess.CalledProcessError(2, ["fake", "cmd"])
2442+
self.assertEqual(str(err), "Command ['fake', 'cmd'] returned non-zero exit status 2.")
2443+
2444+
# signal
24322445
err = subprocess.CalledProcessError(-int(signal.SIGABRT), "fake cmd")
2433-
error_string = str(err)
2434-
# We're relying on the repr() of the signal.Signals intenum to provide
2435-
# the word signal, the signal name and the numeric value.
2436-
self.assertIn("signal", error_string.lower())
2437-
# We're not being specific about the signal name as some signals have
2438-
# multiple names and which name is revealed can vary.
2439-
self.assertIn("SIG", error_string)
2440-
self.assertIn(str(signal.SIGABRT), error_string)
2441-
2442-
def test_CalledProcessError_str_unknown_signal(self):
2443-
err = subprocess.CalledProcessError(-9876543, "fake cmd")
2444-
error_string = str(err)
2445-
self.assertIn("unknown signal 9876543.", error_string)
2446+
self.assertEqual(str(err), f"Command 'fake cmd' died with {signal.SIGABRT!r}.")
24462447

2447-
def test_CalledProcessError_str_non_zero(self):
2448-
err = subprocess.CalledProcessError(2, "fake cmd")
2449-
error_string = str(err)
2450-
self.assertIn("non-zero exit status 2.", error_string)
2448+
# unknown signal
2449+
err = subprocess.CalledProcessError(-9876543, "fake cmd")
2450+
self.assertEqual(str(err), "Command 'fake cmd' died with unknown signal 9876543.")
24512451

24522452
def test_preexec(self):
24532453
# DISCLAIMER: Setting environment variables is *not* a good use
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix command quoting in :exc:`subprocess.CalledProcessError`. Contributed by Benjy Wiener.

0 commit comments

Comments
 (0)