You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

baseparser.py 10 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. """Base option parser setup"""
  2. from __future__ import absolute_import
  3. import sys
  4. import optparse
  5. import os
  6. import re
  7. import textwrap
  8. from distutils.util import strtobool
  9. from pip._vendor.six import string_types
  10. from pip._vendor.six.moves import configparser
  11. from pip.locations import (
  12. legacy_config_file, config_basename, running_under_virtualenv,
  13. site_config_files
  14. )
  15. from pip.utils import appdirs, get_terminal_size
  16. _environ_prefix_re = re.compile(r"^PIP_", re.I)
  17. class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
  18. """A prettier/less verbose help formatter for optparse."""
  19. def __init__(self, *args, **kwargs):
  20. # help position must be aligned with __init__.parseopts.description
  21. kwargs['max_help_position'] = 30
  22. kwargs['indent_increment'] = 1
  23. kwargs['width'] = get_terminal_size()[0] - 2
  24. optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs)
  25. def format_option_strings(self, option):
  26. return self._format_option_strings(option, ' <%s>', ', ')
  27. def _format_option_strings(self, option, mvarfmt=' <%s>', optsep=', '):
  28. """
  29. Return a comma-separated list of option strings and metavars.
  30. :param option: tuple of (short opt, long opt), e.g: ('-f', '--format')
  31. :param mvarfmt: metavar format string - evaluated as mvarfmt % metavar
  32. :param optsep: separator
  33. """
  34. opts = []
  35. if option._short_opts:
  36. opts.append(option._short_opts[0])
  37. if option._long_opts:
  38. opts.append(option._long_opts[0])
  39. if len(opts) > 1:
  40. opts.insert(1, optsep)
  41. if option.takes_value():
  42. metavar = option.metavar or option.dest.lower()
  43. opts.append(mvarfmt % metavar.lower())
  44. return ''.join(opts)
  45. def format_heading(self, heading):
  46. if heading == 'Options':
  47. return ''
  48. return heading + ':\n'
  49. def format_usage(self, usage):
  50. """
  51. Ensure there is only one newline between usage and the first heading
  52. if there is no description.
  53. """
  54. msg = '\nUsage: %s\n' % self.indent_lines(textwrap.dedent(usage), " ")
  55. return msg
  56. def format_description(self, description):
  57. # leave full control over description to us
  58. if description:
  59. if hasattr(self.parser, 'main'):
  60. label = 'Commands'
  61. else:
  62. label = 'Description'
  63. # some doc strings have initial newlines, some don't
  64. description = description.lstrip('\n')
  65. # some doc strings have final newlines and spaces, some don't
  66. description = description.rstrip()
  67. # dedent, then reindent
  68. description = self.indent_lines(textwrap.dedent(description), " ")
  69. description = '%s:\n%s\n' % (label, description)
  70. return description
  71. else:
  72. return ''
  73. def format_epilog(self, epilog):
  74. # leave full control over epilog to us
  75. if epilog:
  76. return epilog
  77. else:
  78. return ''
  79. def indent_lines(self, text, indent):
  80. new_lines = [indent + line for line in text.split('\n')]
  81. return "\n".join(new_lines)
  82. class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
  83. """Custom help formatter for use in ConfigOptionParser.
  84. This is updates the defaults before expanding them, allowing
  85. them to show up correctly in the help listing.
  86. """
  87. def expand_default(self, option):
  88. if self.parser is not None:
  89. self.parser._update_defaults(self.parser.defaults)
  90. return optparse.IndentedHelpFormatter.expand_default(self, option)
  91. class CustomOptionParser(optparse.OptionParser):
  92. def insert_option_group(self, idx, *args, **kwargs):
  93. """Insert an OptionGroup at a given position."""
  94. group = self.add_option_group(*args, **kwargs)
  95. self.option_groups.pop()
  96. self.option_groups.insert(idx, group)
  97. return group
  98. @property
  99. def option_list_all(self):
  100. """Get a list of all options, including those in option groups."""
  101. res = self.option_list[:]
  102. for i in self.option_groups:
  103. res.extend(i.option_list)
  104. return res
  105. class ConfigOptionParser(CustomOptionParser):
  106. """Custom option parser which updates its defaults by checking the
  107. configuration files and environmental variables"""
  108. isolated = False
  109. def __init__(self, *args, **kwargs):
  110. self.config = configparser.RawConfigParser()
  111. self.name = kwargs.pop('name')
  112. self.isolated = kwargs.pop("isolated", False)
  113. self.files = self.get_config_files()
  114. if self.files:
  115. self.config.read(self.files)
  116. assert self.name
  117. optparse.OptionParser.__init__(self, *args, **kwargs)
  118. def get_config_files(self):
  119. # the files returned by this method will be parsed in order with the
  120. # first files listed being overridden by later files in standard
  121. # ConfigParser fashion
  122. config_file = os.environ.get('PIP_CONFIG_FILE', False)
  123. if config_file == os.devnull:
  124. return []
  125. # at the base we have any site-wide configuration
  126. files = list(site_config_files)
  127. # per-user configuration next
  128. if not self.isolated:
  129. if config_file and os.path.exists(config_file):
  130. files.append(config_file)
  131. else:
  132. # This is the legacy config file, we consider it to be a lower
  133. # priority than the new file location.
  134. files.append(legacy_config_file)
  135. # This is the new config file, we consider it to be a higher
  136. # priority than the legacy file.
  137. files.append(
  138. os.path.join(
  139. appdirs.user_config_dir("pip"),
  140. config_basename,
  141. )
  142. )
  143. # finally virtualenv configuration first trumping others
  144. if running_under_virtualenv():
  145. venv_config_file = os.path.join(
  146. sys.prefix,
  147. config_basename,
  148. )
  149. if os.path.exists(venv_config_file):
  150. files.append(venv_config_file)
  151. return files
  152. def check_default(self, option, key, val):
  153. try:
  154. return option.check_value(key, val)
  155. except optparse.OptionValueError as exc:
  156. print("An error occurred during configuration: %s" % exc)
  157. sys.exit(3)
  158. def _update_defaults(self, defaults):
  159. """Updates the given defaults with values from the config files and
  160. the environ. Does a little special handling for certain types of
  161. options (lists)."""
  162. # Then go and look for the other sources of configuration:
  163. config = {}
  164. # 1. config files
  165. for section in ('global', self.name):
  166. config.update(
  167. self.normalize_keys(self.get_config_section(section))
  168. )
  169. # 2. environmental variables
  170. if not self.isolated:
  171. config.update(self.normalize_keys(self.get_environ_vars()))
  172. # Accumulate complex default state.
  173. self.values = optparse.Values(self.defaults)
  174. late_eval = set()
  175. # Then set the options with those values
  176. for key, val in config.items():
  177. # ignore empty values
  178. if not val:
  179. continue
  180. option = self.get_option(key)
  181. # Ignore options not present in this parser. E.g. non-globals put
  182. # in [global] by users that want them to apply to all applicable
  183. # commands.
  184. if option is None:
  185. continue
  186. if option.action in ('store_true', 'store_false', 'count'):
  187. val = strtobool(val)
  188. elif option.action == 'append':
  189. val = val.split()
  190. val = [self.check_default(option, key, v) for v in val]
  191. elif option.action == 'callback':
  192. late_eval.add(option.dest)
  193. opt_str = option.get_opt_string()
  194. val = option.convert_value(opt_str, val)
  195. # From take_action
  196. args = option.callback_args or ()
  197. kwargs = option.callback_kwargs or {}
  198. option.callback(option, opt_str, val, self, *args, **kwargs)
  199. else:
  200. val = self.check_default(option, key, val)
  201. defaults[option.dest] = val
  202. for key in late_eval:
  203. defaults[key] = getattr(self.values, key)
  204. self.values = None
  205. return defaults
  206. def normalize_keys(self, items):
  207. """Return a config dictionary with normalized keys regardless of
  208. whether the keys were specified in environment variables or in config
  209. files"""
  210. normalized = {}
  211. for key, val in items:
  212. key = key.replace('_', '-')
  213. if not key.startswith('--'):
  214. key = '--%s' % key # only prefer long opts
  215. normalized[key] = val
  216. return normalized
  217. def get_config_section(self, name):
  218. """Get a section of a configuration"""
  219. if self.config.has_section(name):
  220. return self.config.items(name)
  221. return []
  222. def get_environ_vars(self):
  223. """Returns a generator with all environmental vars with prefix PIP_"""
  224. for key, val in os.environ.items():
  225. if _environ_prefix_re.search(key):
  226. yield (_environ_prefix_re.sub("", key).lower(), val)
  227. def get_default_values(self):
  228. """Overridding to make updating the defaults after instantiation of
  229. the option parser possible, _update_defaults() does the dirty work."""
  230. if not self.process_default_values:
  231. # Old, pre-Optik 1.5 behaviour.
  232. return optparse.Values(self.defaults)
  233. defaults = self._update_defaults(self.defaults.copy()) # ours
  234. for option in self._get_all_options():
  235. default = defaults.get(option.dest)
  236. if isinstance(default, string_types):
  237. opt_str = option.get_opt_string()
  238. defaults[option.dest] = option.check_value(opt_str, default)
  239. return optparse.Values(defaults)
  240. def error(self, msg):
  241. self.print_usage(sys.stderr)
  242. self.exit(2, "%s\n" % msg)