Skip to content

Conversation

@mkoncek
Copy link
Contributor

@mkoncek mkoncek commented Jan 9, 2026

In case that subprocess.run fails, the raised exception does not print the outputs of the command. Those outputs are very important for debugging and should be logged.
I was not sure about the exact format:

  • one log event per one line of output?
  • one log event per stream?
  • ERROR / WARNING / INFO - mapping to stdout / stderr?

An alternative I considered was:

if len(process.stdout) != 0:
	getLog().error("stdout:\n%s", process.stdout.strip())
if len(process.stderr) != 0:
	getLog().error("stderr:\n%s", process.stderr.strip())

@mkoncek mkoncek changed the title unbreq-plugin: log standard outputs of failed commands unbreq plugin: log standard outputs of failed commands Jan 9, 2026
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a helper function, _run_subprocess, to centralize subprocess calls and log their output on failure, which is a great improvement for debuggability. The refactoring is applied consistently throughout the plugin. My feedback includes a couple of suggestions for the new helper function to enhance the logging format and make its error handling more flexible for the different use cases within this file.

Comment on lines 44 to 69
def _run_subprocess(
command: list[str], *args: Any, expected_returncode: int = 0, **kwargs: Any
) -> subprocess.CompletedProcess:
"""
Helper function which calls `subprocess.run` but logs standard outputs in
case of a failure.
"""
kwargs.setdefault("stdin", subprocess.DEVNULL)
kwargs.setdefault("stdout", subprocess.PIPE)
kwargs.setdefault("stderr", subprocess.PIPE)
kwargs.setdefault("text", True)
kwargs.setdefault("check", False)

process = subprocess.run(command, *args, **kwargs)
if process.returncode != expected_returncode:
for line in process.stdout.splitlines():
getLog().error("%s", line)
for line in process.stderr.splitlines():
getLog().error("%s", line)
raise subprocess.CalledProcessError(
process.returncode, " ".join(process.args), process.stdout, process.stderr
)
return process

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The new _run_subprocess function always raises an exception on failure. This is a good default, but some call sites, like in _PostDepsHook, previously ignored failures by using check=False. This pull request changes that behavior to fail-fast.

If this is not intended, consider adding a check: bool = True parameter to _run_subprocess to control whether it raises an exception. You could then call it with check=False in _PostDepsHook to preserve the old behavior.

If you decide to do this, please also note that the logic following the call in _PostDepsHook should be updated to check process.returncode, as the existing if mount_options_process: is always truthy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point but yes, I believe if the plugin can not invoke /usr/bin/findmnt then it probably can not behave correctly, so the new behaviour is good. I should remove the questioning comment though.

Comment on lines 59 to 68
for line in process.stdout.splitlines():
getLog().error("%s", line)
for line in process.stderr.splitlines():
getLog().error("%s", line)
raise subprocess.CalledProcessError(
process.returncode, " ".join(process.args), process.stdout, process.stderr
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation logs stdout and stderr line by line, which can make logs hard to read if they are interleaved with other messages. As you considered in the pull request description, logging each stream as a single block with a header would be more readable.

Additionally, using mockbuild.util.cmd_pretty to format the command for the exception is more robust than " ".join as it correctly handles arguments with spaces.

Suggested change
for line in process.stdout.splitlines():
getLog().error("%s", line)
for line in process.stderr.splitlines():
getLog().error("%s", line)
raise subprocess.CalledProcessError(
process.returncode, " ".join(process.args), process.stdout, process.stderr
)
if process.stdout:
getLog().error("stdout:\n%s", process.stdout.strip())
if process.stderr:
getLog().error("stderr:\n%s", process.stderr.strip())
raise subprocess.CalledProcessError(
process.returncode, mockbuild.util.cmd_pretty(process.args), process.stdout, process.stderr
)

@mkoncek mkoncek force-pushed the main branch 3 times, most recently from 4e729ad to 9355514 Compare January 15, 2026 09:58
@traceLog()
def _run_subprocess(
command: list[str], *args: Any, expected_returncode: int = 0, **kwargs: Any
) -> subprocess.CompletedProcess:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I will try to see if it could be replaced.

@mkoncek mkoncek force-pushed the main branch 2 times, most recently from 7a774c8 to 2f96871 Compare January 16, 2026 10:57
@mkoncek
Copy link
Contributor Author

mkoncek commented Jan 18, 2026

This PR has grown in size from the initial one, but there should be no more changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants