How do I set an Argparse argument's default value to a positional argument's value?
Solution 1
nargs='?'
handles this situation nicely. If there are only 2 strings, they get assigned to host
and resource
, and address
gets its default (None
). If 3 strings, they get assigned to all 3. If it helps, think of the behavior of ?
in a re
pattern.
It is easy to assign the host
value to address
after the parser is done. No point in trying to do that assignment within parse_args
(since the host value won't be known yet).
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("address", nargs='?')
parser.add_argument("host")
parser.add_argument("resource")
args = parser.parse_args()
if args.address is None:
args.address = args.host
print(args)
and the usage is:
usage: get.py [-h] [address] host resource
with the []
neatly marking the optional positional argument.
Solution 2
If you are trying to achieve the behaviour you described using ONLY positional arguments you can use a list argument (nargs) like this:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("args", nargs="+")
parsed = parser.parse_args()
args = parsed.args
if len(args) == 3:
ip, host, address = args
print ip, host, address
elif len(args) == 2:
ip, host, address = args[0], args[0], args[1]
print ip, host, address
else:
print "Invalid args"
But not only is this hacky you also lose the benefits argparse provides (you have to manually verify the arguments). I recommend you use optional arguments. Perhaps like this:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-host", required=True)
parser.add_argument("-res", required=True)
parser.add_argument("-ip", required=False)
args = parser.parse_args()
ip = args.ip if args.ip else args.host
print args.host, args.res, ip
And execute it like this:
python2.7 test.py -host hello -res world
Solution 3
You have two reasonable choices here, and which you choose depends on how attached you are to using positional arguments as opposed to --options.
Add a positional argument with
nargs='+'
. After parsing the args, check the length of that list. If it was 3, set them to address, host, and resource. If it was 2, set them to host and resource, and copy the address from host. Otherwise, print a usage message and raise an exception.Leave the host and resource as positional arguments. Change the address into an option. This means the CLI will instead be like:
python get.py stackoverflow.com /questions/ask --address 198.252.206.16
You can set the default value to None
and then check if it is none after parsing the args, I don't think there is anyway in argparse to set it to automatically default to another positional arg.
Which you choose is personal preference, I like 2 better (optional arguments should be, by definition, options) because it is fitting the available tool more nicely - if you bend argparse to behave like 1 then you will have to additionally override the --help
option, the usage message, etc because the autogenerated defaults will be misleading.
deau
Updated on June 26, 2022Comments
-
deau almost 2 years
I have a python script that sends a GET request. It uses Argparse to take three arguments:
- Address: where to send the GET request
- Host: the host to declare in the GET request
- Resource: which resource is being requested
An example usage might be:
$ python get.py 198.252.206.16 stackoverflow.com /questions/ask
In most cases, however, only the host and the resource need to be given as the host will resolve to the address:
$ host -t a stackoverflow.com stackoverflow.com has address 198.252.206.16
So desired usage might be:
$ python get.py stackoverflow.com /questions/ask
How do I set up Argparse so that the default value of the Address argument is the value of the Host argument?
I've been asked to show the code that currently parses the arguments. Here it is:
import argparse parser = argparse.ArgumentParser(description= 'Send a GET request and obtain the HTTP response header and/or body.') parser.add_argument("-v", "--verbose", help="Turn on verbose mode.", action="store_true") parser.add_argument("-p", "--port", type=int, default=80, help="Which port to use when sending the GET request." parser.add_argument("address", help="Where to send the GET request to.") parser.add_argument("host", help="Which Host to declare in the GET request.") parser.add_argument("resource", help="Which resource to request.") parser.parse_args()
-
deau over 9 yearsI agree, the address is an optional argument and a better man than me would submit to this logic; so I must +1; call me crazy though, 'cause I can't help but prefer @hpaulj's solution.
-
deau over 9 yearsWhilst I like the use of the clear
required
flag I think the result is a more cluttered command line than necessary. Thank you, though; it was a good idea. -
deau over 9 yearsThank you; not quite as logical as @wim's solution but a more elegant result; one which I'll be implementing :-)
-
wim over 9 yearsActually, I prefer the solution of hpaulj too. I didn't know about this usage of
nargs='?'
before. -
zyy over 4 yearsI set a default with
ap.add_argument('mode', type = int, default = 1, help = message)
and it seems like only when I addednargs = '?'
that the default will be taken in.