comparison planemo/lib/python3.7/site-packages/pip/_internal/cli/parser.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 """Base option parser setup"""
2 from __future__ import absolute_import
3
4 import logging
5 import optparse
6 import sys
7 import textwrap
8 from distutils.util import strtobool
9
10 from pip._vendor.six import string_types
11
12 from pip._internal.cli.status_codes import UNKNOWN_ERROR
13 from pip._internal.configuration import Configuration, ConfigurationError
14 from pip._internal.utils.compat import get_terminal_size
15
16 logger = logging.getLogger(__name__)
17
18
19 class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
20 """A prettier/less verbose help formatter for optparse."""
21
22 def __init__(self, *args, **kwargs):
23 # help position must be aligned with __init__.parseopts.description
24 kwargs['max_help_position'] = 30
25 kwargs['indent_increment'] = 1
26 kwargs['width'] = get_terminal_size()[0] - 2
27 optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs)
28
29 def format_option_strings(self, option):
30 return self._format_option_strings(option, ' <%s>', ', ')
31
32 def _format_option_strings(self, option, mvarfmt=' <%s>', optsep=', '):
33 """
34 Return a comma-separated list of option strings and metavars.
35
36 :param option: tuple of (short opt, long opt), e.g: ('-f', '--format')
37 :param mvarfmt: metavar format string - evaluated as mvarfmt % metavar
38 :param optsep: separator
39 """
40 opts = []
41
42 if option._short_opts:
43 opts.append(option._short_opts[0])
44 if option._long_opts:
45 opts.append(option._long_opts[0])
46 if len(opts) > 1:
47 opts.insert(1, optsep)
48
49 if option.takes_value():
50 metavar = option.metavar or option.dest.lower()
51 opts.append(mvarfmt % metavar.lower())
52
53 return ''.join(opts)
54
55 def format_heading(self, heading):
56 if heading == 'Options':
57 return ''
58 return heading + ':\n'
59
60 def format_usage(self, usage):
61 """
62 Ensure there is only one newline between usage and the first heading
63 if there is no description.
64 """
65 msg = '\nUsage: %s\n' % self.indent_lines(textwrap.dedent(usage), " ")
66 return msg
67
68 def format_description(self, description):
69 # leave full control over description to us
70 if description:
71 if hasattr(self.parser, 'main'):
72 label = 'Commands'
73 else:
74 label = 'Description'
75 # some doc strings have initial newlines, some don't
76 description = description.lstrip('\n')
77 # some doc strings have final newlines and spaces, some don't
78 description = description.rstrip()
79 # dedent, then reindent
80 description = self.indent_lines(textwrap.dedent(description), " ")
81 description = '%s:\n%s\n' % (label, description)
82 return description
83 else:
84 return ''
85
86 def format_epilog(self, epilog):
87 # leave full control over epilog to us
88 if epilog:
89 return epilog
90 else:
91 return ''
92
93 def indent_lines(self, text, indent):
94 new_lines = [indent + line for line in text.split('\n')]
95 return "\n".join(new_lines)
96
97
98 class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
99 """Custom help formatter for use in ConfigOptionParser.
100
101 This is updates the defaults before expanding them, allowing
102 them to show up correctly in the help listing.
103 """
104
105 def expand_default(self, option):
106 if self.parser is not None:
107 self.parser._update_defaults(self.parser.defaults)
108 return optparse.IndentedHelpFormatter.expand_default(self, option)
109
110
111 class CustomOptionParser(optparse.OptionParser):
112
113 def insert_option_group(self, idx, *args, **kwargs):
114 """Insert an OptionGroup at a given position."""
115 group = self.add_option_group(*args, **kwargs)
116
117 self.option_groups.pop()
118 self.option_groups.insert(idx, group)
119
120 return group
121
122 @property
123 def option_list_all(self):
124 """Get a list of all options, including those in option groups."""
125 res = self.option_list[:]
126 for i in self.option_groups:
127 res.extend(i.option_list)
128
129 return res
130
131
132 class ConfigOptionParser(CustomOptionParser):
133 """Custom option parser which updates its defaults by checking the
134 configuration files and environmental variables"""
135
136 def __init__(self, *args, **kwargs):
137 self.name = kwargs.pop('name')
138
139 isolated = kwargs.pop("isolated", False)
140 self.config = Configuration(isolated)
141
142 assert self.name
143 optparse.OptionParser.__init__(self, *args, **kwargs)
144
145 def check_default(self, option, key, val):
146 try:
147 return option.check_value(key, val)
148 except optparse.OptionValueError as exc:
149 print("An error occurred during configuration: %s" % exc)
150 sys.exit(3)
151
152 def _get_ordered_configuration_items(self):
153 # Configuration gives keys in an unordered manner. Order them.
154 override_order = ["global", self.name, ":env:"]
155
156 # Pool the options into different groups
157 section_items = {name: [] for name in override_order}
158 for section_key, val in self.config.items():
159 # ignore empty values
160 if not val:
161 logger.debug(
162 "Ignoring configuration key '%s' as it's value is empty.",
163 section_key
164 )
165 continue
166
167 section, key = section_key.split(".", 1)
168 if section in override_order:
169 section_items[section].append((key, val))
170
171 # Yield each group in their override order
172 for section in override_order:
173 for key, val in section_items[section]:
174 yield key, val
175
176 def _update_defaults(self, defaults):
177 """Updates the given defaults with values from the config files and
178 the environ. Does a little special handling for certain types of
179 options (lists)."""
180
181 # Accumulate complex default state.
182 self.values = optparse.Values(self.defaults)
183 late_eval = set()
184 # Then set the options with those values
185 for key, val in self._get_ordered_configuration_items():
186 # '--' because configuration supports only long names
187 option = self.get_option('--' + key)
188
189 # Ignore options not present in this parser. E.g. non-globals put
190 # in [global] by users that want them to apply to all applicable
191 # commands.
192 if option is None:
193 continue
194
195 if option.action in ('store_true', 'store_false', 'count'):
196 try:
197 val = strtobool(val)
198 except ValueError:
199 error_msg = invalid_config_error_message(
200 option.action, key, val
201 )
202 self.error(error_msg)
203
204 elif option.action == 'append':
205 val = val.split()
206 val = [self.check_default(option, key, v) for v in val]
207 elif option.action == 'callback':
208 late_eval.add(option.dest)
209 opt_str = option.get_opt_string()
210 val = option.convert_value(opt_str, val)
211 # From take_action
212 args = option.callback_args or ()
213 kwargs = option.callback_kwargs or {}
214 option.callback(option, opt_str, val, self, *args, **kwargs)
215 else:
216 val = self.check_default(option, key, val)
217
218 defaults[option.dest] = val
219
220 for key in late_eval:
221 defaults[key] = getattr(self.values, key)
222 self.values = None
223 return defaults
224
225 def get_default_values(self):
226 """Overriding to make updating the defaults after instantiation of
227 the option parser possible, _update_defaults() does the dirty work."""
228 if not self.process_default_values:
229 # Old, pre-Optik 1.5 behaviour.
230 return optparse.Values(self.defaults)
231
232 # Load the configuration, or error out in case of an error
233 try:
234 self.config.load()
235 except ConfigurationError as err:
236 self.exit(UNKNOWN_ERROR, str(err))
237
238 defaults = self._update_defaults(self.defaults.copy()) # ours
239 for option in self._get_all_options():
240 default = defaults.get(option.dest)
241 if isinstance(default, string_types):
242 opt_str = option.get_opt_string()
243 defaults[option.dest] = option.check_value(opt_str, default)
244 return optparse.Values(defaults)
245
246 def error(self, msg):
247 self.print_usage(sys.stderr)
248 self.exit(UNKNOWN_ERROR, "%s\n" % msg)
249
250
251 def invalid_config_error_message(action, key, val):
252 """Returns a better error message when invalid configuration option
253 is provided."""
254 if action in ('store_true', 'store_false'):
255 return ("{0} is not a valid value for {1} option, "
256 "please specify a boolean value like yes/no, "
257 "true/false or 1/0 instead.").format(val, key)
258
259 return ("{0} is not a valid value for {1} option, "
260 "please specify a numerical value like 1/0 "
261 "instead.").format(val, key)