-
-
Notifications
You must be signed in to change notification settings - Fork 355
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
Make CommandResult a dataclass #722
Conversation
@philpep I don't really like how def result(
self,
exit_code: int,
command: bytes,
stdout_bytes: bytes,
stderr_bytes: bytes,
stdout: Optional[str] = None,
stderr: Optional[str] = None,
) -> CommandResult: The code that uses it should be updated of course. Let me know what do you think. |
testinfra/backend/base.py
Outdated
if not out and out_bytes: | ||
out = self.decode(out_bytes) | ||
elif out and not out_bytes: | ||
out_bytes = self.encode(out) |
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 think we should keep lazy encoding or decoding, i.e. using properties, because sometime we may want to get binary data which cannot be converted to text.
I think the dataclass should have _stdout
and _stdout_bytes
and check at __init__
that at least one of them are set. Or make subclasses BinaryCommandResult and TextCommandResult.
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.
Why is lazy encoding and decoding are needed? My way of thoughts here was to remove dependency on Backend
, which looks redundant to me. So why is it needed here?
With dataclass
implementation we always know that parameters are set (at least as None
, but that is the task for mypy
to detect), so this verification is kinda included.
Current solution doesn't break backward compatibility, at least to the extend it is tested now.
Another solution here would be to do similar to what subprocess.CompletedProcess
does: it is generic and can be either bytes
of str
. We can borrow the approach, but that will change the API.
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.
Lazy encoding/decoding is needed because commands like run("cat /usr/bin/cat")
will raise an error if trying to decode. Also IIRC either salt and/or ansible might have weird encoding behavior while returning command output in a json field. Lazy encoding/decoding avoid some error, as long as user doesn't explicitly ask for encoding/decoding.
Yes something like this could be fine:
@dataclass
class CommandResult:
_stdout: AnyStr
_stderr: AnyStr
@property
def stdout(self) -> str:
if isinstance(self._stdout, bytes):
return self._stdout.decode(...)
return self._stdout
[...]
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.
(took longer than I expected, I was busy)
Applied changes to keep lazy encoding/decoding.
@philpep do you think it is time to change result(self, *args: Any, **kwargs: Any)
signature in this PR?
dff3b38
to
e1064e4
Compare
e1064e4
to
145817e
Compare
I modified |
Following discussion for #717, make
CommandResult
a dataclass.