Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
keewenaw
GitHub Repository: keewenaw/ethereum-wallet-cracker
Path: blob/main/test/lib/python3.9/site-packages/pip/_internal/cli/parser.py
4804 views
1
"""Base option parser setup"""
2
3
import logging
4
import optparse
5
import shutil
6
import sys
7
import textwrap
8
from contextlib import suppress
9
from typing import Any, Dict, Generator, List, Tuple
10
11
from pip._internal.cli.status_codes import UNKNOWN_ERROR
12
from pip._internal.configuration import Configuration, ConfigurationError
13
from pip._internal.utils.misc import redact_auth_from_url, strtobool
14
15
logger = logging.getLogger(__name__)
16
17
18
class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
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
31
def _format_option_strings(
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
43
if option._short_opts:
44
opts.append(option._short_opts[0])
45
if option._long_opts:
46
opts.append(option._long_opts[0])
47
if len(opts) > 1:
48
opts.insert(1, optsep)
49
50
if option.takes_value():
51
assert option.dest is not None
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: 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
100
class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
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.parser is not None:
112
assert isinstance(self.parser, ConfigOptionParser)
113
self.parser._update_defaults(self.parser.defaults)
114
assert option.dest is not None
115
default_values = self.parser.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
132
class CustomOptionParser(optparse.OptionParser):
133
def insert_option_group(
134
self, idx: int, *args: Any, **kwargs: Any
135
) -> optparse.OptionGroup:
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:
149
res.extend(i.option_list)
150
151
return res
152
153
154
class ConfigOptionParser(CustomOptionParser):
155
"""Custom option parser which updates its defaults by checking the
156
configuration files and environmental variables"""
157
158
def __init__(
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
178
def _get_ordered_configuration_items(
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:
191
logger.debug(
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.
212
self.values = optparse.Values(self.defaults)
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
253
late_eval.add(option.dest)
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.
275
return optparse.Values(self.defaults)
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")
295
296