Skip to content
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

Fix incorrect sys.argv[0] path when calling project scripts #6737

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7273f41
fix: RunCommand calling scripts with incorrect executable path
chdsbd Mar 31, 2019
9173580
Python 2 compatibility
chdsbd Mar 31, 2019
6209264
Add basic test
chdsbd Mar 31, 2019
399ed6d
doc
chdsbd Mar 31, 2019
1a915a9
Rewrite test to run project script
wagnerluis1982 Oct 7, 2022
121e42f
simplify code
wagnerluis1982 Oct 7, 2022
85174c7
fix type on RunCommand.run_script
wagnerluis1982 Oct 7, 2022
ee4a2a7
fix test for windows platform
wagnerluis1982 Oct 7, 2022
fd4cee7
replace comments for a nice docstring
wagnerluis1982 Oct 8, 2022
c925e39
rename Env.{_bin => get_bin_path}
wagnerluis1982 Oct 8, 2022
7ae7d32
Merge remote-tracking branch 'origin/master' into fix-incorrect-execu…
wagnerluis1982 Jan 11, 2023
4c919b8
rename test method
wagnerluis1982 Jan 11, 2023
81232c0
Merge branch 'master' into fix-incorrect-executable-path
wagnerluis1982 Jan 12, 2023
c761cfe
improve test readability
wagnerluis1982 Jan 15, 2023
e0e01eb
Merge branch 'master' into fix-incorrect-executable-path
wagnerluis1982 Jan 15, 2023
c1d579f
rewrite test assertion
wagnerluis1982 Jan 15, 2023
74b725e
Merge branch 'master' into fix-incorrect-executable-path
wagnerluis1982 Jan 16, 2023
0846cd6
Merge branch 'master' into fix-incorrect-executable-path
wagnerluis1982 Jan 18, 2023
02c9919
Merge branch 'master' into fix-incorrect-executable-path
wagnerluis1982 Jan 20, 2023
e4de1a4
use script_dirs instead of bin_path, make test more realistic
radoering Jan 21, 2023
2bdc670
add script extension ".cmd" when on windows
wagnerluis1982 Jan 21, 2023
713a1de
Merge branch 'master' into fix-incorrect-executable-path
wagnerluis1982 Jan 21, 2023
a758197
do not change args list
wagnerluis1982 Jan 22, 2023
bf6c3ca
Merge branch 'master' into fix-incorrect-executable-path
wagnerluis1982 Jan 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/poetry/console/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from cleo.helpers import argument

from poetry.console.commands.env_command import EnvCommand
from poetry.utils._compat import WINDOWS


if TYPE_CHECKING:
Expand Down Expand Up @@ -44,7 +45,25 @@ def _module(self) -> Module:

return module

def run_script(self, script: str | dict[str, str], args: str) -> int:
def run_script(self, script: str | dict[str, str], args: list[str]) -> int:
"""Runs an entry point script defined in the section ``[tool.poetry.scripts]``.

When a script exists in the venv bin folder, i.e. after ``poetry install``,
then ``sys.argv[0]`` must be set to the full path of the executable, so
``poetry run foo`` and ``poetry shell``, ``foo`` have the same ``sys.argv[0]``
that points to the full path.

Otherwise (when an entry point script does not exist), ``sys.argv[0]`` is the
script name only, i.e. ``poetry run foo`` has ``sys.argv == ['foo']``.
"""
for script_dir in self.env.script_dirs:
script_path = script_dir / args[0]
if WINDOWS:
script_path = script_path.with_suffix(".cmd")
if script_path.exists():
args = [str(script_path), *args[1:]]
break

if isinstance(script, dict):
script = script["callable"]

Expand Down
37 changes: 37 additions & 0 deletions tests/console/commands/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,40 @@ def test_run_script_exit_code(
)
assert tester.execute("exit-code") == 42
assert tester.execute("return-code") == 42


@pytest.mark.parametrize(
"installed_script", [False, True], ids=["not installed", "installed"]
)
def test_run_script_sys_argv0(
installed_script: bool,
poetry_with_scripts: Poetry,
command_tester_factory: CommandTesterFactory,
tmp_venv: VirtualEnv,
mocker: MockerFixture,
) -> None:
"""
If RunCommand calls an installed script defined in pyproject.toml,
sys.argv[0] must be set to the full path of the script.
"""
mocker.patch("poetry.utils.env.EnvManager.get", return_value=tmp_venv)
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
if not installed_script:
for path in tmp_venv.script_dirs[0].glob("check-argv0*"):
path.unlink()
wagnerluis1982 marked this conversation as resolved.
Show resolved Hide resolved

tester = command_tester_factory(
"run", poetry=poetry_with_scripts, environment=tmp_venv
)
argv1 = "absolute" if installed_script else "relative"
assert tester.execute(f"check-argv0 {argv1}") == 0
wagnerluis1982 marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions tests/fixtures/scripts/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ readme = "README.md"
python = "^3.7"

[tool.poetry.scripts]
check-argv0 = "scripts.check_argv0:main"
exit-code = "scripts.exit_code:main"
return-code = "scripts.return_code:main"

Expand Down
23 changes: 23 additions & 0 deletions tests/fixtures/scripts/scripts/check_argv0.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

import sys

from pathlib import Path


def main() -> int:
path = Path(sys.argv[0])
if sys.argv[1] == "absolute":
if not path.is_absolute():
raise RuntimeError(f"sys.argv[0] is not an absolute path: {path}")
if not path.exists():
raise RuntimeError(f"sys.argv[0] does not exist: {path}")
else:
if path.is_absolute():
raise RuntimeError(f"sys.argv[0] is an absolute path: {path}")

return 0


if __name__ == "__main__":
raise sys.exit(main())