Python ArgParse Columns

Have you ever been bothered by cramped help text formatted by Python's argparse module, especially for its affinity for enthusiastic left indents? It has that distinctive format universally discernible for features good and unfortunate. Today we are going to explore and annihilate one of these features of misfortune. Like pip, pytest, and despite what the authors may claim in effect, for it's abstruse Java-celebrating recursions into metastatic blandness: also the logging module--they all depress onto the user some burden of monolithic inflexibility in a way that feels as if the user shall be prevented from getting the job done by the nonblocking opinion of aloof, indifferent code. In the best case, the job is done with extra provisions for these superior opinions that provide comedic commentary written in code on such opinions. In the case of argparse, the formatting customization is limited to 3 simplistic choices. Subclassing is not an option because the way these classes affect argparse output behavior is hidden in source code (obfuscated from sane external minds as that unwholsome logic called implementation detail). However, though learning how awfully the code you routinely employ is hampered by irritating internal rigidity, this particular black box needn't inevitably stumble anyone since the columns conundrum itself yields readily to a few lines of boilerplate.

Problem

Observe:

#/usr/bin/env python3
# my_script.py
import argparse


def _get_opts():
    """
    Setup options for my_script
    """
    ap = argparse.ArgumentParser(description='My script does something, I guess', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    ap.add_argument('-a', '--an-argument-with-a-really-long-help-msg', help='The intent of this help string is to demonstrate how unreadable a hard line wrap at a narrow column span looks when there is an expansive, capacious field of space to the right of this text available in the terminal.  The environment variable os.environ[\'COLUMNS\'] does not inherit the parent process\'s (a bash shell in this case) $COLUMNS value.')
    return vars(ap.parse_args())


def my_action():
    """
    Run the action of my script
    """
    opts = _get_opts()
    raise NotImplementedError('Really, I don\'t plan to do anything at all here')

if __name__ == '__main__':
    my_action()
$ python3 my_script.py --help
usage: my_script.py [-h] [-a AN_ARGUMENT_WITH_A_REALLY_LONG_HELP_MSG]

My script does something, I guess

optional arguments:
  -h, --help            show this help message and exit
  -a AN_ARGUMENT_WITH_A_REALLY_LONG_HELP_MSG, --an-argument-with-a-really-long-help-msg AN_ARGUMENT_WITH_A_REALLY_LONG_HELP_MSG
                        The intent of this help string is to demonstrate how
                        unreadable a hard line wrap at a narrow column span
                        looks when there is an expansive, capacious field of
                        space to the right of this text available in the
                        terminal. The environment variable
                        os.environ['COLUMNS'] does not inherit the parent
                        process's (a bash shell in this case) $COLUMNS value.
                        (default: None)

Solution

Since 3.3, the shutil module provides a way to insert the terminal's geometry into your script in a way that argparse comprehends:

import shutil
import argparse

# Necessary boilerplate to imbue the argparse help output with essential readability
os.environ['COLUMNS'] = str(shutil.get_terminal_size().columns)
$ python3 my_script.py --help
usage: my_script.py [-h] [-a AN_ARGUMENT_WITH_A_REALLY_LONG_HELP_MSG]

My script does something, I guess

optional arguments:
  -h, --help            show this help message and exit
  -a AN_ARGUMENT_WITH_A_REALLY_LONG_HELP_MSG, --an-argument-with-a-really-long-help-msg AN_ARGUMENT_WITH_A_REALLY_LONG_HELP_MSG
                        The intent of this help string is to demonstrate how unreadable a hard line wrap at a narrow column span looks when there is an expansive, capacious field of space to the right of this text available in the terminal. The environment variable os.environ['COLUMNS'] does not inherit the parent
                        process's (a bash shell in this case) $COLUMNS value. (default: None)
Posted: | Source