Sharing argparse arguments with subcommands

argparse subcommands are great, but they have a quirk in which options are only available right after the subcommand that define them.

So, if you for example add the --verbose / -v argument to your main parser, and you have subcommands, you need to give the -v option before the subcommand name. For example, given this script:

#!/usr/bin/python3
import argparse

parser = argparse.ArgumentParser(description="test")
parser.add_argument("-v", "--verbose", action="store_true")
subparsers = parser.add_subparsers(dest="handler", required=True)
subparsers.add_parser("test")

args = parser.parse_args()
print(args.verbose)

You get this behaviour:

$ ./mycmd test
False
$ ./mycmd -v test
True
$ ./mycmd test -v
usage: mycmd [-h] [-v] {test} ...
mycmd: error: unrecognized arguments: -v

This sometimes makes sense, and many other times it's really annoying, since the user has to remember at which level an option was defined.

Last night some pieces clicked in my head, and I created a not-too-dirty ArgumentParser subclass that adds a shared option to arguments, that propagates them to subparsers:

#!/usr/bin/python3
from hacks import argparse

parser = argparse.ArgumentParser(description="test")
parser.add_argument("-v", "--verbose", action="store_true", shared=True)
subparsers = parser.add_subparsers(dest="handler", required=True)
subparsers.add_parser("test")

args = parser.parse_args()
print(args.verbose)

And finally, -v can be given at all levels:

$ ./mycmd test
False
$ ./mycmd -v test
True
$ ./mycmd test -v
True

It even works recursively, forwarding arguments to sub-subparsers, but at the moment it can only do it for store_true and store kind of arguments.