Skip to content

Commit

Permalink
Show arguments list with description in help
Browse files Browse the repository at this point in the history
Show default values help for parameters and arguments
Don't hide bash-install option
  • Loading branch information
igrek51 committed Sep 18, 2019
1 parent 267643a commit d2b1c6d
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 11 deletions.
60 changes: 51 additions & 9 deletions cliglue/help/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from cliglue.builder.rule import PrimaryOptionRule, ParameterRule, FlagRule, CliRule, SubcommandRule, \
PositionalArgumentRule, ManyArgumentsRule, DictionaryRule
from cliglue.parser.context import RunContext
from cliglue.parser.keyword import format_var_names
from cliglue.parser.keyword import format_var_names, format_var_name
from cliglue.parser.parser import Parser
from cliglue.parser.transform import filter_rules
from cliglue.version import __version__
Expand All @@ -19,7 +19,7 @@ class _OptionHelp(object):
parent: '_OptionHelp' = None


internal_options = {'--bash-install', '--bash-autocomplete'}
internal_options = {'--bash-autocomplete'}


def print_help(rules: List[CliRule], app_name: str, version: str, help: str, subargs: List[str], hide_internal: bool):
Expand Down Expand Up @@ -74,6 +74,7 @@ def generate_subcommand_help(
pos_arguments = filter_rules(all_rules, PositionalArgumentRule)
many_args = filter_rules(all_rules, ManyArgumentsRule)

pos_args_helps: List[_OptionHelp] = _generate_pos_args_helps(pos_arguments, many_args)
options: List[_OptionHelp] = _generate_options_helps(all_rules, hide_internal)
commands: List[_OptionHelp] = _generate_commands_helps(subcommands)

Expand All @@ -87,6 +88,10 @@ def generate_subcommand_help(
out.append('Usage:')
out.append(generate_usage(app_bin_prefix, commands, have_rules_options(all_rules), many_args, pos_arguments))

if pos_args_helps:
out.append('\nArguments:')
__helpers_output(pos_args_helps, out)

if options:
out.append('\nOptions:')
__helpers_output(options, out)
Expand Down Expand Up @@ -132,7 +137,11 @@ def __helpers_output(commands, out):
for helper in commands:
name_padded = helper.cmd.ljust(padding)
if helper.help:
out.append(f' {name_padded} - {helper.help}')
for idx, line in enumerate(helper.help.splitlines()):
if idx == 0:
out.append(f' {name_padded} - {line}')
else:
out.append(' ' * (2 + padding + 3) + line)
else:
out.append(f' {name_padded}')

Expand All @@ -151,6 +160,14 @@ def _max_name_width(helps: List[_OptionHelp]) -> int:
return max(map(lambda h: len(h.cmd), helps))


def _generate_pos_args_helps(
pos_arguments: List[PositionalArgumentRule],
many_args: List[ManyArgumentsRule]
) -> List[_OptionHelp]:
return [_pos_arg_help(rule) for rule in pos_arguments] + \
[_many_args_help(rule) for rule in many_args]


def _generate_options_helps(rules: List[CliRule], hide_internal: bool) -> List[_OptionHelp]:
# filter non-empty
return list(filter(lambda o: o, [_generate_option_help(rule, hide_internal) for rule in rules]))
Expand All @@ -165,6 +182,7 @@ def _generate_option_help(rule: CliRule, hide_internal: bool) -> Optional[_Optio
return _parameter_help(rule)
elif isinstance(rule, DictionaryRule):
return _dictionary_help(rule)
return None


def _generate_commands_helps(rules: List[CliRule], parent: _OptionHelp = None, subrules: List[CliRule] = None
Expand Down Expand Up @@ -217,25 +235,40 @@ def _flag_help(rule: FlagRule) -> _OptionHelp:

def _parameter_help(rule: ParameterRule) -> _OptionHelp:
cmd = ', '.join(sorted_keywords(rule.keywords)) + ' ' + _param_display_name(rule)
return _OptionHelp(cmd, rule.help)
default_value = display_default_value(rule.default)
help_text = '\n'.join(filter(lambda t: t is not None, [rule.help, default_value]))
return _OptionHelp(cmd, help_text)


def _dictionary_help(rule: DictionaryRule) -> _OptionHelp:
cmd = ', '.join(sorted_keywords(rule.keywords)) + ' KEY VALUE'
return _OptionHelp(cmd, rule.help)


def _pos_arg_help(rule: PositionalArgumentRule) -> _OptionHelp:
cmd = display_positional_argument(rule)
default_value = display_default_value(rule.default)
help_text = '\n'.join(filter(lambda t: t is not None, [rule.help, default_value]))
return _OptionHelp(cmd, help_text)


def _many_args_help(rule: ManyArgumentsRule) -> _OptionHelp:
cmd = display_many_arguments(rule)
help_text = rule.help
return _OptionHelp(cmd, help_text)


def _param_display_name(rule: ParameterRule) -> str:
if rule.name:
return rule.name.upper()
return format_var_name(rule.name).upper()
else:
# get name from longest keyword
names: Set[str] = format_var_names(rule.keywords)
return max(names, key=lambda n: len(n)).upper()


def _argument_var_name(rule: PositionalArgumentRule) -> str:
return rule.name.upper()
return format_var_name(rule.name).upper()


def _subcommand_short_name(rule: SubcommandRule) -> str:
Expand All @@ -255,17 +288,20 @@ def display_positional_argument(rule: PositionalArgumentRule) -> str:
return f' [{var_name}]'


def display_all_arguments(rule: ManyArgumentsRule) -> str:
def display_many_arguments(rule: ManyArgumentsRule) -> str:
arg_name = rule.name.upper()
return f' [{arg_name}...]'
if rule.count_min():
return f' {arg_name}...'
else:
return f' [{arg_name}...]'


def usage_positional_arguments(rules: List[PositionalArgumentRule]) -> str:
return ''.join([display_positional_argument(rule) for rule in rules])


def usage_many_arguments(rules: List[ManyArgumentsRule]) -> str:
return ''.join([display_all_arguments(rule) for rule in rules])
return ''.join([display_many_arguments(rule) for rule in rules])


def shell_command_name():
Expand All @@ -274,3 +310,9 @@ def shell_command_name():

def have_rules_options(rules: List[CliRule]) -> bool:
return bool(filter_rules(rules, FlagRule, ParameterRule, DictionaryRule, PrimaryOptionRule))


def display_default_value(default) -> Optional[str]:
if default is None:
return None
return 'Default: ' + str(default)
2 changes: 1 addition & 1 deletion docs/example/helpme.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def main():
subcommand('git').has(
subcommand('help', help='shows help'),
subcommand('push').has(
argument('remote'),
argument('remote', 'remote repo name'),
argument('branch', required=False),
),
subcommand('describe').has(
Expand Down
43 changes: 42 additions & 1 deletion tests/help/test_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def test_default_help_when_no_arguments():
def test_hiding_internal_options():
with MockIO('--help') as mockio:
CliBuilder(hide_internal=True).run()
assert '--bash-install' not in mockio.output()
assert '--bash-install' in mockio.output()
assert '--bash-autocomplete' not in mockio.output()
with MockIO('--help') as mockio:
CliBuilder(hide_internal=False).run()
Expand Down Expand Up @@ -169,3 +169,44 @@ def test_display_explicit_param_name():
parameter('p', name='param'),
).run()
assert '-p PARAM' in mockio.output()


def test_display_parameter_default_value():
with MockIO('--help') as mockio:
CliBuilder().has(
parameter('src-path', help='source path', default='/home/user'),
parameter('target-path', default='/root'),
parameter('num', help='number', type=int, default=52),
).run()
assert 'SRC_PATH' in mockio.output()
assert 'source path' in mockio.output()
assert 'Default: /home/user' in mockio.output()
assert 'Default: 52' in mockio.output()


def test_display_argument_default_value():
with MockIO('--help') as mockio:
CliBuilder().has(
argument('src-path', help='source path', default='/home/user', required=False),
argument('target-path', default='/root', required=False),
argument('num', help='number', type=int, default=52, required=False),
).run()
assert 'SRC_PATH' in mockio.output()
assert 'source path' in mockio.output()
assert 'Default: /home/user' in mockio.output()
assert 'Default: 52' in mockio.output()


def test_display_arguments_help():
with MockIO('--help') as mockio:
CliBuilder().has(
argument('src-path', help='source path'),
argument('num', help='number', type=int),
arguments('paths'),
).run()
assert 'Arguments:' in mockio.output()
assert 'SRC_PATH' in mockio.output()
assert 'source path' in mockio.output()
assert 'NUM' in mockio.output()
assert 'number' in mockio.output()
assert 'PATHS' in mockio.output()

0 comments on commit d2b1c6d

Please sign in to comment.