Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I set an Argparse argument's default value to a positional argument's value?

I have a python script that sends a GET request. It uses Argparse to take three arguments:

  1. Address: where to send the GET request
  2. Host: the host to declare in the GET request
  3. 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()
like image 873
deau Avatar asked Aug 25 '14 12:08

deau


People also ask

Do you have to declare positional arguments in argparse?

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?

What is the default name of an argumentparser object?

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.

What is the default value of--color argument in Python argparse?

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.

What is the difference between parse_ARGs and argumentparser?

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.


3 Answers

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.

like image 111
hpaulj Avatar answered Oct 06 '22 15:10

hpaulj


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
like image 23
Joohwan Avatar answered Oct 06 '22 16:10

Joohwan


You have two reasonable choices here, and which you choose depends on how attached you are to using positional arguments as opposed to --options.

  1. 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.

  2. 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.

like image 1
wim Avatar answered Oct 06 '22 16:10

wim