I have a python script that sends a GET request. It uses Argparse to take three arguments:
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()
With python argparse, you must declare your positional arguments explicitly. If you do not, the parser expects to have no arguments left over after it completes parsing, and it raises an error if arguments still remain. How do we define optional and positional arguments?
By default, ArgumentParser objects use the dest value as the “name” of each object. By default, for positional argument actions, the dest value is used directly, and for optional argument actions, the dest value is uppercased. So, a single positional argument with dest='bar' will be referred to as bar.
The default value would be blue if --color argument is not defined. With python argparse, you must declare your positional arguments explicitly. If you do not, the parser expects to have no arguments left over after it completes parsing, and it raises an error if arguments still remain.
In a script, parse_args () will typically be called with no arguments, and the ArgumentParser will automatically determine the command-line arguments from sys.argv. class argparse.
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With