Search code examples
pythonargparse

Using argparse for mandatory argument without prefix


I'm trying to use argparse module within my python application. My application should be run with single mandatory argument without any prefixes. I could not come up with a way to do it.


Solution

  • Here is a simple example using argparse to require exactly one integer argument:

    import argparse
    
    parser = argparse.ArgumentParser(description='process an integer')
    parser.add_argument('integer', metavar='N', type=int, nargs=1,
                   help='an integer')
    args = parser.parse_args()
    print(args.integer)
    

    Saving this code in argparse1.py and running it gives:

    $ python argparse1.py 5
    [5]  
    
    $ python argparse1.py
    usage: argparse1.py [-h] N
    argpars1.py: error: the following arguments are required: N
    
    $ python argparse1.py 5 7
    usage: argparse1.py [-h] N
    argparse1.py: error: unrecognized arguments: 7
    
    $ python argparse1.py test
    usage: argparse1.py N
    argparse1.py: error: argument N: invalid int value: 'test'
    
    $ python argparse1.py -h
    usage: argparse1.py [-h] N
    
    process an integer
    
    positional arguments:
      N           an integer
    
    optional arguments:
      -h, --help  show this help message and exit
    

    To remove the optional arguments define ArgumentParser with add_help=False as follows:

    import argparse
    
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument('integer', metavar='N', type=int, nargs=1)
    args = parser.parse_args()
    print(args.integer)
    

    Putting this code in argparse2.py and testing it gives:

    $ python argparse2.py
    usage: argparse2.py N
    argparse2.py: error: the following arguments are required: N
    
    $ python argparse2.py 5
    [5]
    
    $ python argparse2.py 5 7
    usage: argparse2.py N
    argparse2.py: error: unrecognized arguments: 7
    
    $ python argparse2.py hello
    usage: argparse2.py N
    argparse2.py: error: argument N: invalid int value: 'hello'
    
    $ python argparse2.py -h
    usage: argparse2.py N
    argparse2.py: error: the following arguments are required: N
    

    All usage messages can be suppressed by importing argparse.SUPPRESS and configuring ArgumentParser with usage=SUPPRESS as follows:

    from argparse import ArgumentParser,SUPPRESS
    
    parser = ArgumentParser(add_help=False, usage=SUPPRESS)
    parser.add_argument('integer', metavar='N', type=int, nargs=1)
    args = parser.parse_args()
    print(args.integer)
    

    Saving this code in argparse3.py and running it gives:

    $ python argparse3.py 5
    [5]
    
    $ python argparse3.py 5 7 9
    argparse3.py: error: unrecognized arguments: 7 9
    
    $ python argparse3.py -h
    argparse3.py: error: the following arguments are required: N
    

    The "unrecognized arguments" error is hardcoded in ArgumentParser.parse_args() (https://hg.python.org/cpython/file/3.4/Lib/argparse.py#l1727) and the "following arguments are required" error is separately hardcoded (https://hg.python.org/cpython/file/3.4/Lib/argparse.py#l1994) both without recourse to modification from the API. The latter appears to be in _parse_known_args(self, arg_strings, namespace) that I suppose could be overridden but would involve copying about 250 lines of code in order to modify 1 line to change that error message.

    However, it is possible to avoid these error messages by adding just one argument with nargs='*' and no type, which effectively allows all command lines, and then adding custom error handling of arguments including your own error and usage messages. For example:

    import os
    import sys
    from argparse import ArgumentParser,SUPPRESS
    
    script =  os.path.basename(sys.argv[0])
    usage = 'usage: ' + script + ' n (int)'
    
    parser = ArgumentParser(add_help=False, usage=SUPPRESS)
    parser.add_argument('integer', nargs='*')
    args = parser.parse_args()
    
    if (len(args.integer) == 0):
        print('error: no argument provided\n', usage, file=sys.stderr, sep = '')
        sys.exit(1)
    
    
    if (len(args.integer) > 1):
        print('error: too many arguments\n', usage, file=sys.stderr, sep = '')
        sys.exit(1)
    
    v = args.integer[0]
    
    try:
        v = int(v)
    except ValueError:
        print("error: '", v, "'", ' is not an integer\n', usage, file=sys.stderr, sep='')
        sys.exit(1)
    
    print(v + 2)
    

    Putting this code in argparse4.py and testing it gives:

    $ python argparse4.py 5
    7
    
    $ python argparse4.py
    error: no argument provided
    usage: argparse4.py n (int)
    
    $ python argparse4.py 5 9
    error: too many arguments
    usage: argparse4.py n (int)
    
    $ python argparse4.py hello
    error: 'hello' is not an integer
    usage: argparse4.py n (int)