-
-
Notifications
You must be signed in to change notification settings - Fork 35k
gh-153970: Validate the returncode type in CalledProcessError #153971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e4ebd4f
7162066
a6d21b9
fa1a5d6
da53d9b
8dbfbe3
f3bd991
5dffa29
19a86c2
46d6a68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -137,12 +137,18 @@ class CalledProcessError(SubprocessError): | |
| cmd, returncode, stdout, stderr, output | ||
| """ | ||
| def __init__(self, returncode, cmd, output=None, stderr=None): | ||
| if returncode is not None and not isinstance(returncode, int): | ||
| raise TypeError( | ||
| "returncode must be an integer or None, not " | ||
| f"{type(returncode).__name__}") | ||
| self.returncode = returncode | ||
| self.cmd = cmd | ||
| self.output = output | ||
| self.stderr = stderr | ||
|
|
||
| def __str__(self): | ||
| # A None returncode is falsy, so it skips the '< 0' comparison | ||
| # below and is formatted by the final branch instead. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. returncode should not be None, so I would prefer to omit this comment. |
||
| if self.returncode and self.returncode < 0: | ||
| try: | ||
| return "Command %r died with %r." % ( | ||
|
|
@@ -151,8 +157,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}.") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This two lines changes is the only needed change IMO.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But then we are still having the possibility of the TypeError. In this case I would rather just document that we need an error code that is an integer and leave it as is.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Would you mind to elaborate which operation would raise TypeError? The issue is about With the change above,
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue with <0 branch. If you pass [1] you get a type error there
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I'm sorry, I completely missed the error on Well, in that case, I suggest replacing:
with:
IMO we should accept any returncode value in the CalledProcessError constructor and
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's slightly awkward for me to accept arbitrary return codes. I would assume them to be integers, and not None or other things, and would rather change the docs. In the first place, the class signature is not exposed so I don't consider it as part of the public API, whether subclassing or not. But, if you really want to support arbitrary types, I'm ok with this change (+ the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We can get a type other than int if there is a bug in subprocess under specific conditions. In that case, I would prefer to be able to create CalledProcessError which stores the full process state (returncode, stdout, stderr), rather than raising a new exception (ex: TypeError) which removes all subprocess context. Running a subprocess is an expensive operation, and you might not able to get the same stdout/stderr if you run the same command twice which would make debugging way harder. You can also get a non-int returncode when mocking the Popen object. Example: import unittest.mock
import subprocess
with unittest.mock.patch.object(subprocess, 'Popen'):
subprocess.check_call(['true'])Output:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, the mocking argument convinces me. |
||
|
|
||
| @property | ||
| def stdout(self): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| Calling :func:`str` on a :exc:`subprocess.CalledProcessError` whose | ||
| :attr:`!returncode` is ``None`` no longer raises :exc:`TypeError`. The | ||
| constructor now also raises :exc:`TypeError` when *returncode* is neither | ||
| an integer nor ``None``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dislike raising a new exception, since CalledProcessError is created when we are already handling another exception. CalledProcessError should accept anything for returncode, and then does it best to convert returncode to a string in
__str__().