|
- """Base option parser setup"""
- from __future__ import absolute_import
-
- import sys
- import optparse
- import os
- import re
- import textwrap
- from distutils.util import strtobool
-
- from pip._vendor.six import string_types
- from pip._vendor.six.moves import configparser
- from pip.locations import (
- legacy_config_file, config_basename, running_under_virtualenv,
- site_config_files
- )
- from pip.utils import appdirs, get_terminal_size
-
-
- _environ_prefix_re = re.compile(r"^PIP_", re.I)
-
-
- class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
- """A prettier/less verbose help formatter for optparse."""
-
- def __init__(self, *args, **kwargs):
- # help position must be aligned with __init__.parseopts.description
- kwargs['max_help_position'] = 30
- kwargs['indent_increment'] = 1
- kwargs['width'] = get_terminal_size()[0] - 2
- optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs)
-
- def format_option_strings(self, option):
- return self._format_option_strings(option, ' <%s>', ', ')
-
- def _format_option_strings(self, option, mvarfmt=' <%s>', optsep=', '):
- """
- Return a comma-separated list of option strings and metavars.
-
- :param option: tuple of (short opt, long opt), e.g: ('-f', '--format')
- :param mvarfmt: metavar format string - evaluated as mvarfmt % metavar
- :param optsep: separator
- """
- opts = []
-
- if option._short_opts:
- opts.append(option._short_opts[0])
- if option._long_opts:
- opts.append(option._long_opts[0])
- if len(opts) > 1:
- opts.insert(1, optsep)
-
- if option.takes_value():
- metavar = option.metavar or option.dest.lower()
- opts.append(mvarfmt % metavar.lower())
-
- return ''.join(opts)
-
- def format_heading(self, heading):
- if heading == 'Options':
- return ''
- return heading + ':\n'
-
- def format_usage(self, usage):
- """
- Ensure there is only one newline between usage and the first heading
- if there is no description.
- """
- msg = '\nUsage: %s\n' % self.indent_lines(textwrap.dedent(usage), " ")
- return msg
-
- def format_description(self, description):
- # leave full control over description to us
- if description:
- if hasattr(self.parser, 'main'):
- label = 'Commands'
- else:
- label = 'Description'
- # some doc strings have initial newlines, some don't
- description = description.lstrip('\n')
- # some doc strings have final newlines and spaces, some don't
- description = description.rstrip()
- # dedent, then reindent
- description = self.indent_lines(textwrap.dedent(description), " ")
- description = '%s:\n%s\n' % (label, description)
- return description
- else:
- return ''
-
- def format_epilog(self, epilog):
- # leave full control over epilog to us
- if epilog:
- return epilog
- else:
- return ''
-
- def indent_lines(self, text, indent):
- new_lines = [indent + line for line in text.split('\n')]
- return "\n".join(new_lines)
-
-
- class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
- """Custom help formatter for use in ConfigOptionParser.
-
- This is updates the defaults before expanding them, allowing
- them to show up correctly in the help listing.
- """
-
- def expand_default(self, option):
- if self.parser is not None:
- self.parser._update_defaults(self.parser.defaults)
- return optparse.IndentedHelpFormatter.expand_default(self, option)
-
-
- class CustomOptionParser(optparse.OptionParser):
- def insert_option_group(self, idx, *args, **kwargs):
- """Insert an OptionGroup at a given position."""
- group = self.add_option_group(*args, **kwargs)
-
- self.option_groups.pop()
- self.option_groups.insert(idx, group)
-
- return group
-
- @property
- def option_list_all(self):
- """Get a list of all options, including those in option groups."""
- res = self.option_list[:]
- for i in self.option_groups:
- res.extend(i.option_list)
-
- return res
-
-
- class ConfigOptionParser(CustomOptionParser):
- """Custom option parser which updates its defaults by checking the
- configuration files and environmental variables"""
-
- isolated = False
-
- def __init__(self, *args, **kwargs):
- self.config = configparser.RawConfigParser()
- self.name = kwargs.pop('name')
- self.isolated = kwargs.pop("isolated", False)
- self.files = self.get_config_files()
- if self.files:
- self.config.read(self.files)
- assert self.name
- optparse.OptionParser.__init__(self, *args, **kwargs)
-
- def get_config_files(self):
- # the files returned by this method will be parsed in order with the
- # first files listed being overridden by later files in standard
- # ConfigParser fashion
- config_file = os.environ.get('PIP_CONFIG_FILE', False)
- if config_file == os.devnull:
- return []
-
- # at the base we have any site-wide configuration
- files = list(site_config_files)
-
- # per-user configuration next
- if not self.isolated:
- if config_file and os.path.exists(config_file):
- files.append(config_file)
- else:
- # This is the legacy config file, we consider it to be a lower
- # priority than the new file location.
- files.append(legacy_config_file)
-
- # This is the new config file, we consider it to be a higher
- # priority than the legacy file.
- files.append(
- os.path.join(
- appdirs.user_config_dir("pip"),
- config_basename,
- )
- )
-
- # finally virtualenv configuration first trumping others
- if running_under_virtualenv():
- venv_config_file = os.path.join(
- sys.prefix,
- config_basename,
- )
- if os.path.exists(venv_config_file):
- files.append(venv_config_file)
-
- return files
-
- def check_default(self, option, key, val):
- try:
- return option.check_value(key, val)
- except optparse.OptionValueError as exc:
- print("An error occurred during configuration: %s" % exc)
- sys.exit(3)
-
- def _update_defaults(self, defaults):
- """Updates the given defaults with values from the config files and
- the environ. Does a little special handling for certain types of
- options (lists)."""
- # Then go and look for the other sources of configuration:
- config = {}
- # 1. config files
- for section in ('global', self.name):
- config.update(
- self.normalize_keys(self.get_config_section(section))
- )
- # 2. environmental variables
- if not self.isolated:
- config.update(self.normalize_keys(self.get_environ_vars()))
- # Accumulate complex default state.
- self.values = optparse.Values(self.defaults)
- late_eval = set()
- # Then set the options with those values
- for key, val in config.items():
- # ignore empty values
- if not val:
- continue
-
- option = self.get_option(key)
- # Ignore options not present in this parser. E.g. non-globals put
- # in [global] by users that want them to apply to all applicable
- # commands.
- if option is None:
- continue
-
- if option.action in ('store_true', 'store_false', 'count'):
- val = strtobool(val)
- elif option.action == 'append':
- val = val.split()
- val = [self.check_default(option, key, v) for v in val]
- elif option.action == 'callback':
- late_eval.add(option.dest)
- opt_str = option.get_opt_string()
- val = option.convert_value(opt_str, val)
- # From take_action
- args = option.callback_args or ()
- kwargs = option.callback_kwargs or {}
- option.callback(option, opt_str, val, self, *args, **kwargs)
- else:
- val = self.check_default(option, key, val)
-
- defaults[option.dest] = val
-
- for key in late_eval:
- defaults[key] = getattr(self.values, key)
- self.values = None
- 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"""
- if self.config.has_section(name):
- return self.config.items(name)
- return []
-
- def get_environ_vars(self):
- """Returns a generator with all environmental vars with prefix PIP_"""
- for key, val in os.environ.items():
- if _environ_prefix_re.search(key):
- yield (_environ_prefix_re.sub("", key).lower(), val)
-
- def get_default_values(self):
- """Overridding to make updating the defaults after instantiation of
- the option parser possible, _update_defaults() does the dirty work."""
- if not self.process_default_values:
- # Old, pre-Optik 1.5 behaviour.
- return optparse.Values(self.defaults)
-
- defaults = self._update_defaults(self.defaults.copy()) # ours
- for option in self._get_all_options():
- default = defaults.get(option.dest)
- if isinstance(default, string_types):
- opt_str = option.get_opt_string()
- defaults[option.dest] = option.check_value(opt_str, default)
- return optparse.Values(defaults)
-
- def error(self, msg):
- self.print_usage(sys.stderr)
- self.exit(2, "%s\n" % msg)
|