diff options
-rw-r--r-- | docs/changelog/2695.bugfix.rst | 1 | ||||
-rw-r--r-- | src/tox/config/loader/str_convert.py | 2 | ||||
-rw-r--r-- | src/tox/tox_env/python/pip/pip_install.py | 5 | ||||
-rw-r--r-- | tests/config/loader/test_str_convert.py | 1 | ||||
-rw-r--r-- | tests/session/cmd/test_show_config.py | 8 | ||||
-rw-r--r-- | tests/tox_env/python/pip/test_pip_install.py | 11 |
6 files changed, 27 insertions, 1 deletions
diff --git a/docs/changelog/2695.bugfix.rst b/docs/changelog/2695.bugfix.rst new file mode 100644 index 00000000..30668e9c --- /dev/null +++ b/docs/changelog/2695.bugfix.rst @@ -0,0 +1 @@ +Fail more gracefully when pip :ref:`install_command` is empty - by :user:`jayaddison`. diff --git a/src/tox/config/loader/str_convert.py b/src/tox/config/loader/str_convert.py index e31c034f..f07545f5 100644 --- a/src/tox/config/loader/str_convert.py +++ b/src/tox/config/loader/str_convert.py @@ -63,6 +63,8 @@ class StrConvert(Convert[str]): pos = splitter.instream.tell() except ValueError: args.append(value[pos:]) + if len(args) == 0: + raise ValueError(f"attempting to parse {value!r} into a command failed") if args[0] != "-" and args[0].startswith("-"): args[0] = args[0][1:] args = ["-"] + args diff --git a/src/tox/tox_env/python/pip/pip_install.py b/src/tox/tox_env/python/pip/pip_install.py index a148ce32..2136e862 100644 --- a/src/tox/tox_env/python/pip/pip_install.py +++ b/src/tox/tox_env/python/pip/pip_install.py @@ -160,7 +160,10 @@ class Pip(Installer[Python]): outcome.assert_success() def build_install_cmd(self, args: Sequence[str]) -> list[str]: - cmd: Command = self._env.conf["install_command"] + try: + cmd: Command = self._env.conf["install_command"] + except ValueError as exc: + raise Fail(f"unable to determine pip install command: {str(exc)}") from exc install_command = cmd.args try: opts_at = install_command.index("{packages}") diff --git a/tests/config/loader/test_str_convert.py b/tests/config/loader/test_str_convert.py index 49948c4f..4fe0aaff 100644 --- a/tests/config/loader/test_str_convert.py +++ b/tests/config/loader/test_str_convert.py @@ -62,6 +62,7 @@ def test_str_convert_ok(raw: str, value: Any, of_type: type[Any]) -> None: ("a", TypeVar, TypeError, r"a cannot cast to .*typing.TypeVar.*"), ("3", Literal["1", "2"], ValueError, r"3 must be one of \('1', '2'\)"), ("3", Union[str, int], TypeError, r"3 cannot cast to typing.Union\[str, int\]"), + ("", Command, ValueError, r"attempting to parse '' into a command failed"), ], ) def test_str_convert_nok(raw: str, of_type: type[Any], msg: str, exc_type: type[Exception]) -> None: diff --git a/tests/session/cmd/test_show_config.py b/tests/session/cmd/test_show_config.py index 33c4fc48..646dc2bf 100644 --- a/tests/session/cmd/test_show_config.py +++ b/tests/session/cmd/test_show_config.py @@ -99,6 +99,14 @@ def test_show_config_exception(tox_project: ToxProjectCreator) -> None: assert txt in outcome.out +def test_show_config_empty_install_command_exception(tox_project: ToxProjectCreator) -> None: + project = tox_project({"tox.ini": "[testenv:a]\ninstall_command="}) + outcome = project.run("c", "-e", "a", "-k", "install_command") + outcome.assert_success() + txt = "\ninstall_command = # Exception: " "ValueError(\"attempting to parse '' into a command failed\")" + assert txt in outcome.out + + @pytest.mark.parametrize("stdout_is_atty", [True, False]) def test_pass_env_config_default(tox_project: ToxProjectCreator, stdout_is_atty: bool, mocker: MockerFixture) -> None: mocker.patch("sys.stdout.isatty", return_value=stdout_is_atty) diff --git a/tests/tox_env/python/pip/test_pip_install.py b/tests/tox_env/python/pip/test_pip_install.py index bc470573..8fb70a5f 100644 --- a/tests/tox_env/python/pip/test_pip_install.py +++ b/tests/tox_env/python/pip/test_pip_install.py @@ -6,8 +6,10 @@ from typing import Any from unittest.mock import Mock import pytest +from packaging.requirements import Requirement from tox.pytest import CaptureFixture, ToxProjectCreator +from tox.tox_env.errors import Fail @pytest.mark.parametrize("arg", [object, [object]]) @@ -35,6 +37,15 @@ def test_pip_install_empty_list(tox_project: ToxProjectCreator) -> None: assert execute_calls.call_count == 0 +def test_pip_install_empty_command_error(tox_project: ToxProjectCreator) -> None: + proj = tox_project({"tox.ini": "[testenv]\ninstall_command="}) + result = proj.run("l") + pip = result.state.envs["py"].installer + + with pytest.raises(Fail, match="unable to determine pip install command"): + pip.install([Requirement("name")], "section", "type") + + def test_pip_install_flags_only_error(tox_project: ToxProjectCreator) -> None: proj = tox_project({"tox.ini": "[testenv:py]\ndeps=-i a"}) result = proj.run("r") |