Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/1234.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The config precedence can now be overridden with a special ``config-precedence`` config value
3 changes: 2 additions & 1 deletion src/pip/_internal/commands/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ def list_config_values(self, options: Values, args: List[str]) -> None:
write_output("%s, exists: %r", fname, file_exists)
if file_exists:
self.print_config_file_values(variant)
write_output("load-precedence: %s", ", ".join(self.configuration.load_order))

def print_config_file_values(self, variant: Kind) -> None:
"""Get key-value pairs from the file of a variant"""
Expand All @@ -216,7 +217,7 @@ def print_config_file_values(self, variant: Kind) -> None:

def print_env_var_values(self) -> None:
"""Get key-values pairs present as environment variables"""
write_output("%s:", "env_var")
write_output("%s:", "env-var")
with indent_log():
for key, value in sorted(self.configuration.get_environ_vars()):
env_var = f"PIP_{key.upper()}"
Expand Down
45 changes: 44 additions & 1 deletion src/pip/_internal/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ def __init__(self, isolated: bool, load_only: Optional[Kind] = None) -> None:
self.isolated = isolated
self.load_only = load_only

self._load_order: Tuple[Kind, ...] = OVERRIDE_ORDER

# Because we keep track of where we got the data from
self._parsers: Dict[Kind, List[Tuple[str, RawConfigParser]]] = {
variant: [] for variant in OVERRIDE_ORDER
Expand All @@ -125,6 +127,13 @@ def load(self) -> None:
if not self.isolated:
self._load_environment_vars()

@property
def load_order(self) -> Tuple[Kind, ...]:
# The act of computing the config dictionary will result in the _load_order
# attribute being correctly updated.
_ = self._dictionary
return self._load_order

def get_file_to_edit(self) -> Optional[str]:
"""Returns the file with highest priority in configuration"""
assert self.load_only is not None, "Need to be specified a file to be editing"
Expand Down Expand Up @@ -225,11 +234,45 @@ def _ensure_have_load_only(self) -> None:
@property
def _dictionary(self) -> Dict[str, Any]:
"""A dictionary representing the loaded configuration."""

# We always read the config in the default load-order first, giving
# us a deterministic load-order configuration value.
config = self._blended_config_dict(OVERRIDE_ORDER)

# Re-compose the config based on the desired override-order, if
# different to the default.
config_precedence: List[str] = (
str(config.get("global.config-precedence", "")).strip().splitlines()
)
if config_precedence:
bad_values = [
value
for value in config_precedence
if value not in kinds.reverse_mapping
]
if bad_values:
term_or_terms = "term" if len(bad_values) == 1 else "terms"
raise ConfigurationError(
f"Invalid config-precedence {term_or_terms} provided "
f"({','.join(bad_values)}). "
f"Valid values are {', '.join(map(repr, kinds.reverse_mapping))}."
)
self._load_order = tuple(
getattr(kinds, kinds.reverse_mapping[value])
for value in config_precedence
)
if self._load_order != OVERRIDE_ORDER:
config = self._blended_config_dict(self._load_order)
return config

def _blended_config_dict(
self, load_order: Tuple[Kind, ...] = OVERRIDE_ORDER
) -> Dict[str, Any]:
# NOTE: Dictionaries are not populated if not loaded. So, conditionals
# are not needed here.
retval = {}

for variant in OVERRIDE_ORDER:
for variant in load_order:
retval.update(self._config[variant])

return retval
Expand Down