Skip to content
Open
16 changes: 12 additions & 4 deletions sphinxarg/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -86,10 +86,12 @@ 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),
'prog': parser.prog,
}
subdata['help'] = helps.get(name, '') % {'prog': parser.prog}

if subalias:
subdata['identifier'] = name
parse_parser(subaction, subdata, **kwargs)
Expand Down Expand Up @@ -117,10 +119,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)
formatDict['default'] = default
formatDict = dict(vars(action),
prog=data.get('prog', ''),
default=default,
type=typename)
helpStr = action.help or '' # Ensure we don't print None
try:
helpStr = helpStr % formatDict
Expand Down
98 changes: 78 additions & 20 deletions test/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -157,9 +157,10 @@ def test_parse_nested():
assert data['children'] == [
{
'name': 'install',
'prog': 'test_parse_nested',
'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',
Expand Down Expand Up @@ -190,7 +191,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)

Expand All @@ -217,10 +218,11 @@ 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: 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',
Expand Down Expand Up @@ -249,7 +251,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'])
Expand All @@ -259,15 +261,16 @@ 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)',
'prog': 'test_aliased_traversal',
'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')
Expand Down Expand Up @@ -301,9 +304,10 @@ def test_parse_nested_traversal():
assert data2['children'] == [
{
'name': 'level3',
'prog': 'test_parse_nested_traversal level1 level2',
'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',
Expand All @@ -327,23 +331,51 @@ 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)'
}
]


def test_fill_in_usage_description_epilog():
"""
Ensure that %(prog)s gets filled in inside usage, description and epilog.
"""
parser = argparse.ArgumentParser(
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['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():
"""
If an optional argument has a string type and a default, then the default should be in quotes.
Expand Down Expand Up @@ -396,9 +428,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')
Expand All @@ -420,6 +457,27 @@ 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 [options] baz',
'action_groups': [{'options': [{'default': None,
'name': ['baz'],
'help': 'An integer'}],
'description': None,
'title': 'Positional Arguments'}],
'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,
'choices': 'XYZ',
'name': ['--barg'],
'help': 'A list of choices'}],
'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',
'prog': 'foo',
'help': 'B subparser'}
]