Mercurial > repos > shellac > sam_consensus_v3
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") |
