Skip to content

Commit

Permalink
Rework the functionality of PIP_CONFIG_FILE (#11850)
Browse files Browse the repository at this point in the history
  • Loading branch information
dalebrydon authored Oct 27, 2023
1 parent 6dbd9c6 commit fd77ebf
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 19 deletions.
19 changes: 12 additions & 7 deletions docs/html/topics/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,24 @@ and how they are related to pip's various command line options.

## Configuration Files

Configuration files can change the default values for command line option.
They are written using a standard INI style configuration files.
Configuration files can change the default values for command line options.
The files are written using standard INI format.

pip has 3 "levels" of configuration files:

- `global`: system-wide configuration file, shared across users.
- `user`: per-user configuration file.
- `site`: per-environment configuration file; i.e. per-virtualenv.

Additionally, environment variables can be specified which will override any of the above.

### Location

pip's configuration files are located in fairly standard locations. This
location is different on different operating systems, and has some additional
complexity for backwards compatibility reasons.
complexity for backwards compatibility reasons. Note that if user config files
exist in both the legacy and current locations, values in the current file
will override values in the legacy file.

```{tab} Unix
Expand Down Expand Up @@ -88,9 +92,10 @@ Site
### `PIP_CONFIG_FILE`

Additionally, the environment variable `PIP_CONFIG_FILE` can be used to specify
a configuration file that's loaded first, and whose values are overridden by
the values set in the aforementioned files. Setting this to {any}`os.devnull`
disables the loading of _all_ configuration files.
a configuration file that's loaded last, and whose values override the values
set in the aforementioned files. Setting this to {any}`os.devnull`
disables the loading of _all_ configuration files. Note that if a file exists
at the location that this is set to, the user config file will not be loaded.

(config-precedence)=

Expand All @@ -99,10 +104,10 @@ disables the loading of _all_ configuration files.
When multiple configuration files are found, pip combines them in the following
order:

- `PIP_CONFIG_FILE`, if given.
- Global
- User
- Site
- `PIP_CONFIG_FILE`, if given.

Each file read overrides any values read from previous files, so if the
global timeout is specified in both the global file and the per-user file
Expand Down
1 change: 1 addition & 0 deletions news/11815.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix explanation of how PIP_CONFIG_FILE works
26 changes: 14 additions & 12 deletions src/pip/_internal/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,33 +327,35 @@ def get_environ_vars(self) -> Iterable[Tuple[str, str]]:
def iter_config_files(self) -> Iterable[Tuple[Kind, List[str]]]:
"""Yields variant and configuration files associated with it.
This should be treated like items of a dictionary.
This should be treated like items of a dictionary. The order
here doesn't affect what gets overridden. That is controlled
by OVERRIDE_ORDER. However this does control the order they are
displayed to the user. It's probably most ergononmic to display
things in the same order as OVERRIDE_ORDER
"""
# SMELL: Move the conditions out of this function

# environment variables have the lowest priority
config_file = os.environ.get("PIP_CONFIG_FILE", None)
if config_file is not None:
yield kinds.ENV, [config_file]
else:
yield kinds.ENV, []

env_config_file = os.environ.get("PIP_CONFIG_FILE", None)
config_files = get_configuration_files()

# at the base we have any global configuration
yield kinds.GLOBAL, config_files[kinds.GLOBAL]

# per-user configuration next
# per-user config is not loaded when env_config_file exists
should_load_user_config = not self.isolated and not (
config_file and os.path.exists(config_file)
env_config_file and os.path.exists(env_config_file)
)
if should_load_user_config:
# The legacy config file is overridden by the new config file
yield kinds.USER, config_files[kinds.USER]

# finally virtualenv configuration first trumping others
# virtualenv config
yield kinds.SITE, config_files[kinds.SITE]

if env_config_file is not None:
yield kinds.ENV, [env_config_file]
else:
yield kinds.ENV, []

def get_values_in_config(self, variant: Kind) -> Dict[str, Any]:
"""Get values present in a config file"""
return self._config[variant]
Expand Down

0 comments on commit fd77ebf

Please sign in to comment.