classification
Title: argparse: suggestion for formatting optional positional args
Type: Stage: needs patch
Components: Library (Lib) Versions: Python 3.2, Python 3.3, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: bethard, paul.j3, petri.lehtinen, pwil3058
Priority: normal Keywords: patch

Created on 2011-03-29 04:21 by pwil3058, last changed 2014-04-25 05:25 by paul.j3.

Files
File name Uploaded Description Edit
regroup.patch paul.j3, 2014-04-25 05:22 review
test_regroup.py paul.j3, 2014-04-25 05:25
Messages (6)
msg132462 - (view) Author: Peter Williams (pwil3058) Date: 2011-03-29 04:21
At present, if a number (e.g. 2) of optional positional arguments
are defined (e.g. arg1 and arg2) argparse formats them as follows:

usage: program [arg1] [arg2]

in the usage message.  I would like to suggest that a better format would be:

usage: program [arg1 [arg2]]

as this more accurately reflects the way argparse treats them during parsing of the command line.

This formatting would be consistent with the way argparse currently formats a single optional positional argument with nargs='*', namely:

usage: program [arg [arg ...]]
msg141050 - (view) Author: Petri Lehtinen (petri.lehtinen) * (Python committer) Date: 2011-07-24 18:22
I think this is a good idea, and seems to me more like a bug than a feature request.
msg149543 - (view) Author: Steven Bethard (bethard) * (Python committer) Date: 2011-12-15 12:23
I agree that this is a bug in current argparse formatting.

It might be a little difficult to fix though because the current option formatting handles arguments one at a time, and producing something like [arg1 [arg2]] requires some understanding of how arguments interact, which the formatter currently doesn't have.

But patches are certainly welcome.
msg198038 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2013-09-19 01:40
This is a HelpFormatter function that takes a list of formatted actions, and groups contiguous blocks of optional positional actions.  It accounts for optionals (via prefix_chars) and mutually exclusive groups.

Since it assumes 'parts' is a list, rather than string, it works best with the '_format_actions_usage' method in the patch that I submitted to http://bugs.python.org/issue11874 

    def _positionals_regroup(self, parts):
        # change '[arg1] [arg2]' to '[arg1 [arg2]]'
        # apply to a list of formatted arguments
        # don't apply to optionals (with prefix chars) or groups
        chars = getattr(self, 'prefix_chars',set('-'))
        R = _re.compile(r'\] \[(.*)\]')
        result = []
        text = None
        while  parts:
            part = parts.pop()
            if part:
                if part[0]=='[' and part[1] not in chars and '|' not in part:
                    if text:
                        text = ' '.join([part, text])
                    else:
                        text = part
                    if R.search(text):
                        text = R.sub(' [\g<1>]]',text)
                else:
                    if text:
                        result.insert(0,text)
                        text = None
                    result.insert(0,part)
        if text:
            result.insert(0,text)
        return result

To avoid compatibility issues it could implemented in a subclassed HelpFormatter.
msg217153 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-04-25 05:22
This patch adds a ReGroupHelpFormatter class, which regroups positional arguments in the usage line as discussed before.  It builds on the reworked usage formatter in bugs/python.org/issue11874 (which keeps usage as a list longer).

For a complicate parser, usage may look like:

    usage: regp [-h] foo [arg1 [arg2 [arg3 [arg3 ...] [arg4]]]]
msg217154 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-04-25 05:25
This is a testing script for this patch.  It is not a unit test.
Example:

    p = argparse.ArgumentParser()
    p.formatter_class = argparse.ReGroupHelpFormatter
    p.add_argument('foo')
    p.add_argument('arg1',nargs='?')
    p.add_argument('arg2',nargs='?')
    a = p.add_argument('arg3',nargs='*')
    b = p.add_argument('arg4', nargs='?')
    # usage: regp [-h] foo [arg1 [arg2 [arg3 [arg3 ...] [arg4]]]]
History
Date User Action Args
2014-04-25 05:25:45paul.j3setfiles: + test_regroup.py

messages: + msg217154
2014-04-25 05:22:17paul.j3setfiles: + regroup.patch
keywords: + patch
messages: + msg217153
2013-09-19 01:40:57paul.j3setmessages: + msg198038
2013-09-16 06:46:39paul.j3setnosy: + paul.j3
2011-12-15 12:23:01bethardsetmessages: + msg149543
2011-07-24 18:22:15petri.lehtinensettype: enhancement ->
stage: needs patch
messages: + msg141050
versions: + Python 3.2, Python 3.3
2011-06-21 11:27:30petri.lehtinensetnosy: + bethard
2011-06-21 11:27:15petri.lehtinensetnosy: + petri.lehtinen
2011-03-29 04:21:10pwil3058create