Skip to content

Commit 012fcb9

Browse files
env: do not mix stdout and stderr when running Python scripts and parsing the output (#6665)
Co-authored-by: Randy Döring <[email protected]>
1 parent e92de61 commit 012fcb9

File tree

2 files changed

+83
-18
lines changed

2 files changed

+83
-18
lines changed

src/poetry/utils/env.py

+23-12
Original file line numberDiff line numberDiff line change
@@ -463,13 +463,16 @@ class EnvCommandError(EnvError):
463463
def __init__(self, e: CalledProcessError, input: str | None = None) -> None:
464464
self.e = e
465465

466-
message = (
467-
f"Command {e.cmd} errored with the following return code {e.returncode},"
468-
f" and output: \n{decode(e.output)}"
469-
)
466+
message_parts = [
467+
f"Command {e.cmd} errored with the following return code {e.returncode}"
468+
]
469+
if e.output:
470+
message_parts.append(f"Output:\n{decode(e.output)}")
471+
if e.stderr:
472+
message_parts.append(f"Error output:\n{decode(e.stderr)}")
470473
if input:
471-
message += f"input was : {input}"
472-
super().__init__(message)
474+
message_parts.append(f"Input:\n{input}")
475+
super().__init__("\n\n".join(message_parts))
473476

474477

475478
class NoCompatiblePythonVersionFound(EnvError):
@@ -1503,7 +1506,14 @@ def run_pip(self, *args: str, **kwargs: Any) -> int | str:
15031506

15041507
def run_python_script(self, content: str, **kwargs: Any) -> int | str:
15051508
return self.run(
1506-
self._executable, "-I", "-W", "ignore", "-", input_=content, **kwargs
1509+
self._executable,
1510+
"-I",
1511+
"-W",
1512+
"ignore",
1513+
"-",
1514+
input_=content,
1515+
stderr=subprocess.PIPE,
1516+
**kwargs,
15071517
)
15081518

15091519
def _run(self, cmd: list[str], **kwargs: Any) -> int | str:
@@ -1513,23 +1523,24 @@ def _run(self, cmd: list[str], **kwargs: Any) -> int | str:
15131523
call = kwargs.pop("call", False)
15141524
input_ = kwargs.pop("input_", None)
15151525
env = kwargs.pop("env", dict(os.environ))
1526+
stderr = kwargs.pop("stderr", subprocess.STDOUT)
15161527

15171528
try:
15181529
if input_:
15191530
output = subprocess.run(
15201531
cmd,
15211532
stdout=subprocess.PIPE,
1522-
stderr=subprocess.STDOUT,
1533+
stderr=stderr,
15231534
input=encode(input_),
15241535
check=True,
15251536
**kwargs,
15261537
).stdout
15271538
elif call:
1528-
return subprocess.call(cmd, stderr=subprocess.STDOUT, env=env, **kwargs)
1529-
else:
1530-
output = subprocess.check_output(
1531-
cmd, stderr=subprocess.STDOUT, env=env, **kwargs
1539+
return subprocess.call(
1540+
cmd, stdout=subprocess.PIPE, stderr=stderr, env=env, **kwargs
15321541
)
1542+
else:
1543+
output = subprocess.check_output(cmd, stderr=stderr, env=env, **kwargs)
15331544
except CalledProcessError as e:
15341545
raise EnvCommandError(e, input=input_)
15351546

tests/utils/test_env.py

+60-6
Original file line numberDiff line numberDiff line change
@@ -947,35 +947,89 @@ def test_run_with_called_process_error(
947947
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture
948948
):
949949
mocker.patch(
950-
"subprocess.run", side_effect=subprocess.CalledProcessError(42, "some_command")
950+
"subprocess.run",
951+
side_effect=subprocess.CalledProcessError(
952+
42, "some_command", "some output", "some error"
953+
),
951954
)
952-
with pytest.raises(EnvCommandError):
955+
with pytest.raises(EnvCommandError) as error:
953956
tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT)
954957
subprocess.run.assert_called_once()
958+
assert "some output" in str(error.value)
959+
assert "some error" in str(error.value)
955960

956961

957962
def test_call_with_input_and_called_process_error(
958963
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture
959964
):
960965
mocker.patch(
961-
"subprocess.run", side_effect=subprocess.CalledProcessError(42, "some_command")
966+
"subprocess.run",
967+
side_effect=subprocess.CalledProcessError(
968+
42, "some_command", "some output", "some error"
969+
),
962970
)
963971
kwargs = {"call": True}
964-
with pytest.raises(EnvCommandError):
972+
with pytest.raises(EnvCommandError) as error:
965973
tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT, **kwargs)
966974
subprocess.run.assert_called_once()
975+
assert "some output" in str(error.value)
976+
assert "some error" in str(error.value)
967977

968978

969979
def test_call_no_input_with_called_process_error(
970980
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture
971981
):
972982
mocker.patch(
973-
"subprocess.call", side_effect=subprocess.CalledProcessError(42, "some_command")
983+
"subprocess.call",
984+
side_effect=subprocess.CalledProcessError(
985+
42, "some_command", "some output", "some error"
986+
),
974987
)
975988
kwargs = {"call": True}
976-
with pytest.raises(EnvCommandError):
989+
with pytest.raises(EnvCommandError) as error:
977990
tmp_venv.run("python", "-", **kwargs)
978991
subprocess.call.assert_called_once()
992+
assert "some output" in str(error.value)
993+
assert "some error" in str(error.value)
994+
995+
996+
def test_check_output_with_called_process_error(
997+
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture
998+
):
999+
mocker.patch(
1000+
"subprocess.check_output",
1001+
side_effect=subprocess.CalledProcessError(
1002+
42, "some_command", "some output", "some error"
1003+
),
1004+
)
1005+
with pytest.raises(EnvCommandError) as error:
1006+
tmp_venv.run("python", "-")
1007+
subprocess.check_output.assert_called_once()
1008+
assert "some output" in str(error.value)
1009+
assert "some error" in str(error.value)
1010+
1011+
1012+
def test_run_python_script_called_process_error(
1013+
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture
1014+
):
1015+
mocker.patch(
1016+
"subprocess.run",
1017+
side_effect=subprocess.CalledProcessError(
1018+
42, "some_command", "some output", "some error"
1019+
),
1020+
)
1021+
with pytest.raises(EnvCommandError) as error:
1022+
tmp_venv.run_python_script(MINIMAL_SCRIPT)
1023+
assert "some output" in str(error.value)
1024+
assert "some error" in str(error.value)
1025+
1026+
1027+
def test_run_python_script_only_stdout(tmp_dir: str, tmp_venv: VirtualEnv):
1028+
output = tmp_venv.run_python_script(
1029+
"import sys; print('some warning', file=sys.stderr); print('some output')"
1030+
)
1031+
assert "some output" in output
1032+
assert "some warning" not in output
9791033

9801034

9811035
def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ones_first( # noqa: E501

0 commit comments

Comments
 (0)