diff --git a/src/poetry/console/commands/run.py b/src/poetry/console/commands/run.py index 6496b45af24..50e1ae118f6 100644 --- a/src/poetry/console/commands/run.py +++ b/src/poetry/console/commands/run.py @@ -58,7 +58,7 @@ def run_script(self, script: str | dict[str, str], args: str) -> int: "import sys; " "from importlib import import_module; " f"sys.argv = {args!r}; {src_in_sys_path}" - f"import_module('{module}').{callable_}()" + f"sys.exit(import_module('{module}').{callable_}())" ] return self.env.execute(*cmd) diff --git a/tests/console/commands/test_run.py b/tests/console/commands/test_run.py index 6465c7f55ef..60bc94e74a7 100644 --- a/tests/console/commands/test_run.py +++ b/tests/console/commands/test_run.py @@ -1,5 +1,7 @@ from __future__ import annotations +import subprocess + from typing import TYPE_CHECKING import pytest @@ -12,9 +14,12 @@ from cleo.testers.command_tester import CommandTester from pytest_mock import MockerFixture + from poetry.poetry import Poetry from poetry.utils.env import MockEnv from poetry.utils.env import VirtualEnv from tests.types import CommandTesterFactory + from tests.types import FixtureDirGetter + from tests.types import ProjectFactory @pytest.fixture @@ -27,6 +32,19 @@ def patches(mocker: MockerFixture, env: MockEnv) -> None: mocker.patch("poetry.utils.env.EnvManager.get", return_value=env) +@pytest.fixture +def poetry_with_scripts( + project_factory: ProjectFactory, fixture_dir: FixtureDirGetter +) -> Poetry: + source = fixture_dir("scripts") + + return project_factory( + name="scripts", + pyproject_content=(source / "pyproject.toml").read_text(encoding="utf-8"), + source=source, + ) + + def test_run_passes_all_args(app_tester: ApplicationTester, env: MockEnv): app_tester.execute("run python -V") assert [["python", "-V"]] == env.executed @@ -105,3 +123,26 @@ def test_run_console_scripts_of_editable_dependencies_on_windows( # We prove that the CMD script executed successfully by verifying the exit code # matches what we wrote in the script assert tester.execute("quix") == 123 + + +def test_run_script_exit_code( + poetry_with_scripts: Poetry, + command_tester_factory: CommandTesterFactory, + tmp_venv: VirtualEnv, + mocker: MockerFixture, +) -> None: + mocker.patch( + "os.execvpe", + lambda file, args, env: subprocess.call([file] + args[1:], env=env), + ) + install_tester = command_tester_factory( + "install", + poetry=poetry_with_scripts, + environment=tmp_venv, + ) + assert install_tester.execute() == 0 + tester = command_tester_factory( + "run", poetry=poetry_with_scripts, environment=tmp_venv + ) + assert tester.execute("exit-code") == 42 + assert tester.execute("return-code") == 42 diff --git a/tests/fixtures/scripts/README.md b/tests/fixtures/scripts/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/fixtures/scripts/pyproject.toml b/tests/fixtures/scripts/pyproject.toml new file mode 100644 index 00000000000..a53f36b930e --- /dev/null +++ b/tests/fixtures/scripts/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "scripts" +version = "0.1.0" +description = "" +authors = ["Your Name "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.7" + +[tool.poetry.scripts] +exit-code = "scripts.exit_code:main" +return-code = "scripts.return_code:main" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/fixtures/scripts/scripts/__init__.py b/tests/fixtures/scripts/scripts/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/fixtures/scripts/scripts/exit_code.py b/tests/fixtures/scripts/scripts/exit_code.py new file mode 100644 index 00000000000..862e3956cbe --- /dev/null +++ b/tests/fixtures/scripts/scripts/exit_code.py @@ -0,0 +1,9 @@ +from __future__ import annotations + + +def main() -> None: + raise SystemExit(42) + + +if __name__ == "__main__": + main() diff --git a/tests/fixtures/scripts/scripts/return_code.py b/tests/fixtures/scripts/scripts/return_code.py new file mode 100644 index 00000000000..f645790ee37 --- /dev/null +++ b/tests/fixtures/scripts/scripts/return_code.py @@ -0,0 +1,9 @@ +from __future__ import annotations + + +def main() -> int: + return 42 + + +if __name__ == "__main__": + raise SystemExit(main())