I'm using argparse
to take input and pass it to a function that takes as arguments two variables and **kwargs
.
Here's my function:
import requests
import sys
import argparse
def location_by_coordinate(LAT, LNG, **kwargs):
if not kwargs:
coordinate_url = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)
r = requests.get(coordinate_url).text
else:
coordinate_url = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)
for key, value in kwargs.iteritems():
if 'DISTANCE' in kwargs:
distance = kwargs.get('DISTANCE')
if distance > 5000:
print distance
print "max distance is 5000m, value is reassigned to default of 1000m"
distance = 1000
coordinate_url = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)
r = requests.get(coordinate_url).text
else:
pass
coordinate_url = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)
r = requests.get(coordinate_url).text
if 'FACEBOOK_PLACES_ID' in kwargs:
fb_places_id = kwargs.get('FACEBOOK_PLACES_ID')
payload = {'FACEBOOK_PLACES_ID': '%s' % (fb_places_id), 'DISTANCE': '%s' % (DISTANCE)}
r = requests.get(coordinate_url, params=payload).text
if 'FOURSQUARE_ID' in kwargs:
foursquare_id = kwargs.get('FOURSQUARE_ID')
payload = {'FOURSQUARE_ID': '%s' % (foursquare_id), 'DISTANCE': '%s' % (DISTANCE)}
r = requests.get(coordinate_url, params=payload).text
if 'FOURSQUARE_V2_ID' in kwargs:
foursquare_v2_id = kwargs.get('FOURSQUARE_V2_ID')
payload = {'FOURSQUARE_V2_ID': '%s' % (foursquare_v2_id), 'DISTANCE': '%s' % (DISTANCE)}
r = requests.get(coordinate_url, params=payload).text
#print r
return r
Given this function and its use of **kwargs, how should I setup the subparsers?
Here's how I've setup the command line parser thus far:
def main():
parser = argparse.ArgumentParser(description="API Endpoints tester")
subparsers = parser.add_subparsers(dest="command", help="Available commands")
location_by_parser = subparsers.add_parser("location_by_coordinate", help="location function")
location_by_parser.add_argument("LAT", help="latitude")
location_by_parser.add_argument("LNG", help="longitude")
arguments = parser.parse_args(sys.argv[1:])
arguments = vars(arguments)
command = arguments.pop("command")
if command == "location_by_coordinate":
LAT, LNG = location_by_coordinate(**arguments)
else:
print "No command provided..."
if __name__ == "__main__":
main()
Obviously, the above main() function works fine with the location_by_coordinate() function when I call it at the command line like this:
$ python argstest.py location_by_coordinate 40.5949799 -73.9495148
But with the code the way it is currently, if I try:
$ python argstest.py location_by_coordinate 40.5949799 -73.9495148 DISTANCE=3000
Obviously, I get:
argstest.py: error: unrecognized arguments: DISTANCE=3000
But I'm not sure how to setup a subparser for **kwargs. If I try to setup a subparser like this:
location_by_parser.add_argument("**kwargs", help="**kwargs")
and then try that command again:
$ python argstest.py location_by_coordinate 40.5949799 -73.9495148 DISTANCE=3000
That doesn't work because the arguments
object (which is a dictionary), becomes this:
{'LAT': '40.5949799', 'LNG': '-73.9495148', 'command': 'location_by_coordinate', '**kwargs': 'DISTANCE=3000'
}
And this Traceback is returned:
Traceback (most recent call last):
File "argstest.py", line 118, in <module>
main()
File "argstest.py", line 108, in main
foo = location_by_coordinate(**arguments)
File "argstest.py", line 40, in location_by_coordinate
return r
UnboundLocalError: local variable 'r' referenced before assignment
How can I enable argparse to handle/to parse what is entered at the command line that is intended to be passed to the function via **kwargs?
Do you understand what is going on with the
arguments
dictionary? You defined a 'positional' argument with the name ('dest') of '**kwargs'. You could just as well named it 'foobar'. The parser assigned the string 'DISTANCE=3000' to that attribute in theargs
namespace, which turned into a dictionary key:value pair inarguments
.You could, of course, look for
arguments['**kwargs']
, and parse the value for yourself:It could be generalized to handle multiple pairs (defined with `nargs='*').
argparse
does not handle arguments the same way as Python functions, so there's nothing exactly analogous the**kwargs
.The normal way to accept something like
distance
is with 'optionals' or flagged arguments.which will accept
It could also be setup to use
--DISTANCE
or other names. In the last caseargs
namespace will have a default value fordistance
. The default default isNone
.That's the straight forward way of adding
kwarg
like arguments toargparse
.Accepting arbitrary dictionary like pairs,
distance:3000
,distance=3000
, has been asked before on SO. The answers have always been some variation of the parsing that I sketched above. It could be done in a custom Action class, or post parsing as I suggest.oops, this answer is nearly a clone of one I wrote a few days ago: https://stackoverflow.com/a/33639147/901925
A similar 2011 question: Using argparse to parse arguments of form "arg= val"
Python argparse dict arg
=================================
(edit)
Example with a function that takes
*args
:So I have 2 positional arguments, one with a single string value, the other with a list (due to the
+
nargs).Call
foo
with theseargs
attributes:I defined 'positionals', but it would have worked just as well with 'optionals'. The distinction between positionals and optionals disappears in the namespace.
If I convert the namespace to a dictionary, I can pass values to
foo
in various ways, either through the*args
or through**kwargs
. It's all in how I callfoo
, not in how they appear inargs
orarguments
. None of this is unique toargparse
.This command:
does NOT execute the function call:
That is easy to prove:
Go ahead and parse the args, and you'll see that the function isn't called. As a result, all your work setting up a subparser with the name
location_by_coordinate
was in vain.The
argparse
module just examinessys.argv
, which is a simple list of strings. Each string is one of the 'words' entered on the command line after thepython
command.Yeah,
sys.argv
is a scary name, but a list of strings is just a list of strings. If you look at the argparse docs, all the examples do this:A list of strings you create with
split()
is no different than some list of strings that sys.argv refers to.You need to call your
location_by_coordinate()
function yourself. In order to do that, you need to get the args from the command line, assemble the args that should be kwargs in a dictionary, and call your function like this:If you have these values:
then the function call above will be equivalent to:
Here is an example:
You can also get a dict from the parser:
If you want to make the user type:
on the command line, first of all I would not make them type all caps, so lets make the goal:
Add another mandatory argument to the parser:
Then after you parse the following:
you can do this:
The
args_dict
will contain the key/value pair'distance': 'distance=3000'
. You can change that dict entry to'distance': '3000'
by doing the following:Or, you can set things up so that the parser will automatically execute that code by creating a custom action that executes when the
distance
arg is parsed:You can use the action like this:
And if you want to get fancy, you can collect all the
name=val
args specified on the command line into one dictionary named, say,keyword_args
, which will allow you to call your method like this:Here's the parser configuration:
Example: