gh-153970: Validate the returncode type in CalledProcessError#153971
gh-153970: Validate the returncode type in CalledProcessError#153971fedonman wants to merge 10 commits into
Conversation
… is None CalledProcessError.__str__ fell through to a branch that formats the return code with %d, which raises TypeError when returncode is None. Handle the None case explicitly and return a message reporting an unknown exit status.
|
See my reservations on the issue; I don't think the constructor is a public API and thus it's a case of GIGO (garbage-in, garbage-out) |
Thank you for reviewing this. Answered on the Issue thread. |
|
I don't think that there is a reason to format the returncode using diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 6fe2ec98fb4..df23fb18a51 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -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): |
|
I guess this patch would be acceptable indeed. |
The implementation now formats a None returncode with an f-string
("returned non-zero exit status None.") rather than a dedicated branch,
so update the test assertion and the NEWS wording to match.
…com:fedonman/cpython into fix-pythongh-153970-calledprocesserror-none
|
Thank you. I implemented the requested changes. |
picnixz
left a comment
There was a problem hiding this comment.
Actually, after sleeping a bit, we would still have an issue if someone is using a returncode as a string for instance (just because self.returncode < 0 would raise here). Since we are considering only theoretical usages, I really wonderf whether we need the change.
This change would make None supported, but would still fail on truthy returncodes not comparable to 0.
So... I'd say:
- either check returncode type in the constructor
- do nothing but reword the docs a bit.
…teger The earlier fix changed CalledProcessError.__str__ to tolerate a None returncode, but that only handled one non-integer value and would still fail on others, such as a returncode set to a string. The returncode attribute is meant to hold a process exit status, which subprocess always sets to an integer, so there is no real bug in __str__. This drops the code change and the extra test, and instead notes in the docs that returncode is always an integer.
CalledProcessError.__str__ crash when returncode is None
Documentation build overview
5 files changed ·
|
Calling str() on a CalledProcessError whose returncode was None raised TypeError, because the __str__ else branch formats the value with %d. A returncode of another type, such as a string, failed even earlier at the self.returncode < 0 comparison. The constructor now raises a clear TypeError when returncode is neither an integer nor None, so __str__ can never be handed a value it cannot format. The else branch also uses an f-string, so a None returncode renders as a message instead of crashing. One test that built the exception with an empty-string returncode is updated to pass an integer, which is what a real failed command produces.
CalledProcessError.returncodeholds a process exit status, which is always an integer. Callingstr()on one whosereturncodewasNoneraisedTypeError, because__str__formats the value with%d. A returncode of another type, such as a string, failed even earlier at theself.returncode < 0comparison.The constructor now raises a clear
TypeErrorwhenreturncodeis neither an integer norNone, so__str__can never be handed a value it cannot format. Its else branch also uses an f-string, so aNonereturncode renders as "Command ... returned non-zero exit status None." instead of crashing. A test that built the exception with an empty-string returncode is updated to pass an integer, which is what a real failed command produces.The constructor check is a behavior change, so it is documented with a
.. versionchanged:: 3.16note.returncodeis a public attribute, so assigning a bad value after construction and then callingstr()can still fail, but guarding every__str__call would be the kind of defensive code the stdlib avoids.Found as item 9 in devdanzin's standard library audit: https://gist.github.com/devdanzin/3198710e3c0128fda5e0a7b4e0768e5f
Fixes #153970.
CalledProcessError.__str__crashes when returncode is None #153970