pythonpython-3.xargumentsargparsesubcommand

How to have a specific sub-command require flag with argparse?


so I am writing a python script that uses argparse in order to pass information and process input. I have been using sub-commands to force/use different operations in the program. So far my script is formatted like so:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse

def main(username, password, api, apiId):
    print("Hello, world!")

def _cli():
    # Create parser to obtain arguments
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter, argument_default=argparse.SUPPRESS)
    subparsers = parser.add_subparsers(dest = "api", required = True)
    base_subparser = argparse.ArgumentParser(add_help = False)
    # Shared arguments
    base_subparser.add_argument('-u', '--username', help = "Username", default = "admin")
    base_subparser.add_argument('-p', '--password', help = "Password", default = "admin")

    # Create subcommands
    create_parser = subparsers.add_parser("create", parents = [base_subparser])
    update_parser = subparsers.add_parser("update", parents = [base_subparser])

    # Flag for sub-parser to pass API ID
    update_parser.add_argument('-i', '--apiId', help = "ID for API you are trying to access.", required = True)

    # Obtain all the argument values
    args = parser.parse_args()

    # Return them in a dictionary format
    return vars(args)

if __name__ == '__main__':
    # Pass arguments to main function using dictonary format.
    main(**_cli())

Where usage is specified as such:

usage: test.py [-h] {create,update} ...

positional arguments: {create, update}

optional arguments: -h, --help show this help message and exit

My issue is that while the update command is working fine, where I am required to pass an ID, my create has a bug/issue where it complains about not setting the flag/passing one even though it is not required/needed.

main(**_cli())
TypeError: main() missing 1 required positional argument: 'apiId'

What am I doing wrong here and how do I fix it? I've tried to google but I can't seem to understand the problem/how to resolve it.


Solution

  • The error you are getting is telling you that apiId is not being provided when you are using the create subcommand. You can either specify a default parameter, like so:

    def main(username, password, api, apiId=None):
        print("API: {}".format(api))
        print("API ID: {}".format(apiId))
    
    ~ python args.py create -u a -p b
    API: create
    API ID: None
    ~ python args.py update -u a -p b -i c
    API: update
    API ID: c
    

    or you could get a dict of keyword arguments using ** expansion in the function definition:

    def main(**kwds):
        print(kwds)
    
    ~ python args.py create -u a -p b
    {'api': 'create', 'username': 'a', 'password': 'b'}
    ~ python args.py update -u a -p b -i c
    {'api': 'update', 'username': 'a', 'password': 'b', 'apiId': 'c'}