classification
Title: argparse: Specifying a whitespace-only help message to a positional arg triggers an IndexError when displaying --help
Type: Stage: patch review
Components: Library (Lib) Versions: Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Lucas Cimon, paul.j3, rhettinger
Priority: normal Keywords: patch

Created on 2019-10-24 14:40 by Lucas Cimon, last changed 2019-10-25 00:12 by paul.j3.

Pull Requests
URL Status Linked Edit
PR 16917 open Lucas Cimon, 2019-10-24 14:44
Messages (4)
msg355336 - (view) Author: Lucas Cimon (Lucas Cimon) * Date: 2019-10-24 14:40
The test I am going to add to test_argparse.py:

    def test_whitespace_help(self):
        parser = self._get_parser()
        parser.add_argument(
            '--foo2', action='store_true', help=' ')
        parser.add_argument(
            'bar2', type=float, help=' ')
        parser.format_help()  # raises an IndexError
msg355342 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2019-10-24 17:15
So the error occurs in HelpFormatter._format_action when 'help_lines' is an empty list:

        ....
        if action.help:
            help_text = self._expand_help(action)
            help_lines = self._split_lines(help_text, help_width)
     >>     parts.append('%*s%s\n' % (indent_first, '', help_lines[0]))
            for line in help_lines[1:]:
                parts.append('%*s%s\n' % (help_position, '', line))

It occurs with both optionals and positionals.  It does not occur with argparse.RawTextHelpFormatter.

The default _split_lines:

    def _split_lines(self, text, width):
        text = self._whitespace_matcher.sub(' ', text).strip()
        return _textwrap.wrap(text, width)

Because of the 'strip' it treats a help with space as empty

In [27]: f._split_lines('',40)                                                  
Out[27]: []
In [28]: f._split_lines('test',40)                                              
Out[28]: ['test']
In [29]: f._split_lines(' ',40)                                                 
Out[29]: []

None or '' don't trigger this because they are weeded out by the 'if action.help:' line.

Maybe the change proposed here, to skip the problem line if 'help_lines' is empty is the right one.  Assuming a list is non-empty can be dangerous.  But it would be wise to test the patch with the alternative formatters like the RawText.  

It may also be worth thinking about correcting the issue in _split_lines(), which is shorter.  The only difference in  RawTextHelpFormatter is in this method.
msg355348 - (view) Author: Lucas Cimon (Lucas Cimon) * Date: 2019-10-24 22:22
Thanks for the feedbacks paul.j3 !

I totally agree it would be best to test all formatter classes.
I modified the new test to do so.

I think I should probably place this test somewhere else in test_argparse.py, but I'm having trouble figuring out where exactly... I just updated the PR and moved the new test to the TestOptionalsHelpVersionActions class.

About the best fix, I think a fix to _split_lines() would be more complex. It needs to return a list, so we would make it return [''] in that case ? That would be a bit strange.

What do you think ? :)
msg355349 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2019-10-25 00:12
Just today on SO someone found a similar bug in the help format with metavars, that involved an unpacking the expected only one value, but got 2.

https://stackoverflow.com/questions/58541460/tuple-metavar-value-for-positional-argument-with-nargs-1

This had be raise years ago, as https://bugs.python.org/issue14074
History
Date User Action Args
2019-10-25 00:12:05paul.j3setmessages: + msg355349
2019-10-24 22:22:57Lucas Cimonsetmessages: + msg355348
2019-10-24 17:15:15paul.j3setmessages: + msg355342
2019-10-24 14:47:14xtreaksetnosy: + rhettinger, paul.j3
2019-10-24 14:44:04Lucas Cimonsetkeywords: + patch
stage: patch review
pull_requests: + pull_request16448
2019-10-24 14:40:54Lucas Cimoncreate