Skip to content

Commit c5a5e18

Browse files
committed
Create _ExtendArgument
1 parent 85a4804 commit c5a5e18

File tree

5 files changed

+107
-3
lines changed

5 files changed

+107
-3
lines changed

pylint/config/argument.py

+47-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@
2727

2828
from pylint import interfaces
2929
from pylint import utils as pylint_utils
30-
from pylint.config.callback_actions import _CallbackAction
30+
from pylint.config.callback_actions import _CallbackAction, _ExtendAction
3131
from pylint.config.deprecation_actions import _NewNamesAction, _OldNamesAction
32+
from pylint.constants import PY38_PLUS
3233

3334
if sys.version_info >= (3, 8):
3435
from typing import Literal
@@ -294,7 +295,7 @@ def __init__(
294295
self,
295296
*,
296297
flags: List[str],
297-
action: Type[argparse._StoreAction],
298+
action: Type[argparse.Action],
298299
default: _ArgumentTypes,
299300
arg_type: str,
300301
choices: Optional[List[str]],
@@ -330,6 +331,50 @@ def __init__(
330331
"""
331332

332333

334+
class _ExtendArgument(_DeprecationArgument):
335+
"""Class for extend arguments to be parsed by an argparse.ArgumentsParser.
336+
337+
This is based on the parameters passed to argparse.ArgumentsParser.add_message.
338+
See:
339+
https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
340+
"""
341+
342+
def __init__(
343+
self,
344+
*,
345+
flags: List[str],
346+
action: Literal["extend"],
347+
default: _ArgumentTypes,
348+
arg_type: str,
349+
metavar: str,
350+
arg_help: str,
351+
hide_help: bool,
352+
section: Optional[str],
353+
choices: Optional[List[str]],
354+
dest: Optional[str],
355+
) -> None:
356+
# The extend action is included in the stdlib from 3.8+
357+
if PY38_PLUS:
358+
action_class = argparse._ExtendAction # type: ignore[attr-defined]
359+
else:
360+
action_class = _ExtendAction
361+
362+
self.dest = dest
363+
"""The destination of the argument."""
364+
365+
super().__init__(
366+
flags=flags,
367+
action=action_class,
368+
default=default,
369+
arg_type=arg_type,
370+
choices=choices,
371+
arg_help=arg_help,
372+
metavar=metavar,
373+
hide_help=hide_help,
374+
section=section,
375+
)
376+
377+
333378
class _StoreOldNamesArgument(_DeprecationArgument):
334379
"""Store arguments while also handling old names.
335380

pylint/config/arguments_manager.py

+12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from pylint.config.argument import (
2424
_Argument,
2525
_CallableArgument,
26+
_ExtendArgument,
2627
_StoreArgument,
2728
_StoreNewNamesArgument,
2829
_StoreOldNamesArgument,
@@ -191,6 +192,17 @@ def _add_parser_option(
191192
action=argument.action,
192193
help=argument.help,
193194
)
195+
elif isinstance(argument, _ExtendArgument):
196+
section_group.add_argument(
197+
*argument.flags,
198+
action=argument.action,
199+
default=argument.default,
200+
type=argument.type, # type: ignore[arg-type] # incorrect typing in typeshed
201+
help=argument.help,
202+
metavar=argument.metavar,
203+
choices=argument.choices,
204+
dest=argument.dest,
205+
)
194206
else:
195207
raise UnrecognizedArgumentAction
196208

pylint/config/arguments_provider.py

+12
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,18 @@ def set_option(self, optname, value, action=None, optdict=None):
132132
setattr(self.config, optname, _list + (value,))
133133
else:
134134
_list.append(value)
135+
elif action == "extend":
136+
_list = getattr(self.config, optname, None)
137+
if _list is None:
138+
if isinstance(value, (list, tuple)):
139+
_list = value
140+
elif value is not None:
141+
_list = [value]
142+
setattr(self.config, optname, _list)
143+
elif isinstance(_list, tuple):
144+
setattr(self.config, optname, _list + (value,))
145+
else:
146+
_list.extend(value)
135147
elif (
136148
action == "callback"
137149
or (not isinstance(action, str))

pylint/config/callback_actions.py

+21
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,27 @@ def __call__(
5252
return None
5353

5454

55+
class _ExtendAction(argparse._AppendAction):
56+
"""Action that adds the value to a pre-existing list.
57+
58+
It is directly copied from the stdlib implementation which is only available
59+
on 3.8+.
60+
"""
61+
62+
def __call__(
63+
self,
64+
parser: argparse.ArgumentParser,
65+
namespace: argparse.Namespace,
66+
values: Union[str, Sequence[Any], None],
67+
option_string: Optional[str] = None,
68+
) -> None:
69+
assert isinstance(values, (tuple, list))
70+
current = getattr(namespace, self.dest, [])
71+
assert isinstance(current, list)
72+
current.extend(values)
73+
setattr(namespace, self.dest, current)
74+
75+
5576
class _AccessRunObjectAction(_CallbackAction):
5677
"""Action that has access to the Run object."""
5778

pylint/config/utils.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from pylint import extensions, utils
1414
from pylint.config.argument import (
1515
_CallableArgument,
16+
_ExtendArgument,
1617
_StoreArgument,
1718
_StoreNewNamesArgument,
1819
_StoreOldNamesArgument,
@@ -33,6 +34,7 @@ def _convert_option_to_argument(
3334
_CallableArgument,
3435
_StoreOldNamesArgument,
3536
_StoreNewNamesArgument,
37+
_ExtendArgument,
3638
]:
3739
"""Convert an optdict to an Argument class instance."""
3840
if "level" in optdict and "hide" not in optdict:
@@ -84,7 +86,19 @@ def _convert_option_to_argument(
8486
DeprecationWarning,
8587
)
8688
default = None
87-
89+
if action == "extend":
90+
return _ExtendArgument(
91+
flags=flags,
92+
action=action,
93+
default=default,
94+
arg_type=optdict["type"],
95+
choices=choices,
96+
arg_help=optdict.get("help", ""),
97+
metavar=optdict.get("metavar", ""),
98+
hide_help=optdict.get("hide", False),
99+
section=optdict.get("group", None),
100+
dest=optdict.get("dest", None),
101+
)
88102
if "kwargs" in optdict:
89103
if "old_names" in optdict["kwargs"]:
90104
return _StoreOldNamesArgument(

0 commit comments

Comments
 (0)