This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: argparse: add max_text_width parameter to ArgumentParser
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.9
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: lucatrv, paul.j3, rhettinger
Priority: normal Keywords: patch

Created on 2020-03-01 10:54 by lucatrv, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
argparse_max_text_width.patch lucatrv, 2020-03-01 10:54 Patch to add max_text_width parameter to ArgumentParser
Messages (10)
msg363053 - (view) Author: Luca (lucatrv) * Date: 2020-03-01 10:54
It is often desirable to limit the help text width, for instance to 78 or 88 columns, regardless of the actual size of the terminal window.

Currently you can achieve this in rather cumbersome ways, for instance by setting "os.environ['COLUMNS'] = '80'" (but this requires the "os" module, which may not be needed otherwise by your module, and may lead to other undesired effects), or by writing a custom formatting class. IMHO there should be a simpler option for such a basic task.

I propose to add a max_text_width parameter to ArgumentParser. This would require only minor code changes to argparse (see attached patch), should I open a pull request on GitHub?
msg363073 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2020-03-01 18:09
https://bugs.python.org/issue13041

is (I think) the latest issue/patch to deal with the help width.

I don't like the idea of adding more parameters to the `ArgumentParser` class.  It's too complicated already.

There are a couple of ways that a user can do this already.

One is a custom version of the `parser.format_help`, though as your patch shows, that actually has to go through the `_get_formatter` method.  Only `format_help` is listed in the public API.

Another is a subclass of HelpFormatter.  It just needs to customize the `width` parameter.   

I vaguely recall suggesting such a subclass in a previous bug/issue, but can't find that.

Subclassing HelpFormatter is an established way of customizing the format.

Here's a discussion of this on StackOverflow.  It uses a simple lambda as `formatter_class`:

https://stackoverflow.com/questions/44333577/explain-lambda-argparse-helpformatterprog-width

formatter = lambda prog: argparse.HelpFormatter(prog, width=100)

and 

https://stackoverflow.com/questions/32888815/max-help-position-is-not-works-in-python-argparse-library
msg363076 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2020-03-01 18:36
> I don't like the idea of adding more parameters to 
> the `ArgumentParser` class.  It's too complicated already.

I concur with Paul.  Let's pass on this suggestion.
msg363210 - (view) Author: Luca (lucatrv) * Date: 2020-03-02 21:24
That lambda function would not give the same result, as it would constrain the text width to a fixed value, while my proposal would just set a maximum limit (if the terminal is narrower, the actual text width would be reduced).

With the current implementation all possible solutions are in my opinion overkilling for such a basic task. Maybe it is not a direct consequence of this, but as a matter of fact most Python scripts using `argparse` that I know (including `pip`) do not control the text width, which IMHO is not a good practice.
msg363212 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2020-03-02 22:04
But you can replace the simple 'lambda' with a function that takes the max with 'columns'.  In other words, include: 

             width = _shutil.get_terminal_size().columns
             width -= 2
             width = min(max_text_width, width)

The only thing that the 'formatter_class' parameter requires is a callable that takes 'prog' as argument.  That can be a class, a subclass, a lambda or a function.
msg363213 - (view) Author: Luca (lucatrv) * Date: 2020-03-02 23:05
OK, I will do this for my package, but I do not believe many others will do the same without a `max_text_width` parameter (too much care is needed for this to work correctly), and as a result most packages using `argparse` will continue to not properly handle text width.
msg363216 - (view) Author: Luca (lucatrv) * Date: 2020-03-02 23:33
For the benefit of other developers willing to control text width with `argparse`, using the lambda function let `mypy` fail with the following error:

541: error: Argument "formatter_class" to "ArgumentParser" has incompatible type "Callable[[Any], RawDescriptionHelpFormatter]"; expected "Type[HelpFormatter]"

So I am reverting back to the following custom formatting class:

class RawDescriptionHelpFormatterMaxTextWidth80(argparse.RawDescriptionHelpFormatter):
        """Set maximum text width = 80."""

        def __init__(self, prog):
            width = min(80, shutil.get_terminal_size().columns - 2)
            argparse.RawDescriptionHelpFormatter.__init__(self, prog, width=width)
msg363254 - (view) Author: Luca (lucatrv) * Date: 2020-03-03 12:11
Of course I still think there should be an easier way to do this though.
msg363387 - (view) Author: Luca (lucatrv) * Date: 2020-03-04 20:40
I opened two issues regarding the mypy error, I understand it is going to be fixed in typeshed:

https://github.com/python/mypy/issues/8487

https://github.com/python/typeshed/issues/3806
msg363557 - (view) Author: Luca (lucatrv) * Date: 2020-03-06 21:34
The issue has been fixed in `typeshed`, so the following is now allowed:

width = min(80, shutil.get_terminal_size().columns - 2)
formatter_class = lambda prog: argparse.RawDescriptionHelpFormatter(prog, width=width)

https://github.com/python/typeshed/issues/3806#event-3104796040
History
Date User Action Args
2022-04-11 14:59:27adminsetgithub: 83990
2020-03-06 21:34:21lucatrvsetmessages: + msg363557
2020-03-04 20:40:19lucatrvsetmessages: + msg363387
2020-03-03 12:11:14lucatrvsetmessages: + msg363254
2020-03-02 23:33:22lucatrvsetmessages: + msg363216
2020-03-02 23:05:08lucatrvsetmessages: + msg363213
2020-03-02 22:04:30paul.j3setmessages: + msg363212
2020-03-02 21:24:36lucatrvsetmessages: + msg363210
2020-03-01 18:36:25rhettingersetstatus: open -> closed
resolution: rejected
messages: + msg363076

stage: resolved
2020-03-01 18:09:30paul.j3setmessages: + msg363073
2020-03-01 12:55:11xtreaksetnosy: + rhettinger, paul.j3
2020-03-01 10:54:19lucatrvcreate