This commit is contained in:
chrysle 2023-11-29 15:16:42 -07:00 committed by GitHub
commit 03ad36fcb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 28 deletions

1
news/12099.bugfix.rst Normal file
View File

@ -0,0 +1 @@
This change will fix the incorrect assignment of configuration values to files by ``pip config debug``

View File

@ -185,23 +185,38 @@ class ConfigOptionParser(CustomOptionParser):
section_items: Dict[str, List[Tuple[str, Any]]] = {
name: [] for name in override_order
}
for section_key, val in self.config.items():
# ignore empty values
if not val:
logger.debug(
"Ignoring configuration key '%s' as it's value is empty.",
section_key,
)
continue
section, key = section_key.split(".", 1)
if section in override_order:
section_items[section].append((key, val))
for key, value in self.config.items():
if not isinstance(value, dict):
# ignore empty values
if not value:
logger.debug(
"Ignoring configuration key '%s' as it's empty.",
key,
)
continue
# Yield each group in their override order
for section in override_order:
for key, val in section_items[section]:
yield key, val
section, key = key.split(".", 1)
if section in override_order:
section_items[section].append((key, value))
else:
for section_key, val in value.items():
# ignore empty values
if not val:
logger.debug(
"Ignoring configuration key '%s' as it's value is empty.",
section_key,
)
continue
section, key = section_key.split(".", 1)
if section in override_order:
section_items[section].append((key, val))
# Yield each group in their override order
for section in override_order:
for key, val in section_items[section]:
yield key, val
def _update_defaults(self, defaults: Dict[str, Any]) -> Dict[str, Any]:
"""Updates the given defaults with values from the config files and

View File

@ -172,7 +172,11 @@ class ConfigurationCommand(Command):
self._get_n_args(args, "list", n=0)
for key, value in sorted(self.configuration.items()):
write_output("%s=%r", key, value)
if isinstance(value, dict):
for key, value in sorted(value.items()):
write_output("%s=%r", key, value)
else:
write_output("%s=%r", key, value)
def get_name(self, options: Values, args: List[str]) -> None:
key = self._get_n_args(args, "get [name]", n=1)
@ -206,13 +210,15 @@ class ConfigurationCommand(Command):
file_exists = os.path.exists(fname)
write_output("%s, exists: %r", fname, file_exists)
if file_exists:
self.print_config_file_values(variant)
self.print_config_file_values(variant, fname)
def print_config_file_values(self, variant: Kind) -> None:
def print_config_file_values(self, variant: Kind, fname: str) -> None:
"""Get key-value pairs from the file of a variant"""
for name, value in self.configuration.get_values_in_config(variant).items():
with indent_log():
write_output("%s: %s", name, value)
if name == fname:
for confname, confvalue in value.items():
write_output("%s: %s", confname, confvalue)
def print_env_var_values(self) -> None:
"""Get key-values pairs present as environment variables"""

View File

@ -114,7 +114,7 @@ class Configuration:
self._parsers: Dict[Kind, List[Tuple[str, RawConfigParser]]] = {
variant: [] for variant in OVERRIDE_ORDER
}
self._config: Dict[Kind, Dict[str, Any]] = {
self._config: Dict[Kind, Dict[str, Dict[str, Any]]] = {
variant: {} for variant in OVERRIDE_ORDER
}
self._modified_parsers: List[Tuple[str, RawConfigParser]] = []
@ -145,7 +145,13 @@ class Configuration:
orig_key = key
key = _normalize_name(key)
try:
return self._dictionary[key]
clean_config = {}
for k, value in self._dictionary.items():
if isinstance(value, dict):
clean_config.update(value)
else:
clean_config[k] = value
return clean_config[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
@ -168,7 +174,8 @@ class Configuration:
parser.add_section(section)
parser.set(section, name, value)
self._config[self.load_only][key] = value
self._config[self.load_only].setdefault(fname, {})
self._config[self.load_only][fname][key] = value
self._mark_as_modified(fname, parser)
def unset_value(self, key: str) -> None:
@ -178,11 +185,14 @@ class Configuration:
self._ensure_have_load_only()
assert self.load_only
if key not in self._config[self.load_only]:
raise ConfigurationError(f"No such key - {orig_key}")
fname, parser = self._get_parser_to_modify()
if (
key not in self._config[self.load_only][fname]
and key not in self._config[self.load_only]
):
raise ConfigurationError(f"No such key - {orig_key}")
if parser is not None:
section, name = _disassemble_key(key)
if not (
@ -197,8 +207,10 @@ class Configuration:
if not parser.items(section):
parser.remove_section(section)
self._mark_as_modified(fname, parser)
del self._config[self.load_only][key]
try:
del self._config[self.load_only][fname][key]
except KeyError:
del self._config[self.load_only][key]
def save(self) -> None:
"""Save the current in-memory state."""
@ -270,7 +282,8 @@ class Configuration:
for section in parser.sections():
items = parser.items(section)
self._config[variant].update(self._normalized_keys(section, items))
self._config[variant].setdefault(fname, {})
self._config[variant][fname].update(self._normalized_keys(section, items))
return parser

View File

@ -1,5 +1,6 @@
"""Tests for the config command
"""
import os
import re
import textwrap
@ -110,6 +111,9 @@ class TestBasicLoading(ConfigurationMixin):
assert "freeze.timeout: 10" in result.stdout
assert re.search(r"user:\n( .+\n)+", result.stdout)
# Avoid state leaking for tests re-using the new config file
os.remove(new_config_file)
def test_site_values(
self, script: PipTestEnvironment, virtualenv: VirtualEnvironment
) -> None:
@ -147,3 +151,37 @@ class TestBasicLoading(ConfigurationMixin):
"config", "edit", "--editor", "notrealeditor", expect_error=True
)
assert "notrealeditor" in result.stderr
def test_config_separated(
self, script: PipTestEnvironment, virtualenv: VirtualEnvironment
) -> None:
"""Test that the pip configuration values in the different config sections
are correctly assigned to their origin files.
"""
# Use new config file
new_config_file = get_configuration_files()[kinds.USER][1]
# Get legacy config file and touch it for testing purposes
legacy_config_file = get_configuration_files()[kinds.USER][0]
os.makedirs(os.path.dirname(legacy_config_file))
open(legacy_config_file, "a").close()
# Site config file
site_config_file = virtualenv.location / CONFIG_BASENAME
script.pip("config", "--user", "set", "global.timeout", "60")
script.pip("config", "--site", "set", "freeze.timeout", "10")
result = script.pip("config", "debug")
assert (
f"{site_config_file}, exists: True\n freeze.timeout: 10" in result.stdout
)
assert (
f"{new_config_file}, exists: True\n global.timeout: 60" in result.stdout
)
assert re.search(
rf"{legacy_config_file}, exists: True\n( {new_config_file}.+\n)+",
result.stdout,
)