From 02d0d22f81a99079f548f84343a248236b59809a Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 12 Sep 2024 18:56:14 +0800 Subject: [PATCH 1/6] Fixed argparser parsing optional arguments with same prefix --- Lib/argparse.py | 19 +++++++++++++++++++ ...-09-12-09-52-15.gh-issue-109990.belDmD.rst | 2 ++ 2 files changed, 21 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst diff --git a/Lib/argparse.py b/Lib/argparse.py index 100ef9f55cd2f7..a95125d71003ba 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -89,6 +89,7 @@ import re as _re import sys as _sys +from itertools import chain from gettext import gettext as _, ngettext SUPPRESS = '==SUPPRESS==' @@ -2234,6 +2235,12 @@ def _match_arguments_partial(self, actions, arg_strings_pattern): # return the list of arg string counts return result + def _extract_optional_arguments(self): + return [action.choices.values() + for action in self._subparsers._actions + if isinstance(action, _SubParsersAction) + if hasattr(action, 'choices')] + def _parse_optional(self, arg_string): # if it's an empty string, it was meant to be a positional if not arg_string: @@ -2262,6 +2269,18 @@ def _parse_optional(self, arg_string): # and all actions in the parser for possible interpretations option_tuples = self._get_option_tuples(arg_string) + # if the parameter string exists, + # similar matching exception isn't thrown + if hasattr(self._subparsers, '_actions'): + sub_dict_values = self._extract_optional_arguments()[0] + subparams = list(chain.from_iterable( + action.option_strings + for subvalue in sub_dict_values + for action in subvalue._actions + )) + if arg_string in subparams: + return None + # if multiple actions match, the option string was ambiguous if len(option_tuples) > 1: options = ', '.join([option_string diff --git a/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst b/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst new file mode 100644 index 00000000000000..9d5c4e0765806c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst @@ -0,0 +1,2 @@ +Add checks for optional arguments to the :meth:`ArgumentParser._parse_optional` +of the :class:`ArgumentParser` in the :mod:`argparser`. From 5e6510c1c6e211f4838e6644365209f573e53898 Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 12 Sep 2024 19:47:49 +0800 Subject: [PATCH 2/6] Simplified expression --- Lib/argparse.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index a95125d71003ba..442997a296d8fb 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -89,7 +89,6 @@ import re as _re import sys as _sys -from itertools import chain from gettext import gettext as _, ngettext SUPPRESS = '==SUPPRESS==' @@ -2235,11 +2234,21 @@ def _match_arguments_partial(self, actions, arg_strings_pattern): # return the list of arg string counts return result - def _extract_optional_arguments(self): + def _extract_choices_value(self): return [action.choices.values() for action in self._subparsers._actions if isinstance(action, _SubParsersAction) - if hasattr(action, 'choices')] + and hasattr(action, 'choices')] + + def _extract_actions(self, choices_value): + return [action + for subvalue in choices_value + for action in subvalue._actions] + + def _extract_optional_arguments(self, actions): + return [option_string + for action in actions + for option_string in action.option_strings] def _parse_optional(self, arg_string): # if it's an empty string, it was meant to be a positional @@ -2272,12 +2281,9 @@ def _parse_optional(self, arg_string): # if the parameter string exists, # similar matching exception isn't thrown if hasattr(self._subparsers, '_actions'): - sub_dict_values = self._extract_optional_arguments()[0] - subparams = list(chain.from_iterable( - action.option_strings - for subvalue in sub_dict_values - for action in subvalue._actions - )) + choices_value = self._extract_choices_value()[0] + actions = self._extract_actions(choices_value) + subparams = self._extract_optional_arguments(actions) if arg_string in subparams: return None From 22c3a52b600dd3439f2e4e417f6b2eb921715aaa Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 12 Sep 2024 19:53:44 +0800 Subject: [PATCH 3/6] Delete nospace --- Lib/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index 442997a296d8fb..c7e8de23976fc8 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2244,7 +2244,7 @@ def _extract_actions(self, choices_value): return [action for subvalue in choices_value for action in subvalue._actions] - + def _extract_optional_arguments(self, actions): return [option_string for action in actions From 1076c707e3c611fb17703e9af486e1fd750473c7 Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 12 Sep 2024 21:28:08 +0800 Subject: [PATCH 4/6] Change NEWS --- .../Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst b/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst index 9d5c4e0765806c..05df70e84a14f3 100644 --- a/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst +++ b/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst @@ -1,2 +1,2 @@ -Add checks for optional arguments to the :meth:`ArgumentParser._parse_optional` -of the :class:`ArgumentParser` in the :mod:`argparser`. +Enhance handling of optional arguments in the :meth:`ArgumentParser.add_argument` +of the :class:`argparse.ArgumentParser` in the :mod:`argparse`. From 1cbf896ed7c9486264870c788e49cdffcebd190e Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 12 Sep 2024 21:35:00 +0800 Subject: [PATCH 5/6] Add argparse suffix --- .../next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst b/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst index 05df70e84a14f3..71ab661b238c0f 100644 --- a/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst +++ b/Misc/NEWS.d/next/Library/2024-09-12-09-52-15.gh-issue-109990.belDmD.rst @@ -1,2 +1,2 @@ -Enhance handling of optional arguments in the :meth:`ArgumentParser.add_argument` +Enhance handling of optional arguments in the :meth:`argparse.ArgumentParser.add_argument` of the :class:`argparse.ArgumentParser` in the :mod:`argparse`. From 0f180eca9c24804acc63520fe2a4ceeae483095c Mon Sep 17 00:00:00 2001 From: ruang Date: Fri, 13 Sep 2024 22:02:54 +0800 Subject: [PATCH 6/6] Optimized sub optional parameter extraction --- Lib/argparse.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index c7e8de23976fc8..e9d2ea34c0b122 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2234,21 +2234,17 @@ def _match_arguments_partial(self, actions, arg_strings_pattern): # return the list of arg string counts return result - def _extract_choices_value(self): - return [action.choices.values() - for action in self._subparsers._actions - if isinstance(action, _SubParsersAction) - and hasattr(action, 'choices')] + def _extract_optional_arguments(self): + optionals = [] - def _extract_actions(self, choices_value): - return [action - for subvalue in choices_value - for action in subvalue._actions] - - def _extract_optional_arguments(self, actions): - return [option_string - for action in actions - for option_string in action.option_strings] + for action in self._actions: + if not isinstance(action, _SubParsersAction): + continue + if hasattr(action, 'choices'): + for choices in action.choices.values(): + for action in choices._actions: + optionals.extend(action.option_strings) + return optionals def _parse_optional(self, arg_string): # if it's an empty string, it was meant to be a positional @@ -2280,12 +2276,9 @@ def _parse_optional(self, arg_string): # if the parameter string exists, # similar matching exception isn't thrown - if hasattr(self._subparsers, '_actions'): - choices_value = self._extract_choices_value()[0] - actions = self._extract_actions(choices_value) - subparams = self._extract_optional_arguments(actions) - if arg_string in subparams: - return None + optionals = self._extract_optional_arguments() + if arg_string in optionals: + return None # if multiple actions match, the option string was ambiguous if len(option_tuples) > 1: