summaryrefslogtreecommitdiff
path: root/troveclient/shell.py
diff options
context:
space:
mode:
authoramcrn <amcreynolds@ebaysf.com>2013-10-25 00:12:01 -0700
committeramcrn <amcreynolds@ebaysf.com>2013-10-25 00:15:41 -0700
commit21a468dd3be820bcd83eb7b90b72f2ca48ad8530 (patch)
treea710b46dd5d766fd6b78acfd76680914aad726e2 /troveclient/shell.py
parent24443e177efa445c9dd9004d7896126531e1b238 (diff)
downloadpython-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.py103
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: