From 62877a35afc91a8ebd14f78e82da37fb829fa3a0 Mon Sep 17 00:00:00 2001 From: andrewnester Date: Wed, 15 Feb 2017 17:18:16 +0300 Subject: [PATCH 1/4] bpo-29553 - Fixed ArgumentParses format_usage for mutually exclusive groups --- Lib/argparse.py | 23 ++++++++++++----------- Lib/test/test_argparse.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index b69c5adfa072b9..7a546b1628d3e8 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -396,18 +396,19 @@ def _format_actions_usage(self, actions, groups): if actions[start:end] == group._group_actions: for action in group._group_actions: group_actions.add(action) - if not group.required: - if start in inserts: - inserts[start] += ' [' + if not isinstance(group._container, _MutuallyExclusiveGroup): + if not group.required: + if start in inserts: + inserts[start] += ' [' + else: + inserts[start] = '[' + inserts[end] = ']' else: - inserts[start] = '[' - inserts[end] = ']' - else: - if start in inserts: - inserts[start] += ' (' - else: - inserts[start] = '(' - inserts[end] = ')' + if start in inserts: + inserts[start] += ' (' + else: + inserts[start] = '(' + inserts[end] = ')' for i in range(start + 1, end): inserts[i] = '|' diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index a5c4a8ec97d7ad..5b19ea5f01dc21 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -2725,6 +2725,35 @@ def get_parser(self, required): -c c help ''' + +class TestMutuallyExclusiveNested(TestCase): + def test_format_usage(self): + parser = ErrorRaisingArgumentParser(prog='PROG') + group = parser.add_mutually_exclusive_group() + group.add_argument('-a') + group.add_argument('-b') + group2 = group.add_mutually_exclusive_group() + group2.add_argument('-c') + group2.add_argument('-d') + group3 = group2.add_mutually_exclusive_group() + group3.add_argument('-e') + group3.add_argument('-f') + self.assertEqual(parser.format_usage(), + 'usage: PROG [-h] [-a A | -b B | -c C | -d D | -e E | -f F]\n') + + def test_format_usage_required(self): + parser = ErrorRaisingArgumentParser(prog='PROG') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-a') + group.add_argument('-b') + group2 = group.add_mutually_exclusive_group(required=True) + group2.add_argument('-c') + group2.add_argument('-d') + group3 = group2.add_mutually_exclusive_group(required=True) + group3.add_argument('-e') + group3.add_argument('-f') + self.assertEqual(parser.format_usage(), + 'usage: PROG [-h] (-a A | -b B | -c C | -d D | -e E | -f F)\n') # ================================================= # Mutually exclusive group in parent parser tests # ================================================= From 87b9ec8fb6915c33b3b6eb81023078fe23c50005 Mon Sep 17 00:00:00 2001 From: andrewnester Date: Sun, 19 Feb 2017 20:24:32 +0300 Subject: [PATCH 2/4] Update tests for ArgumentParser --- Lib/test/test_argparse.py | 49 +++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 5b19ea5f01dc21..fbf3b1e0c8cd6a 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -2726,34 +2726,43 @@ def get_parser(self, required): ''' -class TestMutuallyExclusiveNested(TestCase): - def test_format_usage(self): +class TestMutuallyExclusiveNested(MEMixin, TestCase): + def get_parser(self, required): parser = ErrorRaisingArgumentParser(prog='PROG') - group = parser.add_mutually_exclusive_group() + group = parser.add_mutually_exclusive_group(required=required) group.add_argument('-a') group.add_argument('-b') - group2 = group.add_mutually_exclusive_group() + group2 = group.add_mutually_exclusive_group(required=required) group2.add_argument('-c') group2.add_argument('-d') - group3 = group2.add_mutually_exclusive_group() + group3 = group2.add_mutually_exclusive_group(required=required) group3.add_argument('-e') group3.add_argument('-f') - self.assertEqual(parser.format_usage(), - 'usage: PROG [-h] [-a A | -b B | -c C | -d D | -e E | -f F]\n') + return parser + + failures = [] + successes = [] + successes_when_not_required = [] + + usage_when_not_required = '''\ + usage: PROG [-h] [-a A | -b B | -c C | -d D | -e E | -f F] + ''' + usage_when_required = '''\ + usage: PROG [-h] (-a A | -b B | -c C | -d D | -e E | -f F) + ''' + + help = '''\ + + optional arguments: + -h, --help show this help message and exit + -a A + -b B + -c C + -d D + -e E + -f F + ''' - def test_format_usage_required(self): - parser = ErrorRaisingArgumentParser(prog='PROG') - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument('-a') - group.add_argument('-b') - group2 = group.add_mutually_exclusive_group(required=True) - group2.add_argument('-c') - group2.add_argument('-d') - group3 = group2.add_mutually_exclusive_group(required=True) - group3.add_argument('-e') - group3.add_argument('-f') - self.assertEqual(parser.format_usage(), - 'usage: PROG [-h] (-a A | -b B | -c C | -d D | -e E | -f F)\n') # ================================================= # Mutually exclusive group in parent parser tests # ================================================= From b96736996bec2c6e43957e62bbedc8f1baef0d83 Mon Sep 17 00:00:00 2001 From: andrewnester Date: Sun, 19 Feb 2017 20:28:40 +0300 Subject: [PATCH 3/4] Updated MISC/NEWS --- Misc/NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Misc/NEWS b/Misc/NEWS index 57e5ab9c20fe71..5837dfe078e3e6 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -252,6 +252,8 @@ Library - bpo-29576: Improve some deprecations in importlib. Some deprecated methods now emit DeprecationWarnings and have better descriptive messages. +- bpo-29553: Fixed ArgumentParses format_usage for mutually exclusive groups. + - bpo-29534: Fixed different behaviour of Decimal.from_float() for _decimal and _pydecimal. Thanks Andrew Nester. From 3f0ea543a957182d46f18a25417c62022e034afc Mon Sep 17 00:00:00 2001 From: andrewnester Date: Wed, 22 Feb 2017 20:54:18 +0300 Subject: [PATCH 4/4] Increased test coverage for argparse --- Lib/test/test_argparse.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index fbf3b1e0c8cd6a..6f1fe3c176900b 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -2362,6 +2362,26 @@ def test_help(self): ''' self.assertEqual(parser.format_help(), textwrap.dedent(expected)) + def test_help_required(self): + parser = ErrorRaisingArgumentParser(prog='PROG') + group1 = parser.add_mutually_exclusive_group(required=True) + group1.add_argument('--foo', action='store_true') + group1.add_argument('--bar', action='store_false') + group2 = parser.add_mutually_exclusive_group(required=True) + group2.add_argument('--soup', action='store_true') + group2.add_argument('--nuts', action='store_false') + expected = '''\ + usage: PROG [-h] (--foo | --bar) (--soup | --nuts) + + optional arguments: + -h, --help show this help message and exit + --foo + --bar + --soup + --nuts + ''' + self.assertEqual(parser.format_help(), textwrap.dedent(expected)) + class MEMixin(object): def test_failures_when_not_required(self):