diff options
author | amcrn <amcreynolds@ebaysf.com> | 2013-10-25 00:12:01 -0700 |
---|---|---|
committer | amcrn <amcreynolds@ebaysf.com> | 2013-10-25 00:15:41 -0700 |
commit | 21a468dd3be820bcd83eb7b90b72f2ca48ad8530 (patch) | |
tree | a710b46dd5d766fd6b78acfd76680914aad726e2 /troveclient/shell.py | |
parent | 24443e177efa445c9dd9004d7896126531e1b238 (diff) | |
download | python-troveclient-21a468dd3be820bcd83eb7b90b72f2ca48ad8530.tar.gz |
Usage String has Positionals after Optionals
print positionals before optionals in the usage string to help
users avoid argparse nargs='*' problems.
ex: 'trove create --databases <db_name> <name> <flavor_id>'
fails with 'error: too few arguments', but this succeeds:
'trove create <name> <flavor_id> --databases <db_name>'
Change-Id: I400c9f0ca5580bd287c5f3bcf935352e88071bc8
Closes-Bug: #1243981
Diffstat (limited to 'troveclient/shell.py')
-rw-r--r-- | troveclient/shell.py | 103 |
1 files changed, 101 insertions, 2 deletions
diff --git a/troveclient/shell.py b/troveclient/shell.py index adca730..5bcf7f3 100644 --- a/troveclient/shell.py +++ b/troveclient/shell.py @@ -35,10 +35,9 @@ import six import troveclient from troveclient import client -#from troveclient import exceptions as exc -#import troveclient.extension from troveclient.openstack.common import strutils from troveclient.openstack.common.apiclient import exceptions as exc +from troveclient.openstack.common.gettextutils import _ from troveclient import utils from troveclient.v1 import shell as shell_v1 @@ -514,6 +513,106 @@ class OpenStackHelpFormatter(argparse.HelpFormatter): heading = '%s%s' % (heading[0].upper(), heading[1:]) super(OpenStackHelpFormatter, self).start_section(heading) + def _format_usage(self, usage, actions, groups, prefix): + """ + Print positionals before optionals in the usage string to help + users avoid argparse nargs='*' problems. + + ex: 'trove create --databases <db_name> <name> <flavor_id>' + fails with 'error: too few arguments', but this succeeds: + 'trove create <name> <flavor_id> --databases <db_name>' + """ + if prefix is None: + prefix = _('usage: ') + + # if usage is specified, use that + if usage is not None: + usage = usage % dict(prog=self._prog) + + # if no optionals or positionals are available, usage is just prog + elif usage is None and not actions: + usage = '%(prog)s' % dict(prog=self._prog) + + # if optionals and positionals are available, calculate usage + elif usage is None: + prog = '%(prog)s' % dict(prog=self._prog) + + # split optionals from positionals + optionals = [] + positionals = [] + for action in actions: + if action.option_strings: + optionals.append(action) + else: + positionals.append(action) + + # build full usage string + format = self._format_actions_usage + action_usage = format(optionals + positionals, groups) + usage = ' '.join([s for s in [prog, action_usage] if s]) + + # wrap the usage parts if it's too long + text_width = self._width - self._current_indent + if len(prefix) + len(usage) > text_width: + + # break usage into wrappable parts + part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' + opt_usage = format(optionals, groups) + pos_usage = format(positionals, groups) + opt_parts = argparse._re.findall(part_regexp, opt_usage) + pos_parts = argparse._re.findall(part_regexp, pos_usage) + assert ' '.join(opt_parts) == opt_usage + assert ' '.join(pos_parts) == pos_usage + + # helper for wrapping lines + def get_lines(parts, indent, prefix=None): + lines = [] + line = [] + if prefix is not None: + line_len = len(prefix) - 1 + else: + line_len = len(indent) - 1 + for part in parts: + if line_len + 1 + len(part) > text_width: + lines.append(indent + ' '.join(line)) + line = [] + line_len = len(indent) - 1 + line.append(part) + line_len += len(part) + 1 + if line: + lines.append(indent + ' '.join(line)) + if prefix is not None: + lines[0] = lines[0][len(indent):] + return lines + + # if prog is short, follow it with optionals or positionals + if len(prefix) + len(prog) <= 0.75 * text_width: + indent = ' ' * (len(prefix) + len(prog) + 1) + if pos_parts: + lines = get_lines([prog] + pos_parts, indent, prefix) + lines.extend(get_lines(opt_parts, indent)) + elif opt_parts: + lines = get_lines([prog] + opt_parts, indent, prefix) + else: + lines = [prog] + + # if prog is long, put it on its own line + else: + indent = ' ' * len(prefix) + parts = pos_parts + opt_parts + lines = get_lines(parts, indent) + if len(lines) > 1: + lines = [] + lines.extend(get_lines(pos_parts, indent)) + lines.extend(get_lines(opt_parts, indent)) + lines = [prog] + lines + + # join lines into usage + usage = '\n'.join(lines) + + # prefix with 'usage:' + return '%s%s\n\n' % (prefix, usage) + def main(): try: |