0

So I have the following code:

parser = argparse.ArgumentParser(description='Manages anime_list.json', add_help=True, version='0.1')
parser.add_argument('-l', '--list', action='store_true', help='List all anime in file')

subparser = parser.add_subparsers(title='Actions', description='Actions that can be performed', dest='command')

add = subparser.add_parser('=add', help='Add anime entry')
add.add_argument('-n', '--name', type=str, required=False, default='',
                 help='The name of the anime adding. Will be auto filled in if left blank')
add.add_argument('-e', '--episode', type=int, required=False, default=0,
                 help='The last watched episode. Download starts add +1 this episode')
add.add_argument('-u', '--url', type=str, required=True, help='A link to any episode for the anime')

remove = subparser.add_parser('=remove', help='Remove anime entry')
remove.add_argument('-n', '--name', type=str, required=True, default='',
                    help='The name of the anime to remove')

args = parser.parse_args()

What I want is for the subparsers to be optional. When the user uses the --list argument, the subparsers arguments should not have to be supplied. When using argsparse's -h or -v options the parsing completes and the help information or version number is shown. But when just using my own -l it throws an exception saying that not enough arguments have been supplied.

I found a suggestion saying that using subparser.required = False should make them optional but it does not work.

Any idea how I can do this? I have looked up on this and can't seem to find a solution.

jovaee
  • 145
  • 3
  • 12
  • Slightly off-topic, but if you're okay with using a non-standard lib, you should have a look at [`docopt`](https://github.com/docopt/docopt). Definitely the most elegant way to write command-line apps. – Right leg Jul 23 '18 at 21:40

3 Answers3

1

So I have found a solution, it's not optimal in my opinion but it works.

Thanks to Matthew in this answer.

Modifying the code like follows give me the functionality I want.

parser = argparse.ArgumentParser(description='Manages anime_list.json', add_help=True, version='0.1')
parser.add_argument('-l', '--list', action='store_true', help='List all anime in file')    

args, sub_commands = parser.parse_known_args()

if args.list:
    print 'Doing list'
else:
    subparser = parser.add_subparsers(title='Actions', description='Actions that can be performed', dest='command')

    add = subparser.add_parser('=add', help='Add anime entry')
    add.add_argument('-n', '--name', type=str, required=False, default='',
                     help='The name of the anime adding. Will be auto filled in if left blank')
    add.add_argument('-e', '--episode', type=int, required=False, default=0,
                     help='The last watched episode. Download starts add +1 this episode')
    add.add_argument('-u', '--url', type=str, required=True, help='A link to any episode for the anime')

    remove = subparser.add_parser('=remove', help='Remove anime entry')
    remove.add_argument('-n', '--name', type=str, required=True, default='',
                        help='The name of the anime to remove')

    args = parser.parse_args()
    print args

return args

Basically parse the known arguments, in this case it would be the -l one. If the -l argument was not supplied, add the required subparsers and parse the arguments again.

If it is done this way, your --help will not work anymore as it will not show the subparsers' help text. You will have to create a manual help function.

Community
  • 1
  • 1
jovaee
  • 145
  • 3
  • 12
0

Throw in a default parameter. For example:

parser.add_argument('-l', '--list', action='store_true', help='List all anime in file', default=False)    

This will store a default value of False, and only when the -l flag is provided will the list variable be True.

Roman
  • 8,826
  • 10
  • 63
  • 103
0

I use the following solution, I find it quite clean, and it helps me to extend and make more specific my command line applications.

1) Define a main common and standard argparse with parent and subparsers. I do this in the main library or in a common class that I use in similar but different projects.

2) Add a subparser to escape the common argparse, something like "other" or something similar.

3) In the specific applications create a second argparser that is used when the first argument is "other".

example: let's say I want to always use/inherit some common subparsers and parent but then also add some application specific parsers. Say the standard common parser has the following subparsers: load, query, show, ..., other.

then it will work with:

python code.py load -arg1 -arg2 ...

python code.py show -arga -argb ...

while, when using "other" it will take the parent args and do nothing, so that other case specific parsers can be used.

python code.py other application_specific_command -arg3 -arg4

clearly this does not make the subparser optional, so it is not a direct answer; more a possible alternative that works in some cases. I think it offers some advantages:

1) logic implementation without many if or try 2) can pass the parent args to the application specific parser (if "other" has parents) 3) allows some common args for "other", i.e. for all the case specific commands 4) maintains args helps

Peruz
  • 403
  • 3
  • 10