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)