1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

Fix for subtle config override bug

Config keys should be normalized (i.e., have underscores replaced with
dashes) *before* the config dictionaries are merged.

Otherwise, if you set a value in a config file and via an environment
variable both the dash separated key (from the config file) and the
underscore separated key (from the environment variable) end up in the
config dictionary. Usually this doesn't matter because the latter key
comes after the former when the dictionary is iterated over. But in
cases where the two config keys hash to the same value module the size
of the dictionary, the order is reversed and the wrong value takes
precedance.

For instance with 'index-url' there's no problem:

    >>> repr({'index-url': 1, 'index_url': 2})
    "{'index-url': 1, 'index_url': 2}"

But with 'no-index', the value from the config file overwrites the
value from the environment variable:

    >>> repr({'no-index': 1, 'no_index': 2})
    "{'no_index': 2, 'no-index': 1}"
This commit is contained in:
David Evans 2011-12-07 16:47:08 +00:00
parent 68e18e593d
commit 7a889d800c

View file

@ -46,14 +46,11 @@ class ConfigOptionParser(optparse.OptionParser):
config = {}
# 1. config files
for section in ('global', self.name):
config.update(dict(self.get_config_section(section)))
config.update(self.normalize_keys(self.get_config_section(section)))
# 2. environmental variables
config.update(dict(self.get_environ_vars()))
config.update(self.normalize_keys(self.get_environ_vars()))
# Then set the options with those values
for key, val in config.items():
key = key.replace('_', '-')
if not key.startswith('--'):
key = '--%s' % key # only prefer long opts
option = self.get_option(key)
if option is not None:
# ignore empty values
@ -74,6 +71,18 @@ class ConfigOptionParser(optparse.OptionParser):
sys.exit(3)
defaults[option.dest] = val
return defaults
def normalize_keys(self, items):
"""Return a config dictionary with normalized keys regardless of
whether the keys were specified in environment variables or in config
files"""
normalized = {}
for key, val in items:
key = key.replace('_', '-')
if not key.startswith('--'):
key = '--%s' % key # only prefer long opts
normalized[key] = val
return normalized
def get_config_section(self, name):
"""Get a section of a configuration"""