comparison env/lib/python3.9/site-packages/pip/_internal/cli/parser.py @ 0:4f3585e2f14b draft default tip

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