diff -r 9ea84f006892 Lib/argparse.py --- a/Lib/argparse.py Wed May 01 15:15:50 2013 +0200 +++ b/Lib/argparse.py Tue Jul 16 11:49:43 2013 -0700 @@ -311,21 +311,15 @@ # 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]) + action_parts = format(optionals + positionals, groups) + usage = ' '.join([prog]+action_parts) # 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 = _re.findall(part_regexp, opt_usage) - pos_parts = _re.findall(part_regexp, pos_usage) - assert ' '.join(opt_parts) == opt_usage - assert ' '.join(pos_parts) == pos_usage + opt_parts = format(optionals, groups) + pos_parts = format(positionals, groups) # helper for wrapping lines def get_lines(parts, indent, prefix=None): @@ -336,7 +330,7 @@ else: line_len = len(indent) - 1 for part in parts: - if line_len + 1 + len(part) > text_width: + if line and line_len + 1 + len(part) > text_width: lines.append(indent + ' '.join(line)) line = [] line_len = len(indent) - 1 @@ -377,61 +371,75 @@ return '%s%s\n\n' % (prefix, usage) def _format_actions_usage(self, actions, groups): - # find group indices and identify actions in groups - group_actions = set() - inserts = {} - for group in groups: - try: - start = actions.index(group._group_actions[0]) - except ValueError: - continue - else: - end = start + len(group._group_actions) - if actions[start:end] == group._group_actions: - for action in group._group_actions: - group_actions.add(action) - if not group.required: - if start in inserts: - inserts[start] += ' [' - else: - inserts[start] = '[' - inserts[end] = ']' - else: - if start in inserts: - inserts[start] += ' (' - else: - inserts[start] = '(' - inserts[end] = ')' - for i in range(start + 1, end): - inserts[i] = '|' + # format the usage using the actions list. Where possible + # format the groups that include those actions + # The actions list has priority + # This is a new version that formats the groups directly without + # needing inserts, (most) cleanup, or parsing into parts + parts = [] + i = 0 + # step through the actions list + while i1: + parts[-1] = ')' if group.required else ']' + else: + # nothing added + parts = [] + arg_parts = [''.join(parts)] + + def cleanup(text): + # remove unnecessary () + text = _re.sub(r'^\(([^|]*)\)$', r'\1', text) + return text + arg_parts = [cleanup(t) for t in arg_parts] + return arg_parts + + def _format_just_actions_usage(self, actions): + # actions, without any group markings + parts = [] + for action in actions: if action.help is SUPPRESS: - parts.append(None) - if inserts.get(i) == '|': - inserts.pop(i) - elif inserts.get(i + 1) == '|': - inserts.pop(i + 1) - - # produce all arg strings + pass elif not action.option_strings: default = self._get_default_metavar_for_positional(action) part = self._format_args(action, default) - - # if it's in a group, strip the outer [] - if action in group_actions: - if part[0] == '[' and part[-1] == ']': - part = part[1:-1] - - # add the action string to the list parts.append(part) - - # produce the first way to invoke the option in brackets else: option_string = action.option_strings[0] @@ -447,31 +455,11 @@ args_string = self._format_args(action, default) part = '%s %s' % (option_string, args_string) - # make it look optional if it's not required or in a group - if not action.required and action not in group_actions: + # make it look optional if it's not required + if not action.required: part = '[%s]' % part - - # add the action string to the list parts.append(part) - - # insert things at the necessary indices - for i in sorted(inserts, reverse=True): - parts[i:i] = [inserts[i]] - - # join all the action items with spaces - text = ' '.join([item for item in parts if item is not None]) - - # clean up separators for mutually exclusive groups - open = r'[\[(]' - close = r'[\])]' - text = _re.sub(r'(%s) ' % open, r'\1', text) - text = _re.sub(r' (%s)' % close, r'\1', text) - text = _re.sub(r'%s *%s' % (open, close), r'', text) - text = _re.sub(r'\(([^|]*)\)', r'\1', text) - text = text.strip() - - # return the text - return text + return parts def _format_text(self, text): if '%(prog)' in text: diff -r 9ea84f006892 Lib/test/test_argparse.py --- a/Lib/test/test_argparse.py Wed May 01 15:15:50 2013 +0200 +++ b/Lib/test/test_argparse.py Tue Jul 16 11:49:43 2013 -0700 @@ -4118,6 +4118,46 @@ version = '' +class TestHelpMetavarArgumentsInnerBracketSplitLines(HelpTestCase): + """""" + + def custom_type(string): + return string + + parser_signature = Sig(prog='PROG') + long_a = 'a' * 60 + long_d = 'd' * 60 + argument_signatures = [Sig('--a', metavar=long_a), + Sig('--b', metavar='[innerpart]outerpart'), + Sig('--c'), + Sig('d', metavar=long_d), + Sig('e', metavar='[innerpart2]outerpart2'), + Sig('f'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] + [--a {0}] + [--b [innerpart]outerpart] [--c C] + {1} + [innerpart2]outerpart2 f + '''.format(long_a, long_d) + help = usage + '''\ + + positional arguments: + {0} + [innerpart2]outerpart2 + f + + optional arguments: + -h, --help show this help message and exit + --a {1} + --b [innerpart]outerpart + --c C + '''.format(long_d, long_a) + version = '' + + # ===================================== # Optional/Positional constructor tests # =====================================