2

In my example below, I am trying to setup the following command line scenarios:

$ myapp.py flowers [-h]
$ myapp.py flowers plants [-h]
$ myapp.py flowers plants add [-h]

The actual outcomes are:

$ python myapp.py flowers
usage: myapp.py flowers [-h] {plants} ...
myapp.py flowers: error: too few arguments
$ python myapp.py flowers plants
usage: myapp.py flowers plants [-h] [--format {plain,json}] {add} ...
myapp.py flowers plants: error: too few arguments
$ python myapp.py flowers plants add
Adding flowers...  The input format is expected to be plain

The problem is the second scenario. I intend for it to be an implicit "list" operation. How to do this?

Here is the source code to my example.

import sys
import argparse

def _flowers_plants_list(args):
    print('if this were real, the output format would be %s' % (args.format))       
    print('Here are all the flowers:')
    print('flower 1: rose')
    print('flower 2: tulip')
    print('flower 3: daisy')

def _flowers_plants_add(args):
    print('Adding flowers...  The input format is expected to be %s' % (args.format))

def main():
    # myapp.py
    parser_main = argparse.ArgumentParser()
    subparsers_main = parser_main.add_subparsers()

    # myapp.py flowers [-h]
    parser_flowers = subparsers_main.add_parser("flowers")
    subparsers_flowers = parser_flowers.add_subparsers()

    # (this is supposed to be an implicit list - NOT WORKING)
    # myapp.py flowers plants [-h]
    parser_flowers_plants = subparsers_flowers.add_parser("plants")
    parser_flowers_plants.add_argument("--format", default='plain', choices=['plain', 'json'], help="what format should the result be in")
    parser_flowers_plants.set_defaults(func=_flowers_plants_list)
    subparsers_flowers_plants = parser_flowers_plants.add_subparsers()

    # myapp.py flowers plants add [-h]
    parser_flowers_plants_add = subparsers_flowers_plants.add_parser("add")
    parser_flowers_plants_add.add_argument("--format", default='plain', choices=['plain', 'json'], help="what format will the input be in")
    parser_flowers_plants_add.set_defaults(func=_flowers_plants_add)

    args = parser_main.parse_args()
    r = args.func(args)
    return r

if __name__ == "__main__":
    sys.exit(main())
101010
  • 14,866
  • 30
  • 95
  • 172
  • In newer versions, esp. Py3, `subparsers` are optional (that's mistake that's here to stay). In older ones `subparsers` are required. It looks like that's what's happening here. The `parser_flowers_plants` parser is complaining that it can't find an argument (with `{'add'}` choices). – hpaulj May 15 '18 at 18:02
  • An earlier SO exploration of nested or multiple subcommands: https://stackoverflow.com/questions/10448200/how-to-parse-multiple-nested-sub-commands-using-python-argparse – hpaulj May 15 '18 at 21:20

1 Answers1

0

In Py3:

1420:~/mypy$ python3 stack50350537.py 
Traceback (most recent call last):
  File "stack50350537.py", line 40, in <module>
    sys.exit(main())
  File "stack50350537.py", line 36, in main
    r = args.func(args)
AttributeError: 'Namespace' object has no attribute 'func'
1421:~/mypy$ python3 stack50350537.py flowers
Traceback (most recent call last):
  File "stack50350537.py", line 40, in <module>
    sys.exit(main())
  File "stack50350537.py", line 36, in main
    r = args.func(args)
AttributeError: 'Namespace' object has no attribute 'func'
1421:~/mypy$ python3 stack50350537.py flowers plants
if this were real, the output format would be plain
Here are all the flowers:
flower 1: rose
flower 2: tulip
flower 3: daisy
1422:~/mypy$ python3 stack50350537.py flowers plants add
Adding flowers...  The input format is expected to be plain

In py2, subparsers are required:

1423:~/mypy$ python2 stack50350537.py
usage: stack50350537.py [-h] {flowers} ...
stack50350537.py: error: too few arguments
1423:~/mypy$ python2 stack50350537.py flowers
usage: stack50350537.py flowers [-h] {plants} ...
stack50350537.py flowers: error: too few arguments
1423:~/mypy$ python2 stack50350537.py flowers plants
usage: stack50350537.py flowers plants [-h] [--format {plain,json}] {add} ...
stack50350537.py flowers plants: error: too few arguments
1423:~/mypy$ python2 stack50350537.py flowers plants add
Adding flowers...  The input format is expected to be plain

In Py3, subparsers are not required. Initially that change was a bug, that resulted from a change in how required was tested. But it's been that way long enough that it is now a feature. There's a move toward giving users a bit more control over this feature, but I think the not required will remain the default.

Argparse with required subparser

hpaulj
  • 221,503
  • 14
  • 230
  • 353