diff --git a/news/13793.bugfix.rst b/news/13793.bugfix.rst new file mode 100644 index 00000000000..516ba51d446 --- /dev/null +++ b/news/13793.bugfix.rst @@ -0,0 +1 @@ +Include the underlying exception type/message in the self version check warning to improve diagnosability. diff --git a/src/pip/_internal/cli/index_command.py b/src/pip/_internal/cli/index_command.py index 0f1ee1a4f82..ba9491907e1 100644 --- a/src/pip/_internal/cli/index_command.py +++ b/src/pip/_internal/cli/index_command.py @@ -190,6 +190,10 @@ def handle_pip_version_check(self, options: Values) -> None: ) with session: _pip_self_version_check(session, options) - except Exception: - logger.warning("There was an error checking the latest version of pip.") + except Exception as e: + logger.warning( + "There was an error checking the latest version of pip. (%s: %s)", + e.__class__.__name__, + e, + ) logger.debug("See below for error", exc_info=True) diff --git a/src/pip/_internal/configuration.py b/src/pip/_internal/configuration.py index e164653bbc2..0816de012cc 100644 --- a/src/pip/_internal/configuration.py +++ b/src/pip/_internal/configuration.py @@ -378,9 +378,16 @@ def _get_parser_to_modify(self) -> tuple[str, RawConfigParser]: assert self.load_only parsers = self._parsers[self.load_only] if not parsers: - # This should not happen if everything works correctly. + env_config_file = os.environ.get("PIP_CONFIG_FILE") + if env_config_file and not os.path.isfile(env_config_file): + raise ConfigurationError( + "Cannot modify pip configuration: " + "PIP_CONFIG_FILE is not a regular file: " + f"{env_config_file}" + ) + raise ConfigurationError( - "Fatal Internal error [id=2]. Please report as a bug." + "Cannot modify pip configuration: no configuration file is available." ) # Use the highest priority parser. diff --git a/tests/functional/test_configuration.py b/tests/functional/test_configuration.py index b235212477a..3c8b8c6670e 100644 --- a/tests/functional/test_configuration.py +++ b/tests/functional/test_configuration.py @@ -216,6 +216,22 @@ def test_config_separated( result.stdout, ) + def test_config_set_with_pip_config_file_devnull_shows_human_error( + self, script: PipTestEnvironment + ) -> None: + script.environ["PIP_CONFIG_FILE"] = os.devnull + + result = script.pip( + "config", + "set", + "global.index-url", + "https://example.com/", + expect_error=True, + ) + assert "Fatal Internal error [id=2]" not in result.stderr + assert "PIP_CONFIG_FILE" in result.stderr + assert "not a regular file" in result.stderr + @pytest.mark.network def test_editable_mode_default_config( self, script: PipTestEnvironment, data: TestData diff --git a/tests/unit/test_commands.py b/tests/unit/test_commands.py index 04ddd0316cc..915023ce22e 100644 --- a/tests/unit/test_commands.py +++ b/tests/unit/test_commands.py @@ -1,3 +1,4 @@ +import logging import os from typing import Callable from unittest import mock @@ -158,3 +159,27 @@ def test_list_pip_version_check(version_check_mock: mock.Mock, flag: str) -> Non version_check_mock.assert_called_once() else: version_check_mock.assert_not_called() + + +@mock.patch("pip._internal.cli.index_command._pip_self_version_check") +def test_handle_pip_version_check_warning_includes_reason( + mock_version_check: mock.Mock, caplog: pytest.LogCaptureFixture +) -> None: + mock_version_check.side_effect = PermissionError("Access is denied") + + cmd = create_command("install") + options = cmd.parser.get_default_values() + options.disable_pip_version_check = False + options.no_index = False + + with mock.patch.object( + cmd, + "_build_session", + side_effect=PermissionError("Access is denied"), + ): + with caplog.at_level(logging.WARNING): + cmd.handle_pip_version_check(options) + + assert "There was an error checking the latest version of pip." in caplog.text + assert "PermissionError" in caplog.text + assert "Access is denied" in caplog.text