Tony
Tony

Reputation: 1290

Using argparse.subparser with boolean values

I've read through the Python argparse documentation several times and must be missing something huge. I'm trying to add a makefile like syntax onto my cli tool, but despite the --help flag showing me the expected behavior the execution of it errors out:

test.py

import argparse
import os

def main():
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()

    make_subparser = subparsers.add_parser("make")
    make_subparser.add_argument("update", action="store_false", help="Runs all make targets")
    make_subparser.add_argument("clean", action="store_false", help="Cleans up everything done previously")

    args = parser.parse_args()

    if args.update:
        os.system("touch test.txt")

    elif args.clean:
        os.system("rm test.txt")

    else:
        print("Bad args, see: python test.py make --help.")


if __name__ == "__main__":
    main()

Then in my terminal:

$> python test.py make --help
usage: test.py make [-h]

positional arguments:
  update      Runs all make targets

$> python test.py make update
usage: test.py [-h] {make} ...
test.py: error: unrecognized arguments: update
$> # this should make a file
$> 
$> python test.py make clean
usage: test.py [-h] {make} ...
test.py: error: unrecognized arguments: clean
$> # this should delete the file

This works fine if I swap out the action="store_false" for action="store_true", but there's a dozen make commands so I only want to run the one that the user adds in the cli, if they try to enter any more it should exit.

Upvotes: 1

Views: 489

Answers (2)

chepner
chepner

Reputation: 530960

Instead of separate boolean flags, use single argument target which allows a restricted set of values.

make_subparser.add_argument("target", choices=['update', 'clean'], help="Runs all make targets")

if args.target == "update":
    ...
elif args.target == "clean":
    ...

No else is needed, because any attempt to pass something other than update or clean will cause an error in parse_args, before args.target ever gets set.

Upvotes: 1

Konrad Rudolph
Konrad Rudolph

Reputation: 545528

In the POSIX (and POSIX adjacent) world, boolean arguments are handled as flags which are passed via the command line as --argument. What you want isn’t boolean arguments (and that isn’t how Make treats them either), it’s simply a variable number of positional arguments, i.e. having arity *:

make_subparser.add_argument('targets', nargs='*')

This would be closest to how Make operates. However, from your command line it looks more like what you actually want is nested subcommands.

Upvotes: 1

Related Questions