mirror of https://github.com/pypa/pip
Compare commits
13 Commits
3c2c902c05
...
03ad36fcb3
Author | SHA1 | Date |
---|---|---|
chrysle | 03ad36fcb3 | |
Tzu-ping Chung | a15dd75d98 | |
Tzu-ping Chung | d8ab6dc6c1 | |
Qiming Xu | fe10d368f6 | |
Qiming Xu | 28250baffb | |
Qiming Xu | 88ac529219 | |
chrysle | af49b81e05 | |
Pradyun Gedam | ed035ab6f8 | |
chrysle | 8d6c282c91 | |
chrysle | d9b5a9df85 | |
chrysle | f12b8db25f | |
chrysle | 4bd343f805 | |
chrysle | 4b87307b34 |
|
@ -45,8 +45,8 @@ When looking at the items to be installed, pip checks what type of item
|
|||
each is, in the following order:
|
||||
|
||||
1. Project or archive URL.
|
||||
2. Local directory (which must contain a ``setup.py``, or pip will report
|
||||
an error).
|
||||
2. Local directory (which must contain a ``pyproject.toml`` or ``setup.py``,
|
||||
otherwise pip will report an error).
|
||||
3. Local file (a sdist or wheel format archive, following the naming
|
||||
conventions for those formats).
|
||||
4. A requirement, as specified in :pep:`440`.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
This change will fix the incorrect assignment of configuration values to files by ``pip config debug``
|
|
@ -0,0 +1 @@
|
|||
Fix outdated pip install argument description in documentation.
|
|
@ -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
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue