From 97dc6f91c2642a49bd7def2aa7862c8985e3cb0f Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Wed, 7 Aug 2019 22:39:09 +0200 Subject: [PATCH 01/12] Make tests work when not invoked by exactly 'py.test' --- test/test_parser.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/test_parser.py b/test/test_parser.py index 26a7481..ecb2278 100755 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -130,7 +130,7 @@ def test_parse_description(): def test_parse_nested(): - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(prog='test_parse_nested') parser.add_argument('foo', default=False, help='foo help') parser.add_argument('bar', default=False) @@ -158,8 +158,8 @@ def test_parse_nested(): { 'name': 'install', 'help': 'install help', - 'usage': 'usage: py.test install [-h] [--upgrade] ref', - 'bare_usage': 'py.test install [-h] [--upgrade] ref', + 'usage': 'usage: test_parse_nested install [-h] [--upgrade] ref', + 'bare_usage': 'test_parse_nested install [-h] [--upgrade] ref', 'action_groups': [ { 'title': 'Positional Arguments', @@ -190,7 +190,7 @@ def test_parse_nested(): if six.PY3: def test_parse_nested_with_alias(): - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(prog='test_parse_nested_with_alias') parser.add_argument('foo', default=False, help='foo help') parser.add_argument('bar', default=False) @@ -219,8 +219,8 @@ def test_parse_nested_with_alias(): 'name': 'install (i)', 'identifier': 'install', 'help': 'install help', - 'usage': 'usage: py.test install [-h] [--upgrade] ref', - 'bare_usage': 'py.test install [-h] [--upgrade] ref', + 'usage': 'usage: test_parse_nested_with_alias install [-h] [--upgrade] ref', + 'bare_usage': 'test_parse_nested_with_alias install [-h] [--upgrade] ref', 'action_groups': [ { 'title': 'Positional Arguments', @@ -249,7 +249,7 @@ def test_parse_nested_with_alias(): ] def test_aliased_traversal(): - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(prog='test_aliased_traversal') subparsers1 = parser.add_subparsers() subparsers1.add_parser('level1', aliases=['l1']) @@ -259,15 +259,15 @@ def test_aliased_traversal(): data2 = parser_navigate(data, 'level1') assert(data2 == { - 'bare_usage': 'py.test level1 [-h]', + 'bare_usage': 'test_aliased_traversal level1 [-h]', 'help': '', - 'usage': 'usage: py.test level1 [-h]', + 'usage': 'usage: test_aliased_traversal level1 [-h]', 'name': 'level1 (l1)', 'identifier': 'level1'}) def test_parse_nested_traversal(): - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(prog='test_parse_nested_traversal') subparsers1 = parser.add_subparsers() subparser1 = subparsers1.add_parser('level1') @@ -302,8 +302,8 @@ def test_parse_nested_traversal(): { 'name': 'level3', 'help': '', - 'usage': 'usage: py.test level1 level2 level3 [-h] foo bar', - 'bare_usage': 'py.test level1 level2 level3 [-h] foo bar', + 'usage': 'usage: test_parse_nested_traversal level1 level2 level3 [-h] foo bar', + 'bare_usage': 'test_parse_nested_traversal level1 level2 level3 [-h] foo bar', 'action_groups': [ { 'title': 'Positional Arguments', From 916011f3a917c25c864ad24e9ef3342aebb8dcd9 Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Tue, 6 Aug 2019 23:44:54 +0200 Subject: [PATCH 02/12] Add test_fill_in_description_epilog --- test/test_parser.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/test_parser.py b/test/test_parser.py index ecb2278..b0c958e 100755 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -344,6 +344,20 @@ def test_fill_in_default_prog(): ] +def test_fill_in_description_epilog(): + """ + Ensure that %(prog)s gets filled in inside description and epilog. + """ + parser = argparse.ArgumentParser( + prog='test_fill_in_description_epilog', + description='Welcome to %(prog)s', + epilog='%(prog)s salutes you') + data = parse_parser(parser) + + assert data['description'] == 'Welcome to test_fill_in_description_epilog' + assert data['epilog'] == 'test_fill_in_description_epilog salutes you' + + def test_string_quoting(): """ If an optional argument has a string type and a default, then the default should be in quotes. From 06a32371ae9e62a2538897fe2b72e849a022680e Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Wed, 7 Aug 2019 22:56:42 +0200 Subject: [PATCH 03/12] Substitute %(prog)s in description and epilog --- sphinxarg/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinxarg/parser.py b/sphinxarg/parser.py index 2b2c79f..d4ee60c 100644 --- a/sphinxarg/parser.py +++ b/sphinxarg/parser.py @@ -37,7 +37,7 @@ def _try_add_parser_attribute(data, parser, attribname): if not isinstance(attribval, str): return if len(attribval) > 0: - data[attribname] = attribval + data[attribname] = attribval % {'prog': data['prog']} def _format_usage_without_prefix(parser): From 2f247152894a4ebc2fe265b3093bf94cff38af34 Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Sun, 7 Mar 2021 15:55:12 +0100 Subject: [PATCH 04/12] Test substitution of more format specifiers in help messages --- test/test_parser.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/test/test_parser.py b/test/test_parser.py index b0c958e..ea06bff 100755 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -327,19 +327,30 @@ def test_parse_nested_traversal(): assert data == parser_navigate(data, '') -def test_fill_in_default_prog(): +def test_fill_in_help_specifiers_incl_prog(): """ - Ensure that %(default)s and %(prog)s are getting properly filled in inside help='' + Ensure that format specifiers get filled in inside help. + + Specifiers are %(prog)s and most keyword arguments to add_argument(). """ - parser = argparse.ArgumentParser(prog='test_fill_in_default_prog') + parser = argparse.ArgumentParser(prog='test_fill_in_help_specifiers_incl_prog') parser.add_argument('bar', default='foo', help='%(prog)s (default: %(default)s)') + parser.add_argument('-p', type=int, metavar='passes', + help='number of %(metavar)s (%(type)s)') data = parse_parser(parser) assert data['action_groups'][0]['options'] == [ { 'default': '"foo"', 'name': ['bar'], - 'help': 'test_fill_in_default_prog (default: "foo")' + 'help': 'test_fill_in_help_specifiers_incl_prog (default: "foo")' + } + ] + assert data['action_groups'][1]['options'] == [ + { + 'default': None, + 'name': ['-p'], + 'help': 'number of passes (int)' } ] From 63fab89e7039ae261be2f0d0e87ac140c6631817 Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Sun, 7 Mar 2021 15:57:14 +0100 Subject: [PATCH 05/12] Test that prog gets filled in in usage too --- test/test_parser.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/test_parser.py b/test/test_parser.py index ea06bff..24ac750 100755 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -355,18 +355,21 @@ def test_fill_in_help_specifiers_incl_prog(): ] -def test_fill_in_description_epilog(): +def test_fill_in_usage_description_epilog(): """ - Ensure that %(prog)s gets filled in inside description and epilog. + Ensure that %(prog)s gets filled in inside usage, description and epilog. """ parser = argparse.ArgumentParser( - prog='test_fill_in_description_epilog', + prog='test_fill_in_usage_description_epilog', + usage='%(prog)s [options]', description='Welcome to %(prog)s', epilog='%(prog)s salutes you') data = parse_parser(parser) - assert data['description'] == 'Welcome to test_fill_in_description_epilog' - assert data['epilog'] == 'test_fill_in_description_epilog salutes you' + assert data['usage'] == 'usage: test_fill_in_usage_description_epilog [options]' + assert data['bare_usage'] == 'test_fill_in_usage_description_epilog [options]' + assert data['description'] == 'Welcome to test_fill_in_usage_description_epilog' + assert data['epilog'] == 'test_fill_in_usage_description_epilog salutes you' def test_string_quoting(): From 664f05dd776c77e60be5e363294a0f4eb2e1eb9c Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Sun, 7 Mar 2021 16:01:53 +0100 Subject: [PATCH 06/12] Fold long lines --- test/test_parser.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/test_parser.py b/test/test_parser.py index 24ac750..19a42ee 100755 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -448,6 +448,23 @@ def test_action_groups_with_subcommands(): ] assert data['children'] == [ - {'usage': 'usage: foo A [-h] baz', 'action_groups': [{'options': [{'default': None, 'name': ['baz'], 'help': 'An integer'}], 'description': None, 'title': 'Positional Arguments'}], 'bare_usage': 'foo A [-h] baz', 'name': 'A', 'help': 'A subparser'}, - {'usage': 'usage: foo B [-h] [--barg {X,Y,Z}]', 'action_groups': [{'options': [{'default': None, 'choices': 'XYZ', 'name': ['--barg'], 'help': 'A list of choices'}], 'description': None, 'title': 'Named Arguments'}], 'bare_usage': 'foo B [-h] [--barg {X,Y,Z}]', 'name': 'B', 'help': 'B subparser'} + {'usage': 'usage: foo A [-h] baz', + 'action_groups': [{'options': [{'default': None, + 'name': ['baz'], + 'help': 'An integer'}], + 'description': None, + 'title': 'Positional Arguments'}], + 'bare_usage': 'foo A [-h] baz', + 'name': 'A', + 'help': 'A subparser'}, + {'usage': 'usage: foo B [-h] [--barg {X,Y,Z}]', + 'action_groups': [{'options': [{'default': None, + 'choices': 'XYZ', + 'name': ['--barg'], + 'help': 'A list of choices'}], + 'description': None, + 'title': 'Named Arguments'}], + 'bare_usage': 'foo B [-h] [--barg {X,Y,Z}]', + 'name': 'B', + 'help': 'B subparser'} ] From f5a9353067b054129ea1c55b37185462b7f566bf Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Sun, 7 Mar 2021 16:10:33 +0100 Subject: [PATCH 07/12] Test that prog gets substituted in documentation for subcommands This provokes an error pointed out by snowman2 (Alan D. Snow) and a few others. Specifically help, usage, description and epilog strings. --- test/test_parser.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/test/test_parser.py b/test/test_parser.py index 19a42ee..27c31e5 100755 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -424,9 +424,14 @@ def test_action_groups_with_subcommands(): """ parser = argparse.ArgumentParser('foo') subparsers = parser.add_subparsers() - parserA = subparsers.add_parser('A', help='A subparser') + parserA = subparsers.add_parser('A', help='A subparser for %(prog)s', + usage='%(prog)s [options] baz', + description='Perform action a from' + ' within %(prog)s.') parserA.add_argument('baz', type=int, help='An integer') - parserB = subparsers.add_parser('B', help='B subparser') + parserB = subparsers.add_parser('B', help='B subparser', + epilog='Action b is expensive when' + ' performed by %(prog)s.') parserB.add_argument('--barg', choices='XYZ', help='A list of choices') parser.add_argument('--foo', help='foo help') @@ -448,15 +453,16 @@ def test_action_groups_with_subcommands(): ] assert data['children'] == [ - {'usage': 'usage: foo A [-h] baz', + {'usage': 'usage: foo A [options] baz', 'action_groups': [{'options': [{'default': None, 'name': ['baz'], 'help': 'An integer'}], 'description': None, 'title': 'Positional Arguments'}], - 'bare_usage': 'foo A [-h] baz', + 'bare_usage': 'foo A [options] baz', + 'description': 'Perform action a from within foo.', 'name': 'A', - 'help': 'A subparser'}, + 'help': 'A subparser for foo'}, {'usage': 'usage: foo B [-h] [--barg {X,Y,Z}]', 'action_groups': [{'options': [{'default': None, 'choices': 'XYZ', @@ -465,6 +471,7 @@ def test_action_groups_with_subcommands(): 'description': None, 'title': 'Named Arguments'}], 'bare_usage': 'foo B [-h] [--barg {X,Y,Z}]', + 'epilog': 'Action b is expensive when performed by foo.', 'name': 'B', 'help': 'B subparser'} ] From a1585dc153d072b959edd9da9a9e8a84f0be1545 Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Sun, 7 Mar 2021 17:13:22 +0100 Subject: [PATCH 08/12] Substitute prog in help messages for subparsers --- sphinxarg/parser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinxarg/parser.py b/sphinxarg/parser.py index d4ee60c..d5ef205 100644 --- a/sphinxarg/parser.py +++ b/sphinxarg/parser.py @@ -86,10 +86,11 @@ def parse_parser(parser, data=None, **kwargs): subaction.prog = '%s %s' % (parser.prog, name) subdata = { 'name': name if not subalias else '%s (%s)' % (name, ', '.join(subalias)), - 'help': helps.get(name, ''), 'usage': subaction.format_usage().strip(), 'bare_usage': _format_usage_without_prefix(subaction), } + subdata['help'] = helps.get(name, '') % {'prog': parser.prog} + if subalias: subdata['identifier'] = name parse_parser(subaction, subdata, **kwargs) From 8b36f4bc1e8759cc66444041d20b1c7d95ce4d3e Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Sun, 7 Mar 2021 17:14:59 +0100 Subject: [PATCH 09/12] Remove redundant setting of default in formatDict --- sphinxarg/parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinxarg/parser.py b/sphinxarg/parser.py index d5ef205..b900ef7 100644 --- a/sphinxarg/parser.py +++ b/sphinxarg/parser.py @@ -121,7 +121,6 @@ def parse_parser(parser, data=None, **kwargs): # fill in any formatters, like %(default)s formatDict = dict(vars(action), prog=data.get('prog', ''), default=default) - formatDict['default'] = default helpStr = action.help or '' # Ensure we don't print None try: helpStr = helpStr % formatDict From 7b1f5a071badbacaaf9112fb5f261ca8844037b4 Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Sun, 7 Mar 2021 17:16:18 +0100 Subject: [PATCH 10/12] Fold long line --- sphinxarg/parser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinxarg/parser.py b/sphinxarg/parser.py index b900ef7..507454d 100644 --- a/sphinxarg/parser.py +++ b/sphinxarg/parser.py @@ -120,7 +120,9 @@ def parse_parser(parser, data=None, **kwargs): default = '"%s"' % default # fill in any formatters, like %(default)s - formatDict = dict(vars(action), prog=data.get('prog', ''), default=default) + formatDict = dict(vars(action), + prog=data.get('prog', ''), + default=default) helpStr = action.help or '' # Ensure we don't print None try: helpStr = helpStr % formatDict From b3f028989ba6146a37ee92c3a6ee32976792dd0e Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Sun, 7 Mar 2021 17:18:25 +0100 Subject: [PATCH 11/12] Use plain name of types in substitutions rather than their repr --- sphinxarg/parser.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sphinxarg/parser.py b/sphinxarg/parser.py index 507454d..41f97d5 100644 --- a/sphinxarg/parser.py +++ b/sphinxarg/parser.py @@ -118,11 +118,16 @@ def parse_parser(parser, data=None, **kwargs): default = action.default if action.default not in ['', None, True, False] and action.type in [None, str] and isinstance(action.default, str): default = '"%s"' % default + # Don't use repr of types + typename = None + if action.type is not None: + typename = action.type.__name__ # fill in any formatters, like %(default)s formatDict = dict(vars(action), prog=data.get('prog', ''), - default=default) + default=default, + type=typename) helpStr = action.help or '' # Ensure we don't print None try: helpStr = helpStr % formatDict From a941d9f8947216eae6d17bc2f643f4500c6c6668 Mon Sep 17 00:00:00 2001 From: Ulrik Haugen Date: Sun, 7 Mar 2021 17:20:54 +0100 Subject: [PATCH 12/12] Set prog to parent parsers prog in subparsers subdata dict Also adjust test cases to account for the new key. --- sphinxarg/parser.py | 1 + test/test_parser.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/sphinxarg/parser.py b/sphinxarg/parser.py index 41f97d5..8c0fd3e 100644 --- a/sphinxarg/parser.py +++ b/sphinxarg/parser.py @@ -88,6 +88,7 @@ def parse_parser(parser, data=None, **kwargs): 'name': name if not subalias else '%s (%s)' % (name, ', '.join(subalias)), 'usage': subaction.format_usage().strip(), 'bare_usage': _format_usage_without_prefix(subaction), + 'prog': parser.prog, } subdata['help'] = helps.get(name, '') % {'prog': parser.prog} diff --git a/test/test_parser.py b/test/test_parser.py index 27c31e5..2320069 100755 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -157,6 +157,7 @@ def test_parse_nested(): assert data['children'] == [ { 'name': 'install', + 'prog': 'test_parse_nested', 'help': 'install help', 'usage': 'usage: test_parse_nested install [-h] [--upgrade] ref', 'bare_usage': 'test_parse_nested install [-h] [--upgrade] ref', @@ -217,6 +218,7 @@ def test_parse_nested_with_alias(): assert data['children'] == [ { 'name': 'install (i)', + 'prog': 'test_parse_nested_with_alias', 'identifier': 'install', 'help': 'install help', 'usage': 'usage: test_parse_nested_with_alias install [-h] [--upgrade] ref', @@ -263,6 +265,7 @@ def test_aliased_traversal(): 'help': '', 'usage': 'usage: test_aliased_traversal level1 [-h]', 'name': 'level1 (l1)', + 'prog': 'test_aliased_traversal', 'identifier': 'level1'}) @@ -301,6 +304,7 @@ def test_parse_nested_traversal(): assert data2['children'] == [ { 'name': 'level3', + 'prog': 'test_parse_nested_traversal level1 level2', 'help': '', 'usage': 'usage: test_parse_nested_traversal level1 level2 level3 [-h] foo bar', 'bare_usage': 'test_parse_nested_traversal level1 level2 level3 [-h] foo bar', @@ -462,6 +466,7 @@ def test_action_groups_with_subcommands(): 'bare_usage': 'foo A [options] baz', 'description': 'Perform action a from within foo.', 'name': 'A', + 'prog': 'foo', 'help': 'A subparser for foo'}, {'usage': 'usage: foo B [-h] [--barg {X,Y,Z}]', 'action_groups': [{'options': [{'default': None, @@ -473,5 +478,6 @@ def test_action_groups_with_subcommands(): 'bare_usage': 'foo B [-h] [--barg {X,Y,Z}]', 'epilog': 'Action b is expensive when performed by foo.', 'name': 'B', + 'prog': 'foo', 'help': 'B subparser'} ]