diff --git a/src/pip/_internal/configuration.py b/src/pip/_internal/configuration.py index a2b0607c5..8fd46c9b8 100644 --- a/src/pip/_internal/configuration.py +++ b/src/pip/_internal/configuration.py @@ -147,6 +147,9 @@ class Configuration: try: return self._dictionary[key] except KeyError: + # disassembling triggers a more useful error message than simply + # "No such key" in the case that the key isn't in the form command.option + _disassemble_key(key) raise ConfigurationError(f"No such key - {orig_key}") def set_value(self, key: str, value: Any) -> None: diff --git a/tests/unit/test_configuration.py b/tests/unit/test_configuration.py index dabbb73ea..c6b44d45a 100644 --- a/tests/unit/test_configuration.py +++ b/tests/unit/test_configuration.py @@ -1,6 +1,7 @@ """Tests for all things related to the configuration """ +import re from unittest.mock import MagicMock import pytest @@ -87,6 +88,25 @@ class TestConfigurationLoading(ConfigurationMixin): err.value ) + def test_no_such_key_error_message_no_command(self) -> None: + self.configuration.load_only = kinds.GLOBAL + self.configuration.load() + expected_msg = ( + "Key does not contain dot separated section and key. " + "Perhaps you wanted to use 'global.index-url' instead?" + ) + pat = f"^{re.escape(expected_msg)}$" + with pytest.raises(ConfigurationError, match=pat): + self.configuration.get_value("index-url") + + def test_no_such_key_error_message_missing_option(self) -> None: + self.configuration.load_only = kinds.GLOBAL + self.configuration.load() + expected_msg = "No such key - global.index-url" + pat = f"^{re.escape(expected_msg)}$" + with pytest.raises(ConfigurationError, match=pat): + self.configuration.get_value("global.index-url") + class TestConfigurationPrecedence(ConfigurationMixin): # Tests for methods to that determine the order of precedence of