From b19b5d81616d3ce2be84def82b1b79ef140fc72a Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 16 Sep 2024 15:12:14 -0700 Subject: [PATCH 01/12] update logic to remove -- in cases where used to delineate pos args --- Lib/argparse.py | 18 ++++++++++++------ Lib/test/test_argparse.py | 10 ++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index 100ef9f55cd2f7..a757eac17fd32d 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2061,7 +2061,7 @@ def consume_optional(start_index): # function to convert arg_strings into positional actions def consume_positionals(start_index): - # match as many Positionals as possible + # match as many positionals as possible match_partial = self._match_arguments_partial selected_pattern = arg_strings_pattern[start_index:] arg_counts = match_partial(positionals, selected_pattern) @@ -2470,12 +2470,18 @@ def parse_known_intermixed_args(self, args=None, namespace=None): # Value conversion methods # ======================== def _get_values(self, action, arg_strings): - # for everything but PARSER, REMAINDER args, strip out first '--' if not action.option_strings and action.nargs not in [PARSER, REMAINDER]: - try: - arg_strings.remove('--') - except ValueError: - pass + if action.nargs == ZERO_OR_MORE and action.type is None: + # if nargs='*' starts with '--', then we should treat any + # subsequent arguments as positional arguments and we should not + # strip out the first '--' + if arg_strings and arg_strings[0] == '--': + arg_strings = arg_strings[1:] + else: + try: + arg_strings.remove('--') + except ValueError: + pass # optional argument produces a default when not present if not arg_strings and action.nargs == OPTIONAL: diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index fd111be18aed6e..6bd370234a6f67 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -5715,6 +5715,16 @@ def test_double_dash(self): self.assertEqual(NS(foo=[], bar=[]), args) args = parser.parse_args(['--foo', 'a', 'b', '--', 'c', 'd']) self.assertEqual(NS(foo=['a', 'b'], bar=['c', 'd']), args) + args = parser.parse_args(['--', '--foo', 'a', '--', 'b']) + self.assertEqual(NS(foo=None, bar=['--foo', 'a', '--', 'b']), args) + + parser2 = argparse.ArgumentParser() + parser2.add_argument('foo') + parser2.add_argument('bar', nargs='*') + + args = parser2.parse_args(['foo', '--', '--bar', '--', 'com']) + self.assertEqual(NS(foo='foo', bar=['--bar', '--', 'com']), args) + # =========================== From 8a4ebc1f367e65aad6dc0249a8c622b8764da0af Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 16 Sep 2024 15:13:00 -0700 Subject: [PATCH 02/12] update comment --- Lib/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index a757eac17fd32d..ae8a2818e7f411 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2474,7 +2474,7 @@ def _get_values(self, action, arg_strings): if action.nargs == ZERO_OR_MORE and action.type is None: # if nargs='*' starts with '--', then we should treat any # subsequent arguments as positional arguments and we should not - # strip out the first '--' + # strip out the first '--' unless it violates a specified type if arg_strings and arg_strings[0] == '--': arg_strings = arg_strings[1:] else: From a25e0c8d310ba42da137fa4024bbb2675a13ebf5 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 16 Sep 2024 15:13:55 -0700 Subject: [PATCH 03/12] undo comment update --- Lib/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index ae8a2818e7f411..1751016400a004 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2061,7 +2061,7 @@ def consume_optional(start_index): # function to convert arg_strings into positional actions def consume_positionals(start_index): - # match as many positionals as possible + # match as many Positionals as possible match_partial = self._match_arguments_partial selected_pattern = arg_strings_pattern[start_index:] arg_counts = match_partial(positionals, selected_pattern) From 4adad77fdf628ef098b5d23a76ab552ad2e877c1 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 16 Sep 2024 15:17:15 -0700 Subject: [PATCH 04/12] update tests --- Lib/test/test_argparse.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 6bd370234a6f67..4a181e6ebb15bc 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -5715,8 +5715,6 @@ def test_double_dash(self): self.assertEqual(NS(foo=[], bar=[]), args) args = parser.parse_args(['--foo', 'a', 'b', '--', 'c', 'd']) self.assertEqual(NS(foo=['a', 'b'], bar=['c', 'd']), args) - args = parser.parse_args(['--', '--foo', 'a', '--', 'b']) - self.assertEqual(NS(foo=None, bar=['--foo', 'a', '--', 'b']), args) parser2 = argparse.ArgumentParser() parser2.add_argument('foo') From 7f7057c3c11b7cbf1bdfc3730e10eb3844478359 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 16 Sep 2024 15:18:02 -0700 Subject: [PATCH 05/12] remove newline --- Lib/test/test_argparse.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 4a181e6ebb15bc..ea7d88ad4d26d5 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -5723,8 +5723,6 @@ def test_double_dash(self): args = parser2.parse_args(['foo', '--', '--bar', '--', 'com']) self.assertEqual(NS(foo='foo', bar=['--bar', '--', 'com']), args) - - # =========================== # parse_intermixed_args tests # =========================== From ad6b26224b468b9264ab943f50644647ecdc09d0 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 16 Sep 2024 15:23:51 -0700 Subject: [PATCH 06/12] appease linter --- Lib/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index 1751016400a004..892f5c0bf8e86b 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2476,7 +2476,7 @@ def _get_values(self, action, arg_strings): # subsequent arguments as positional arguments and we should not # strip out the first '--' unless it violates a specified type if arg_strings and arg_strings[0] == '--': - arg_strings = arg_strings[1:] + arg_strings = arg_strings[1:] else: try: arg_strings.remove('--') From 3f502d771011f9cb016e8f3a18e5820e6912ce69 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 22:26:45 +0000 Subject: [PATCH 07/12] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst diff --git a/Misc/NEWS.d/next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst b/Misc/NEWS.d/next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst new file mode 100644 index 00000000000000..f1f2a1ffad4436 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst @@ -0,0 +1 @@ +Fix a bug where argparse would remove -- in cases where it delineates positional arguments From cfb4b04ba74997b464cd1def95198d89b731997a Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 16 Sep 2024 15:44:21 -0700 Subject: [PATCH 08/12] update comment --- Lib/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index 892f5c0bf8e86b..cd28946108f882 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2474,7 +2474,7 @@ def _get_values(self, action, arg_strings): if action.nargs == ZERO_OR_MORE and action.type is None: # if nargs='*' starts with '--', then we should treat any # subsequent arguments as positional arguments and we should not - # strip out the first '--' unless it violates a specified type + # strip out the first '--' unless it violates the specified nargs type if arg_strings and arg_strings[0] == '--': arg_strings = arg_strings[1:] else: From 1fd18856a1eb82bb90b99d7482ce8c27ca1dd4a5 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Mon, 16 Sep 2024 15:49:29 -0700 Subject: [PATCH 09/12] update news entry --- .../next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst b/Misc/NEWS.d/next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst index f1f2a1ffad4436..1beeaaa7211e99 100644 --- a/Misc/NEWS.d/next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst +++ b/Misc/NEWS.d/next/Library/2024-09-16-22-26-43.gh-issue-95468.L1bRag.rst @@ -1 +1 @@ -Fix a bug where argparse would remove -- in cases where it delineates positional arguments +Fix a bug where :mod:`argparse` would remove "--" in cases where it delineates positional arguments in nargs. From 1c4c03aae3e2c96ec33b08c5afbd48efe38778be Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 18 Sep 2024 12:48:21 -0700 Subject: [PATCH 10/12] add tests --- Lib/argparse.py | 5 +---- Lib/test/test_argparse.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index cd28946108f882..bfd366265111af 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2471,10 +2471,7 @@ def parse_known_intermixed_args(self, args=None, namespace=None): # ======================== def _get_values(self, action, arg_strings): if not action.option_strings and action.nargs not in [PARSER, REMAINDER]: - if action.nargs == ZERO_OR_MORE and action.type is None: - # if nargs='*' starts with '--', then we should treat any - # subsequent arguments as positional arguments and we should not - # strip out the first '--' unless it violates the specified nargs type + if action.nargs in [ZERO_OR_MORE, ONE_OR_MORE] and action.type is None: if arg_strings and arg_strings[0] == '--': arg_strings = arg_strings[1:] else: diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index ea7d88ad4d26d5..6043a5ee5202a6 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -5723,6 +5723,22 @@ def test_double_dash(self): args = parser2.parse_args(['foo', '--', '--bar', '--', 'com']) self.assertEqual(NS(foo='foo', bar=['--bar', '--', 'com']), args) + parser3 = argparse.ArgumentParser() + parser3.add_argument('foo') + parser3.add_argument('bar', nargs='+') + + args = parser3.parse_args(['foo', '--', '--bar', '--', 'com']) + self.assertEqual(NS(foo='foo', bar=['--bar', '--', 'com']), args) + + parser4 = argparse.ArgumentParser() + parser4.add_argument('foo') + parser4.add_argument('bar', nargs=3) + + args = parser4.parse_args(['foo', '--', '--bar', '--', 'com']) + self.assertEqual(NS(foo='foo', bar=['--', '--bar', '--']), args) + + + # =========================== # parse_intermixed_args tests # =========================== From 76d74c3edebd18131d8601003bd8b557bea4603c Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 18 Sep 2024 13:04:23 -0700 Subject: [PATCH 11/12] update test cases --- Lib/argparse.py | 4 +++- Lib/test/test_argparse.py | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index bfd366265111af..6eb081d1df6c63 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2471,7 +2471,9 @@ def parse_known_intermixed_args(self, args=None, namespace=None): # ======================== def _get_values(self, action, arg_strings): if not action.option_strings and action.nargs not in [PARSER, REMAINDER]: - if action.nargs in [ZERO_OR_MORE, ONE_OR_MORE] and action.type is None: + is_multiple_args = (action.nargs in [ZERO_OR_MORE, ONE_OR_MORE] or + (isinstance(action.nargs, int) and action.nargs > 1)) + if is_multiple_args and action.type is None: if arg_strings and arg_strings[0] == '--': arg_strings = arg_strings[1:] else: diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 6043a5ee5202a6..7da0da8ec5a32e 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -5735,8 +5735,7 @@ def test_double_dash(self): parser4.add_argument('bar', nargs=3) args = parser4.parse_args(['foo', '--', '--bar', '--', 'com']) - self.assertEqual(NS(foo='foo', bar=['--', '--bar', '--']), args) - + self.assertEqual(NS(foo='foo', bar=['--bar', '--', 'com']), args) # =========================== From 74a6a586e82cda0810a8cbae783c227ee79220ee Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 18 Sep 2024 13:13:13 -0700 Subject: [PATCH 12/12] formatting --- Lib/argparse.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index 6eb081d1df6c63..99806d848443a7 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2471,9 +2471,9 @@ def parse_known_intermixed_args(self, args=None, namespace=None): # ======================== def _get_values(self, action, arg_strings): if not action.option_strings and action.nargs not in [PARSER, REMAINDER]: - is_multiple_args = (action.nargs in [ZERO_OR_MORE, ONE_OR_MORE] or - (isinstance(action.nargs, int) and action.nargs > 1)) - if is_multiple_args and action.type is None: + if ((action.nargs in [ZERO_OR_MORE, ONE_OR_MORE] or + (isinstance(action.nargs, int) and action.nargs > 1)) and + action.type is None): if arg_strings and arg_strings[0] == '--': arg_strings = arg_strings[1:] else: