]> crepu.dev Git - config.git/blob - djavu-asus/emacs/elpy/rpc-venv/lib/python3.11/site-packages/flake8/options/manager.py
4fd26b2936cdbb9af20fa6b82e0a4c888c5be14a
[config.git] / djavu-asus / emacs / elpy / rpc-venv / lib / python3.11 / site-packages / flake8 / options / manager.py
1 """Option handling and Option management logic."""
2 from __future__ import annotations
3
4 import argparse
5 import enum
6 import functools
7 import logging
8 from typing import Any
9 from typing import Callable
10 from typing import Sequence
11
12 from flake8 import utils
13 from flake8.plugins.finder import Plugins
14
15 LOG = logging.getLogger(__name__)
16
17 # represent a singleton of "not passed arguments".
18 # an enum is chosen to trick mypy
19 _ARG = enum.Enum("_ARG", "NO")
20
21
22 def _flake8_normalize(
23 value: str,
24 *args: str,
25 comma_separated_list: bool = False,
26 normalize_paths: bool = False,
27 ) -> str | list[str]:
28 ret: str | list[str] = value
29 if comma_separated_list and isinstance(ret, str):
30 ret = utils.parse_comma_separated_list(value)
31
32 if normalize_paths:
33 if isinstance(ret, str):
34 ret = utils.normalize_path(ret, *args)
35 else:
36 ret = utils.normalize_paths(ret, *args)
37
38 return ret
39
40
41 class Option:
42 """Our wrapper around an argparse argument parsers to add features."""
43
44 def __init__(
45 self,
46 short_option_name: str | _ARG = _ARG.NO,
47 long_option_name: str | _ARG = _ARG.NO,
48 # Options below are taken from argparse.ArgumentParser.add_argument
49 action: str | type[argparse.Action] | _ARG = _ARG.NO,
50 default: Any | _ARG = _ARG.NO,
51 type: Callable[..., Any] | _ARG = _ARG.NO,
52 dest: str | _ARG = _ARG.NO,
53 nargs: int | str | _ARG = _ARG.NO,
54 const: Any | _ARG = _ARG.NO,
55 choices: Sequence[Any] | _ARG = _ARG.NO,
56 help: str | _ARG = _ARG.NO,
57 metavar: str | _ARG = _ARG.NO,
58 required: bool | _ARG = _ARG.NO,
59 # Options below here are specific to Flake8
60 parse_from_config: bool = False,
61 comma_separated_list: bool = False,
62 normalize_paths: bool = False,
63 ) -> None:
64 """Initialize an Option instance.
65
66 The following are all passed directly through to argparse.
67
68 :param short_option_name:
69 The short name of the option (e.g., ``-x``). This will be the
70 first argument passed to ``ArgumentParser.add_argument``
71 :param long_option_name:
72 The long name of the option (e.g., ``--xtra-long-option``). This
73 will be the second argument passed to
74 ``ArgumentParser.add_argument``
75 :param default:
76 Default value of the option.
77 :param dest:
78 Attribute name to store parsed option value as.
79 :param nargs:
80 Number of arguments to parse for this option.
81 :param const:
82 Constant value to store on a common destination. Usually used in
83 conjunction with ``action="store_const"``.
84 :param choices:
85 Possible values for the option.
86 :param help:
87 Help text displayed in the usage information.
88 :param metavar:
89 Name to use instead of the long option name for help text.
90 :param required:
91 Whether this option is required or not.
92
93 The following options may be passed directly through to :mod:`argparse`
94 but may need some massaging.
95
96 :param type:
97 A callable to normalize the type (as is the case in
98 :mod:`argparse`).
99 :param action:
100 Any action allowed by :mod:`argparse`.
101
102 The following parameters are for Flake8's option handling alone.
103
104 :param parse_from_config:
105 Whether or not this option should be parsed out of config files.
106 :param comma_separated_list:
107 Whether the option is a comma separated list when parsing from a
108 config file.
109 :param normalize_paths:
110 Whether the option is expecting a path or list of paths and should
111 attempt to normalize the paths to absolute paths.
112 """
113 if (
114 long_option_name is _ARG.NO
115 and short_option_name is not _ARG.NO
116 and short_option_name.startswith("--")
117 ):
118 short_option_name, long_option_name = _ARG.NO, short_option_name
119
120 # flake8 special type normalization
121 if comma_separated_list or normalize_paths:
122 type = functools.partial(
123 _flake8_normalize,
124 comma_separated_list=comma_separated_list,
125 normalize_paths=normalize_paths,
126 )
127
128 self.short_option_name = short_option_name
129 self.long_option_name = long_option_name
130 self.option_args = [
131 x
132 for x in (short_option_name, long_option_name)
133 if x is not _ARG.NO
134 ]
135 self.action = action
136 self.default = default
137 self.type = type
138 self.dest = dest
139 self.nargs = nargs
140 self.const = const
141 self.choices = choices
142 self.help = help
143 self.metavar = metavar
144 self.required = required
145 self.option_kwargs: dict[str, Any | _ARG] = {
146 "action": self.action,
147 "default": self.default,
148 "type": self.type,
149 "dest": self.dest,
150 "nargs": self.nargs,
151 "const": self.const,
152 "choices": self.choices,
153 "help": self.help,
154 "metavar": self.metavar,
155 "required": self.required,
156 }
157
158 # Set our custom attributes
159 self.parse_from_config = parse_from_config
160 self.comma_separated_list = comma_separated_list
161 self.normalize_paths = normalize_paths
162
163 self.config_name: str | None = None
164 if parse_from_config:
165 if long_option_name is _ARG.NO:
166 raise ValueError(
167 "When specifying parse_from_config=True, "
168 "a long_option_name must also be specified."
169 )
170 self.config_name = long_option_name[2:].replace("-", "_")
171
172 self._opt = None
173
174 @property
175 def filtered_option_kwargs(self) -> dict[str, Any]:
176 """Return any actually-specified arguments."""
177 return {
178 k: v for k, v in self.option_kwargs.items() if v is not _ARG.NO
179 }
180
181 def __repr__(self) -> str: # noqa: D105
182 parts = []
183 for arg in self.option_args:
184 parts.append(arg)
185 for k, v in self.filtered_option_kwargs.items():
186 parts.append(f"{k}={v!r}")
187 return f"Option({', '.join(parts)})"
188
189 def normalize(self, value: Any, *normalize_args: str) -> Any:
190 """Normalize the value based on the option configuration."""
191 if self.comma_separated_list and isinstance(value, str):
192 value = utils.parse_comma_separated_list(value)
193
194 if self.normalize_paths:
195 if isinstance(value, list):
196 value = utils.normalize_paths(value, *normalize_args)
197 else:
198 value = utils.normalize_path(value, *normalize_args)
199
200 return value
201
202 def to_argparse(self) -> tuple[list[str], dict[str, Any]]:
203 """Convert a Flake8 Option to argparse ``add_argument`` arguments."""
204 return self.option_args, self.filtered_option_kwargs
205
206
207 class OptionManager:
208 """Manage Options and OptionParser while adding post-processing."""
209
210 def __init__(
211 self,
212 *,
213 version: str,
214 plugin_versions: str,
215 parents: list[argparse.ArgumentParser],
216 formatter_names: list[str],
217 ) -> None:
218 """Initialize an instance of an OptionManager."""
219 self.formatter_names = formatter_names
220 self.parser = argparse.ArgumentParser(
221 prog="flake8",
222 usage="%(prog)s [options] file file ...",
223 parents=parents,
224 epilog=f"Installed plugins: {plugin_versions}",
225 )
226 self.parser.add_argument(
227 "--version",
228 action="version",
229 version=(
230 f"{version} ({plugin_versions}) "
231 f"{utils.get_python_version()}"
232 ),
233 )
234 self.parser.add_argument("filenames", nargs="*", metavar="filename")
235
236 self.config_options_dict: dict[str, Option] = {}
237 self.options: list[Option] = []
238 self.extended_default_ignore: list[str] = []
239 self.extended_default_select: list[str] = []
240
241 self._current_group: argparse._ArgumentGroup | None = None
242
243 # TODO: maybe make this a free function to reduce api surface area
244 def register_plugins(self, plugins: Plugins) -> None:
245 """Register the plugin options (if needed)."""
246 groups: dict[str, argparse._ArgumentGroup] = {}
247
248 def _set_group(name: str) -> None:
249 try:
250 self._current_group = groups[name]
251 except KeyError:
252 group = self.parser.add_argument_group(name)
253 self._current_group = groups[name] = group
254
255 for loaded in plugins.all_plugins():
256 add_options = getattr(loaded.obj, "add_options", None)
257 if add_options:
258 _set_group(loaded.plugin.package)
259 add_options(self)
260
261 if loaded.plugin.entry_point.group == "flake8.extension":
262 self.extend_default_select([loaded.entry_name])
263
264 # isn't strictly necessary, but seems cleaner
265 self._current_group = None
266
267 def add_option(self, *args: Any, **kwargs: Any) -> None:
268 """Create and register a new option.
269
270 See parameters for :class:`~flake8.options.manager.Option` for
271 acceptable arguments to this method.
272
273 .. note::
274
275 ``short_option_name`` and ``long_option_name`` may be specified
276 positionally as they are with argparse normally.
277 """
278 option = Option(*args, **kwargs)
279 option_args, option_kwargs = option.to_argparse()
280 if self._current_group is not None:
281 self._current_group.add_argument(*option_args, **option_kwargs)
282 else:
283 self.parser.add_argument(*option_args, **option_kwargs)
284 self.options.append(option)
285 if option.parse_from_config:
286 name = option.config_name
287 assert name is not None
288 self.config_options_dict[name] = option
289 self.config_options_dict[name.replace("_", "-")] = option
290 LOG.debug('Registered option "%s".', option)
291
292 def extend_default_ignore(self, error_codes: Sequence[str]) -> None:
293 """Extend the default ignore list with the error codes provided.
294
295 :param error_codes:
296 List of strings that are the error/warning codes with which to
297 extend the default ignore list.
298 """
299 LOG.debug("Extending default ignore list with %r", error_codes)
300 self.extended_default_ignore.extend(error_codes)
301
302 def extend_default_select(self, error_codes: Sequence[str]) -> None:
303 """Extend the default select list with the error codes provided.
304
305 :param error_codes:
306 List of strings that are the error/warning codes with which
307 to extend the default select list.
308 """
309 LOG.debug("Extending default select list with %r", error_codes)
310 self.extended_default_select.extend(error_codes)
311
312 def parse_args(
313 self,
314 args: Sequence[str] | None = None,
315 values: argparse.Namespace | None = None,
316 ) -> argparse.Namespace:
317 """Proxy to calling the OptionParser's parse_args method."""
318 if values:
319 self.parser.set_defaults(**vars(values))
320 return self.parser.parse_args(args)