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