Search code examples
pythonargparse

argparse: optional argument overwriting positional arguments


Let's say I have a python script with arguments foo [--bar baz] and want to add an optional argument/switch --extra-info that would just print some extra information and exit. (My actual use case is bit more complicated, but this illustrates the issue.)

I found the following solution, but it looks more like a hack. Basically, I am making the positional argument optional if '--extra-info' is present in the argument list:

import sys
import argparse

parser.add_argument('--extra-info', action='store_true')
pos_nargs = '?' if '--extra-info' in sys.argv else None
parser.add_argument('foo', nargs=pos_nargs)
parser.add_argument('--bar')

Is there a better approach to this? I would expect there to be one, given that --help behaves exactly the way I want my switch to behave...


Solution

  • You can parse the command line in 2 passes: The first pass is to parse --extra-info, the second will parse the rest.

    #!/usr/bin/env python3
    import argparse
    import sys
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--extra-info", action="store_true")
    options, remainder = parser.parse_known_args()
    
    if options.extra_info:
        print("Show extra information")
        sys.exit()
    
    parser = argparse.ArgumentParser()
    parser.add_argument("foo")
    parser.add_argument("--bar")
    # Add here for the help text only
    parser.add_argument("--extra-info", action="store_true", help="blah")
    options = parser.parse_args(remainder)
    print(options)
    

    Some interactions:

    # Show help
    $ ./main.py 
    usage: main.py [-h] [--bar BAR] [--extra-info] foo
    main.py: error: the following arguments are required: foo
    
    # Show extra info
    $ ./main.py --extra-info
    Show extra information
    
    # specify foo/bar
    $ ./main.py myfoo --bar mybar
    Namespace(foo='myfoo', bar='mybar', extra_info=False)
    

    Notes

    • In the first pass, I used parse_known_args just to parse --extra-info. The remainder holds those command-line arguments which the first-pass parser does not understand. We will use this remainder in the second pass.

    • In the second pass, I added --extra-info simply for the help/usage.