Skip to content

Commit 2cc48ba

Browse files
committed
gh-96859: [argparse] Avoid copying lists on every append and extend action
Currently the append, append_const, and extend actions make a copy of the target list every time the action is called. It becomes rather slow with very long option lists.
1 parent c00964e commit 2cc48ba

File tree

1 file changed

+29
-16
lines changed

1 file changed

+29
-16
lines changed

Lib/argparse.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def _get_args(self):
138138
def _copy_items(items):
139139
if items is None:
140140
return []
141-
# The copy module is used only in the 'append' and 'append_const'
141+
# The copy module is used only in the 'append', 'append_const', and 'extend'
142142
# actions, and it is needed only when the default value isn't a list.
143143
# Delay its import for speeding up the common case.
144144
if type(items) is list:
@@ -835,7 +835,7 @@ def _get_kwargs(self):
835835
def format_usage(self):
836836
return self.option_strings[0]
837837

838-
def __call__(self, parser, namespace, values, option_string=None):
838+
def __call__(self, parser, cache, namespace, values, option_string=None):
839839
raise NotImplementedError(_('.__call__() not defined'))
840840

841841

@@ -866,7 +866,7 @@ def __init__(self,
866866
deprecated=deprecated)
867867

868868

869-
def __call__(self, parser, namespace, values, option_string=None):
869+
def __call__(self, parser, cache, namespace, values, option_string=None):
870870
if option_string in self.option_strings:
871871
setattr(namespace, self.dest, not option_string.startswith('--no-'))
872872

@@ -907,7 +907,7 @@ def __init__(self,
907907
metavar=metavar,
908908
deprecated=deprecated)
909909

910-
def __call__(self, parser, namespace, values, option_string=None):
910+
def __call__(self, parser, cache, namespace, values, option_string=None):
911911
setattr(namespace, self.dest, values)
912912

913913

@@ -932,7 +932,7 @@ def __init__(self,
932932
help=help,
933933
deprecated=deprecated)
934934

935-
def __call__(self, parser, namespace, values, option_string=None):
935+
def __call__(self, parser, cache, namespace, values, option_string=None):
936936
setattr(namespace, self.dest, self.const)
937937

938938

@@ -1007,9 +1007,13 @@ def __init__(self,
10071007
metavar=metavar,
10081008
deprecated=deprecated)
10091009

1010-
def __call__(self, parser, namespace, values, option_string=None):
1010+
def __call__(self, parser, cache, namespace, values, option_string=None):
10111011
items = getattr(namespace, self.dest, None)
1012-
items = _copy_items(items)
1012+
1013+
if self.dest not in cache:
1014+
cache.add(self.dest)
1015+
items = _copy_items(items)
1016+
10131017
items.append(values)
10141018
setattr(namespace, self.dest, items)
10151019

@@ -1036,9 +1040,13 @@ def __init__(self,
10361040
metavar=metavar,
10371041
deprecated=deprecated)
10381042

1039-
def __call__(self, parser, namespace, values, option_string=None):
1043+
def __call__(self, parser, cache, namespace, values, option_string=None):
10401044
items = getattr(namespace, self.dest, None)
1041-
items = _copy_items(items)
1045+
1046+
if self.dest not in cache:
1047+
cache.add(self.dest)
1048+
items = _copy_items(items)
1049+
10421050
items.append(self.const)
10431051
setattr(namespace, self.dest, items)
10441052

@@ -1061,7 +1069,7 @@ def __init__(self,
10611069
help=help,
10621070
deprecated=deprecated)
10631071

1064-
def __call__(self, parser, namespace, values, option_string=None):
1072+
def __call__(self, parser, cache, namespace, values, option_string=None):
10651073
count = getattr(namespace, self.dest, None)
10661074
if count is None:
10671075
count = 0
@@ -1084,7 +1092,7 @@ def __init__(self,
10841092
help=help,
10851093
deprecated=deprecated)
10861094

1087-
def __call__(self, parser, namespace, values, option_string=None):
1095+
def __call__(self, parser, cache, namespace, values, option_string=None):
10881096
parser.print_help()
10891097
parser.exit()
10901098

@@ -1108,7 +1116,7 @@ def __init__(self,
11081116
help=help)
11091117
self.version = version
11101118

1111-
def __call__(self, parser, namespace, values, option_string=None):
1119+
def __call__(self, parser, cache, namespace, values, option_string=None):
11121120
version = self.version
11131121
if version is None:
11141122
version = parser.version
@@ -1191,7 +1199,7 @@ def add_parser(self, name, *, deprecated=False, **kwargs):
11911199
def _get_subactions(self):
11921200
return self._choices_actions
11931201

1194-
def __call__(self, parser, namespace, values, option_string=None):
1202+
def __call__(self, parser, cache, namespace, values, option_string=None):
11951203
parser_name = values[0]
11961204
arg_strings = values[1:]
11971205

@@ -1228,9 +1236,13 @@ def __call__(self, parser, namespace, values, option_string=None):
12281236
getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)
12291237

12301238
class _ExtendAction(_AppendAction):
1231-
def __call__(self, parser, namespace, values, option_string=None):
1239+
def __call__(self, parser, cache, namespace, values, option_string=None):
12321240
items = getattr(namespace, self.dest, None)
1233-
items = _copy_items(items)
1241+
1242+
if self.dest not in cache:
1243+
cache.add(self.dest)
1244+
items = _copy_items(items)
1245+
12341246
items.extend(values)
12351247
setattr(namespace, self.dest, items)
12361248

@@ -1941,6 +1953,7 @@ def _parse_known_args(self, arg_strings, namespace):
19411953
# converts arg strings to the appropriate and then takes the action
19421954
seen_actions = set()
19431955
seen_non_default_actions = set()
1956+
cache = set()
19441957
warned = set()
19451958

19461959
def take_action(action, argument_strings, option_string=None):
@@ -1960,7 +1973,7 @@ def take_action(action, argument_strings, option_string=None):
19601973
# take the action if we didn't receive a SUPPRESS value
19611974
# (e.g. from a default)
19621975
if argument_values is not SUPPRESS:
1963-
action(self, namespace, argument_values, option_string)
1976+
action(self, cache, namespace, argument_values, option_string)
19641977

19651978
# function to convert arg_strings into an optional action
19661979
def consume_optional(start_index):

0 commit comments

Comments
 (0)