简体   繁体   中英

Python argparse, Value after positional argument

So I'm writing this very small program to do http get and post requests. The requests are as follows:

requestApp.py help
requestApp.py help get
requestApp.py help post
requestApp.py get [-v] [-h key:value] URL
requestApp.py post [-v] [-h key:value] [-d inline-data] [-f file] URL

As you can see, the -v, -h, -d, -f, URL arguments are optional. The get and post arguments are non-optional. I'll show you the snippet of my program that is relevant to this situation:

parser = argparse.ArgumentParser(description='httpc is a curl-like application but supports HTTP protocol only.')
parser.add_argument('command', type=str, help=help_output())
parser.add_argument('url', action='store_true', help='The URL that will be provided to perform the requested command.')
parser.add_argument('-v', '--verbose', action='store_true')

The command argument will be help, get, or post, and the url argument is self explanatory. My question is related to the second and third commands above, namely:

requestApp.py help get
requestApp.py help post

How can I make sure that when typing help get , the get will not be registered in the URL (same for help post ). In addition, when I do include a URL, I want it to be stored inside of the URL argument. Would I have to manually evaluate the arguments passed through if statements? Or there is a better way to do it?

It is quite complicated to do this using argparse here is how to do it using docopt , docopt parses the usage pattern and returns a dictionary :

"""
Usage:
    requestApp help [get|post]
    requestApp get [-v] [-k=key:value] <URL>
    requestApp post [-v] [-k=key:value] [-d=data] [-f file] <URL>

Options:
    -v --verbose       This is verbose mode
    -d=data            This option does this
    -k=key:value       This one does that
    -f file            This one is magic
"""

from docopt import docopt

ARGS = docopt(__doc__)

For example with requestApp.py post -k hello:world -f myfile.txt google.com docopt will return:

{
  "--verbose": false, 
  "-d": None, 
  "-f": "myfile.txt", 
  "-k": "hello:world", 
  "<URL>": "google.com", 
  "get": false, 
  "help": false, 
  "post": true
}

Then you can do:

if ARGS['help']:
    if ARGS['get']: pass                # requestApp help get
    else if ARGS['post']: pass          # requestApp help post
    else: pass                          # requestApp help
    exit()

if ARGS['get']: pass                    # requestApp get
else if ARGS['post']: pass              # requestApp post

if ARGS['--verbose']: print("this is just the beginning") 

-h is a reserved option by default (for help) that makes docopt returns the usage pattern and exit. docopt will return the usage pattern to stdout and exit if you try illegal commands such as requestApp help unicorn

Perhaps the closest an argparse solution can come, at least without going the subparser route, is:

import argparse
import sys
print(sys.argv)
parser = argparse.ArgumentParser()
parser.add_argument('-k', '--keyvalue')
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('-d', '--data')
parser.add_argument('-f', '--file')
parser.add_argument('pos1', choices = ['help', 'get', 'post'])
parser.add_argument('pos2')

args = parser.parse_args()
print(args)

The resulting help is:

1744:~/mypy$ python stack54383659.py get aurl -h
['stack54383659.py', 'get', 'aurl', '-h']
usage: stack54383659.py [-h] [-k KEYVALUE] [-v] [-d DATA] [-f FILE]
                        {help,get,post} pos2

positional arguments:
  {help,get,post}
  pos2

optional arguments:
  -h, --help            show this help message and exit
  -k KEYVALUE, --keyvalue KEYVALUE
  -v, --verbose
  -d DATA, --data DATA
  -f FILE, --file FILE

The fit isn't perfect. For example you can give just help , but you can provide just -h . The 2nd positional value can be any string, 'get', a valid url or something else. Your own code will have to valid that. The key:value bit requires your own parsing.

In the argparse way of parsing the optionals can occur in any order. The two positionals have to occur in the given order (relative to each other).


In newer Pythons I can change the last positional to be 'optional', and use the new intermixed parser. That would allow me to give just 'help' (or just 'get'):

parser.add_argument('pos2', nargs='?')
args = parser.parse_intermixed_args()

intermixed is needed if the two positional values are separated by flags. For some complex reasons, the regular parsing may consume the '?' argument prematurely leaving you with an extra unrecognized string.

Another approach is to define all the flagged arguments, and use parse_known_args . The non-flag values will be in the extras list, which you can parse as you like. Older parsers like optparse did essentially that. argparse added a limited ability to handle positional arguments as well, but strictly by position, not by value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM