442

Suppose I have the following argparse snippet:

diags.cmdln_parser.add_argument( '--scan-time',
                     action  = 'store',
                     nargs   = '?',
                     type    = int,
                     default = 5,
                     help    = "Wait SCAN-TIME seconds between status checks.")

Currently, --help returns:

usage: connection_check.py [-h]
                             [--version] [--scan-time [SCAN_TIME]]

          Test the reliability/uptime of a connection.



optional arguments:
-h, --help            show this help message and exit
--version             show program's version number and exit
--scan-time [SCAN_TIME]
                    Wait SCAN-TIME seconds between status checks.

I would prefer something like:

--scan-time [SCAN_TIME]
                    Wait SCAN-TIME seconds between status checks.
                    (Default = 5)

Peeking at the help formatter code revealed limited options. Is there a clever way to get argparse to print the default value for --scan-time in a similar fashion, or should I just subclass the help formatter?

akshat
  • 1,219
  • 1
  • 8
  • 24
JS.
  • 14,781
  • 13
  • 63
  • 75
  • 7
    You may be interested in [docopt](https://github.com/docopt/docopt). I've never looked at argparse again. – Paulo Scardine Aug 28 '12 at 00:28
  • 31
    @PauloScardine - Being built in to the language is a major benefit for argparse. – jordanm Aug 28 '12 at 00:32
  • 1
    @PauloScardine: Pulling a non-standard library into my current project will indeed be a pain, but I sure like the look of docopt's output. Thanks for the tip! – JS. Aug 28 '12 at 17:47
  • @JS. you say "Pulling a non-standard library into my current project will indeed be a pain" Really? There are plenty of very useful libraries at pypi. In my context it is easy to pull in a non-standard library. It is sad, if it is hard in your context. – guettli Jun 04 '18 at 12:17
  • 5
    @guettli: That project was for a commercial embedded project. You're right installation was easy. Getting approval from corporate legal was a nightmare. – JS. Jun 04 '18 at 16:55

4 Answers4

647

Use the argparse.ArgumentDefaultsHelpFormatter formatter:

parser = argparse.ArgumentParser(
    # ... other options ...
    formatter_class=argparse.ArgumentDefaultsHelpFormatter)

To quote the documentation:

The other formatter class available, ArgumentDefaultsHelpFormatter, will add information about the default value of each of the arguments.

Note that this only applies to arguments that have help text defined; with no help value for an argument, there is no help message to add information about the default value to.

The exact output for your scan-time option then becomes:

  --scan-time [SCAN_TIME]
                        Wait SCAN-TIME seconds between status checks.
                        (default: 5)
akaihola
  • 26,309
  • 7
  • 59
  • 69
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 22
    Can I control that only the arguments with an explicit `default=` show the default value? Since I don't like the 'default: None' texts. – Ziyuan Jun 07 '13 at 09:55
  • 27
    You can set the `default` to `SUPPRESS`: `default=argparse.SUPPRESS`. Note that in that case no attribute will be added to the namespace result if that argument was omitted, see [the `default` documentation](http://docs.python.org/2/library/argparse.html#default). – Martijn Pieters Jun 07 '13 at 10:18
  • 10
    Note that you need to specify this for each subparser created as well. – KomodoDave Nov 19 '14 at 10:59
  • I just get, e.g. optional arguments: -h, --help show this help message and exit --dataset DATASET ............. instead of --dataset DATASET (default: "MNIST") – capybaralet Apr 07 '16 at 19:46
  • 1
    Then create a new question, including a [mcve] that demonstrates the problem. As I said, it works for me with Python 2.7. – Martijn Pieters Apr 07 '16 at 20:00
  • This only works for optional args, not positional args. – SomeGuyOnAComputer Apr 16 '18 at 01:25
  • @SomeGuyOnAComputer that’s because positional args don’t have their own help text. Only their metavar is used, in the generated *usage* line. Your only options are to supply your own `usage`, `description` or `epilog` parameters with the defaults manually included (because there could be multiple such values for multiple options, `argparse` can't do this for you). – Martijn Pieters Apr 16 '18 at 05:48
  • 2
    What do I do if I've already set a `formatter_class`? – Azor Ahai -him- Jan 27 '21 at 21:34
  • @AzorAhai: depends. What formatter class did you set? – Martijn Pieters Jan 30 '21 at 13:12
  • @MartijnPieters `RawTextHelpFormatter` – Azor Ahai -him- Jan 31 '21 at 18:14
  • 7
    @AzorAhai-him- create a new class that subclasses both and use that as the formatter; e.g. `class RawTextArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter, argparse.ArgumentDefaultsHelpFormatter): pass`, `parser = argparse.ArgumentParser(..., formatter_class=RawTextArgumentDefaultsHelpFormatter)`. The two variants override two different aspects of the base help formatter and so can be combined trivially. – Martijn Pieters Feb 01 '21 at 00:04
286

Add '%(default)s' to the help parameter to control what is displayed.

parser.add_argument("--type", default="toto", choices=["toto","titi"],
                              help = "type (default: %(default)s)")

Notes:

  • It is %+ default in parenthesis + format characters (not to be confused with curly brackets {default} we find in format or f-string)
  • Don't forget to add the "specifier character" for the type representation at the end (i.e. s for strings, d for integers, f for floats, etc.)
  • You can also add the usual "printf" format specifiers (like number of digits for floats, leading zeros, etc.)

You can refer to printf documentation for more details.

Jean-Francois T.
  • 11,549
  • 7
  • 68
  • 107
polux.moon
  • 2,885
  • 1
  • 12
  • 2
  • 19
    I like this option because I'd already used the format_class=argparse.RawTestHelpFormatter and didn't feel like farting around with OOP. – mqsoh Nov 14 '13 at 18:37
  • 31
    Don't forget to include the variable 'type' in your formatting string-- e.g. '%(default)s' for a string, or '%(default)d' for a digit. – strongMA Jan 23 '14 at 22:26
  • 6
    I like this solution much better, it's far simpler, and I don't have to explicitly handle arguments with no default values. – void.pointer Aug 22 '18 at 14:06
  • @mqsoh multiple inheritance actually just worked, but unfortunately is not public API: https://stackoverflow.com/a/52025430/895245 – Ciro Santilli OurBigBook.com Aug 26 '18 at 10:41
  • 1
    I like this because changing the formatter class adds a bunch of "(default: None)" everywhere which is cluttering the help. – Caleb Stanford Aug 22 '19 at 03:39
  • Is there a Python3-esque way of doing this? `str.format` won't work, because named arguments like `{default}` got removed in 3.8. – Noldorin Jul 11 '20 at 23:41
  • @Noldorin It works perfectly on my 3.8.1 install. Are you sure you used `%(default)s` and not `{default}`? (i.e. with parenthesis and not curly brackets) – Jean-Francois T. Feb 22 '21 at 06:23
  • I set `help=" "`, and it shows the default – Neil McGuigan Feb 26 '22 at 20:20
  • I used `help="%(type)s (default: %(default)s)"` (with `%(type)s` rather than just `type`), and it seems like argparse is happy to fill in your type as well – bitoffdev Aug 22 '23 at 14:25
22

Wrapper class

This is the most reliable and DRY approach I've found so far to both show defaults and use another formatter such as argparse.RawTextHelpFormatter at the same time:

#!/usr/bin/env python3

import argparse

class ArgumentParserWithDefaults(argparse.ArgumentParser):
    def add_argument(self, *args, help=None, default=None, **kwargs):
        if help is not None:
            kwargs['help'] = help
        if default is not None and args[0] != '-h':
            kwargs['default'] = default
            if help is not None:
                kwargs['help'] += ' Default: {}'.format(default)
        super().add_argument(*args, **kwargs)

parser = ArgumentParserWithDefaults(
    formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument('-a', default=13, help='''my help
for a''')
parser.add_argument('-b', default=42, help='''my help
for b''')
parser.add_argument('--no-default', help='''my help
for no-default''')
parser.add_argument('--no-help', default=101)

parser.print_help()
print()
print(parser.parse_args())

Output:

usage: main.py [-h] [-a A] [-b B] [--no-default NO_DEFAULT]
               [--no-help NO_HELP]

optional arguments:
  -h, --help            show this help message and exit
  -a A                  my help
                        for a Default: 13
  -b B                  my help
                        for b Default: 42
  --no-default NO_DEFAULT
                        my help
                        for no-default
  --no-help NO_HELP

Namespace(a=13, b=42, no_default=None, no_help=101)

ArgumentDefaultsHelpFormatter + RawTextHelpFormatter multiple inheritance

Multiple inheritance just works, but it does not seem to be public API:

#!/usr/bin/env python3

import argparse

class RawTextArgumentDefaultsHelpFormatter(
    argparse.ArgumentDefaultsHelpFormatter,
    argparse.RawTextHelpFormatter
):
    pass

parser = argparse.ArgumentParser(
    formatter_class=RawTextArgumentDefaultsHelpFormatter
)
parser.add_argument('-a', default=13, help='''my help
for a''')
parser.add_argument('-b', default=42, help='''my help
for b''')
parser.print_help()

Output:

usage: a.py [-h] [-a A] [-b B]

optional arguments:
  -h, --help  show this help message and exit
  -a A        my help
              for a (default: 13)
  -b B        my help
              for b (default: 42)

It just works works because as we can see trivially from the sources https://github.com/python/cpython/blob/v3.6.5/Lib/argparse.py#L648 that:

  • RawTextHelpFormatter implements _split_lines
  • ArgumentDefaultsHelpFormatter implements _get_help_string

so we can guess that they will work together just fine.

However, this does not seem to be public API, and neither are the methods of formatter_class, so I don't think there is a public API way to do it currently. argparse docstring says:

All other classes in this module are considered implementation details. (Also note that HelpFormatter and RawDescriptionHelpFormatter are only considered public as object names -- the API of the formatter objects is still considered an implementation detail.)

See also: Customize argparse help message

Tested on Python 3.6.5.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
6

It is often useful to be able to automatically include the default values in the help output, but only those that were explicitly specified (with default=..). The methods already mentioned have some shortcomings in this respect:

  • The ArgumentDefaultsHelpFormatter method prints out (default: None) for every argument whose default was not explicitly specified, and (default: False) for 'flags' (action='store_true'). This clutters the help output. To avoid it, default=argparse.SUPPRESS needs to be manually added for each such argument.

  • The '%(default)s' method requires manually adding it to all the arguments' help strings that we do want printed in help.

Both methods end up needing manual intervention to print out only the "right" defaults. One way to do this automatically is to augment the ArgumentDefaultsHelpFormatter to ignore the Nones and Falses default values:

class ExplicitDefaultsHelpFormatter(argparse.ArgumentDefaultsHelpFormatter):
    def _get_help_string(self, action):
        if action.default in (None, False):
            return action.help
        return super()._get_help_string(action)

Use it in place of ArgumentDefaultsHelpFormatter:

parser = argparse.ArgumentParser(
        formatter_class=ExplicitDefaultsHelpFormatter
                )

This will print only the explicitly set default values in the help output.

Note: if an argument's default was explicitly set as None or False, it won't be shown in help with this class; add %(default)s string to help for that argument if you want it in the help output.

exquo
  • 65
  • 1
  • 4
  • 1
    This does not display the default for arguments with default=0. Alternative: `if action.default is None or action.default is False`. – sfinkens Jul 20 '22 at 13:46