Large CLIs need readable help sections and must reject combinations like --quiet --verbose.
Argument groups only affect help output. g = parser.add_argument_group("output", "description") creates a titled section; arguments added with g.add_argument(...) are listed under it instead of the default "options" heading. Parsing is unchanged.
Mutually exclusive groups affect parsing: at most one of their arguments may be given.
mx = parser.add_mutually_exclusive_group()
mx.add_argument("--quiet", action="store_true")
mx.add_argument("--verbose", action="store_true")Supplying both gives argument --verbose: not allowed with argument --quiet. Pass required=True to demand exactly one. Usage shows the group as [--quiet | --verbose].
You can nest a mutually exclusive group inside an argument group to get both a heading and exclusivity: group.add_mutually_exclusive_group(). Other nesting (groups inside mutually exclusive groups) is deprecated.
Mutually exclusive groups only handle "pick one". For richer rules ("--key requires --cert", "at least one of A or B") check after parsing and call parser.error("..."), which prints usage and exits with status 2 exactly like built-in errors, so your custom checks feel native.
Example
import argparse
p = argparse.ArgumentParser(prog="sync")
mx = p.add_mutually_exclusive_group()
mx.add_argument("-q", "--quiet", action="store_true")
mx.add_argument("-v", "--verbose", action="store_true")
net = p.add_argument_group("network", "connection settings")
net.add_argument("--cert")
net.add_argument("--key")
for argv in (["-v"], ["-q", "-v"], ["--key", "k.pem"]):
try:
a = p.parse_args(argv)
if a.key and not a.cert:
p.error("--key requires --cert")
print("ok", argv)
except SystemExit as e:
print("exit", e.code, argv)
# ok ['-v']
# sync: error: argument -v/--verbose: not allowed with argument -q/--quiet
# exit 2 ['-q', '-v']
# sync: error: --key requires --cert
# exit 2 ['--key', 'k.pem']Remember
- Argument groups only change help layout
- Mutually exclusive groups reject combinations; required=True demands one
- Use parser.error() for custom cross-argument rules
Watch out for
- Expecting add_argument_group to enforce anything; it is purely cosmetic.