Python argparse default values not working - python

I'm experimenting with argparse, the program works, but default values don't work. Here's my code:
'''This simple program helps you in understanding how to feed the user input from command line and to show help on passing invalid argument.'''
import argparse
import sys
def calc(args):
#Enable variables within the function to take on values from 'args' object.
operation = args.operation
x = args.x
y = args.y
if (operation == "add"):
return x + y
elif (operation == "sub"):
return x - y
parser = argparse.ArgumentParser(description="This is a summing program") #parser is an object of the class Argument Parser.
parser.add_argument("x", type=float, default=1.0, help="What is the first number?") #add_argument is a method of the class ArgumentParser.
parser.add_argument("y", type=float, default=1.0, help='What is the second number?')
parser.add_argument("operation", type=str, default="add", help='What operation? Can choose add, sub, mul, or div')
args = parser.parse_args()
print(str(calc(args)))
This simple program work, however attempting to call it without values returns the following error:
usage: cmdline.py [-h] x y operation
cmdline.py: error: the following arguments are required: x, y, operation
Where am I going wrong?

You are missing nargs='?'. The following works:
import argparse
import sys
def calc(args):
#Enable variables within the function to take on values from 'args' object.
operation = args.operation
x = args.x
y = args.y
if (operation == "add"):
return x + y
elif (operation == "sub"):
return x - y
parser = argparse.ArgumentParser(description="This is a summing program") #parser is an object of the class Argument Parser.
parser.add_argument("x", nargs='?', type=float, default=1.0, help="What is the first number?") #add_argument is a method of the class ArgumentParser.
parser.add_argument("y", nargs='?', type=float, default=1.0, help='What is the second number?')
parser.add_argument("operation", nargs='?', type=str, default="add", help='What operation? Can choose add, sub, mul, or div')
args = parser.parse_args()
print(str(calc(args)))

Change these lines to indicate that you want named, optional command-line arguments (so "-x" not "x"):
parser.add_argument("-x", type=float, default=1.0, help="What is the first number?") #add_argument is a method of the class ArgumentParser.
parser.add_argument("-y", type=float, default=1.0, help='What is the second number?')

#jbcoe - I think you have a few typoes in your code, but thank you, it works! Here's the solution, cleaned up:
'''This simple program helps you in understanding how to feed the user input from command line and to show help on passing invalid argument. It uses the argparse module.'''
import argparse
def calc(args):
#Enable variables within the function to take on values from 'args' object.
operation = args.operation
x = args.x
y = args.y
if (operation == "add"):
return x + y
elif (operation == "sub"):
return x - y
parser = argparse.ArgumentParser(description="This is a summing program") #parser is an object of the class Argument Parser.
parser.add_argument("x", nargs='?', type=float, default=1.0, help="What is the first number?") #add_argument is a method of the class ArgumentParser.
parser.add_argument("y", nargs='?', type=float, default=2.0, help='What is the second number?')
parser.add_argument("operation", nargs='?', type=str, default="add", help='What operation? Can choose add, sub, mul, or div')
args = parser.parse_args()
print(str(calc(args)))

Related

How do I use/get the list element out of the namespace?

I'm trying to write a Python program that will take given list and return the average.
#program name: average.py, author: Leo
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-lst", nargs='+', type=int, required=True)
xyz = parser.parse_args()
def GetAvg(xyz):
total = 0
for i in xyz:
total = total + i
finalAvg = total / len(xyz)
return finalAvg
if __name__ == "__main__":
GetAvg(xyz)
When I run it in my cmd prompt I run it as
python average.py -lst 4 5 7 3 2
However, I always get the error message: 'Namespace' object is not iterable.
When I do a print(xyz) it returns "Namespace(lst=[4, 5, 7, 3, 2])".
So my question is:
How do I get this function to use the list within the namespace? -or- Do I use some other argparse function to accomplish this?
I apologize for any incorrect syntax or styling, first post on StackOverflow.
The parser returns the namespace with all arguments, you have to access the specific argument. Here is your program with a few commented changes:
import argparse
def get_avg(xyz): # use pep8-style names (get_avg instead of GetAvg)
total = sum(xyz) # use predefined Python functions
return total / len(xyz)
if __name__ == "__main__":
# put all of the main program here so that it is not executed
# if the function is called from elsewhere
parser = argparse.ArgumentParser()
parser.add_argument("-lst", nargs='+', type=int, required=True)
xyz = parser.parse_args().lst # access the needed argument
print(get_avg(xyz))

Make functions importable when using argparse

I have the following CLI program which adds two numbers:
import argparse
def foo(args):
print('X + Y:', args.x + args.y)
return args.x + args.y
if __name__ == '__main__':
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
foo_parser = subparsers.add_parser('foo')
foo_parser.add_argument('-x', type=int, default=1)
foo_parser.add_argument('-y', type=int, default=2)
foo_parser.set_defaults(func=foo)
parser.add_argument('--debug', action='store_true', default=False)
args = parser.parse_args()
args.func(args)
Suppose now I want my users to also be able to import foo and call it directly with arguments x and y. I.e. I want foo to look like this:
def foo(x, y):
print('X + Y:', x + y)
return x + y
How can I adapt args.func(args) to handle this new foo?
The use of args.func(**vars(args)) is not a best fit for use-cases which require import as well as a CLI view
When users import a function and call it, they expect a return value for further processing (nothing printed on a console. The caller can decide to print based on the result obtained)
When they use a CLI, they expect to see an output printed on the console and a proper exit code (0 or 1 based on the return value)
The ideal way is to separate the parsing/CLI management/sum-function into separate functions and delegate processing once the parsing is complete (below is a sample example)
from __future__ import print_function # for python2
import argparse
def mysum(x, y=5):
return x+y
def delegator(input_args):
func_map = {
"mysum" : {
"func" : mysum,
"args": (input_args.get("x"),),
"kwargs" : {
"y" : input_args.get("y")
},
"result_renderer": print
}
}
func_data = func_map.get(input_args.get("action_to_perform"))
func = func_data.get("func")
args = func_data.get("args")
kwargs = func_data.get("kwargs")
renderer = func_data.get("result_renderer")
renderer(func(*args, **kwargs))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="action_to_perform")
foo_parser = subparsers.add_parser('mysum')
foo_parser.add_argument('-x', metavar='PATH', type=int, default=1)
foo_parser.add_argument('-y', metavar='PATH', type=int, default=3)
delegator(vars(parser.parse_args()))
Above example would also remove *args, **kwargs from your original function and lets the delegator send only what is needed by the function based on the command
You can extend it to support multiple commands
Hope this helps!
This is the cleanest way I've found so far:
import argparse
def foo(*, x, y, **kwargs):
print('X + Y:', x + y)
return x + y
if __name__ == '__main__':
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
foo_parser = subparsers.add_parser('foo')
foo_parser.add_argument('-x', metavar='PATH', type=int, default=1)
foo_parser.add_argument('-y', metavar='PATH', type=int, default=2)
foo_parser.set_defaults(func=foo)
parser.add_argument('--debug', action='store_true', default=False)
args = parser.parse_args()
args.func(**vars(args))
Maybe someone else can find something better.

how to apply options for the functions using argparse

I have a functions like this
def add(x,y):
print x+y
def square(a):
print a**2
Now I am defining linux commands(options) for this functions using argparse.
I tried with this code
import argparse
# Create Parser and Subparser
parser = argparse.ArgumentParser(description="Example ArgumentParser")
subparser = parser.add_subparsers(help="commands")
# Make Subparsers
add_parser = subparser.add_parser('--add', help="add func")
add_parser.add_argument("x",type=float,help='first number')
add_parser.add_argument("y",type=float,help='second number')
add_parser.set_defaults(func='add')
square_parser = subparser.add_parser('--square', help="square func")
square_parser.add_argument("a",type=float,help='number to square')
square_parser.set_defaults(func='square')
args = parser.parse_args()
def add(x,y):
print x + y
def square(a):
print a**2
if args.func == '--add':
add(args.x,args.y)
if args.func == '--square':
square(args.a)
But I am getting error while passing command as python code.py --add 2 3
invalid choice: '2' (choose from '--add', '--square')
--add is the form of an optionals flag, add is the correct form for a subparser name
import argparse
# Create Parser and Subparser
parser = argparse.ArgumentParser(description="Example ArgumentParser")
subparser = parser.add_subparsers(dest='cmd', help="commands")
# Make Subparsers
add_parser = subparser.add_parser('add', help="add func")
add_parser.add_argument("x",type=float,help='first number')
add_parser.add_argument("y",type=float,help='second number')
add_parser.set_defaults(func='add')
square_parser = subparser.add_parser('square', help="square func")
square_parser.add_argument("a",type=float,help='number to square')
square_parser.set_defaults(func='square')
args = parser.parse_args()
print(args)
def add(x,y):
print x + y
def square(a):
print a**2
if args.func == 'add': # if args.cmd=='add': also works
add(args.x,args.y)
if args.func == 'square':
square(args.a)
producing
0950:~/mypy$ python stack43557510.py add 2 3
Namespace(cmd='add', func='add', x=2.0, y=3.0)
5.0
I added dest='cmd' to the add_subparsers command, and print(args) to give more information. Note that the subparser name is now available as args.cmd. So you don't need the added func.
However the argparse docs do suggest an alternative use of set_defaults
https://docs.python.org/3/library/argparse.html#sub-commands
add_parser.set_defaults(func=add)
With this args.func is actually a function object, not just a string name. So it can be used as
args.func(args)
Note that I had to change how the functions handle their parameters:
def add(args):
print(args.x + args.y)
def square(args):
print(args.a**2)
# Create Parser and Subparser
parser = argparse.ArgumentParser(description="Example ArgumentParser")
subparser = parser.add_subparsers(dest='cmd', help="commands")
# Make Subparsers
add_parser = subparser.add_parser('add', help="add func")
add_parser.add_argument("x",type=float,help='first number')
add_parser.add_argument("y",type=float,help='second number')
add_parser.set_defaults(func=add)
square_parser = subparser.add_parser('square', help="square func")
square_parser.add_argument("a",type=float,help='number to square')
square_parser.set_defaults(func=square)
args = parser.parse_args()
print(args)
args.func(args)
producing
1001:~/mypy$ python stack43557510.py add 2 3
Namespace(cmd='add', func=<function add at 0xb73fd224>, x=2.0, y=3.0)
5.0

Find multiple arguments in getopt command Python 3

I need the script to look into the arguments given in command line and give an error output if two specific arguments are given in the same command line.
Please note that parameters b & c are mutually exclusive.
I need to have a way that if in the command line both -b & -c is given, the system will provide an error message and exit. Also if there any other way to write the code?
Thanks, NH
My sample code is like this:
import getopt
def main():
x = ''
try:
opts, args = getopt.getopt(sys.argv[1:], "habc",["help","Task_a", "Task_b", "Task_c"])
except getopt.GetoptError:
print("Wrong Parameter")
sys.exit()
for opt, args in opts:
if opt in ("-h", "--help"):
x = "h"
elif opt in ("-a", "--Task_a"):
x= "a"
elif opt in ("-b", "--Task_b"):
x = "b"
elif opt in ("-c", "--Task_c"):
x = "c"
else:
x = "something Else"
return x
if __name__ =="main":
main()
print(main())
First of all, you should use argparse module that support mutual exclusion.
To answer your question, you could use this simple logic
optnames = [opt[0] for opt in opts]
if (("-b" in optnames or "--Task-b" in optnames) and
("-c" in optnames or "--Task-c" in optnames)):
print("-b and -c are mutually exclusive", file=sys.stderr)
sys.exit()
Use argparse for that.
Here's a simple example to make it work:
parser = argparse.ArgumentParser(description='Doing some tasks')
parser.add_argument('-b', action='store_true', help="Proceed to task B")
parser.add_argument('-c', action='store_true', help="Proceed to task C")
args = parser.parse_args('-b -c'.split())
if args.b and args.c:
sys.exit()
if args.b:
# do something
if args.c:
# do something else
EDIT:
You can also use a mutually exclusive group. Thanks for suggesting shiplu.
parser = argparse.ArgumentParser(description='Doing some tasks')
group = parser.add_mutually_exclusive_group()
group.add_argument('-b', action='store_true', help="Proceed to task B")
group.add_argument('-c', action='store_true', help="Proceed to task C")
And then when you try to enter both of the arguments:
In [80]: args=parser.parse_args('-b -c'.split())
usage: ipython [-h] [-b | -c]
ipython: error: argument -c: not allowed with argument -b
An exception has occurred, use %tb to see the full traceback.
SystemExit: 2
Otherwise,
In [82]: parser.parse_args('-b'.split())
Out[82]: Namespace(b=True, c=False)
In [83]: parser.parse_args('-c'.split())
Out[83]: Namespace(b=False, c=True)

Is there a pythonic way of assigning values to variables when passed in from the command line?

I have written to following code but it feels very clunky and I was wondering if there was a pythonic way of writing the following code:
import argparse
foo = 0
bar = 1
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--foo", type=int,
help="foo")
parser.add_argument("-b", "--bar", type=int,
help="bar")
args = parser.parse_args()
if args.foo: # This is the bit that I think is clunky
foo = args.foo #
if args.bar: #
bar = args.bar #
In my code I have about 7 different arguments and having a list of if statements doesn't seem like the best method. Is there a better way of writing this section?
argparse have default arguments, so there is no need for the ifs. Also, you should seperate argument parsing and processing, therefore you don't need local variables for your args, but can use them as parameters. So you would finally get to something like this:
import argparse
def some_function(foo, bar):
pass
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--foo", type=int, default=0,
help="foo")
parser.add_argument("-b", "--bar", type=int, default=1,
help="bar")
args = parser.parse_args()
some_function(args.foo, args.bar)
if __name__ == '__main__':
main()
In argsparse there are default and required fields that can help you reduce the amount of if's you have.
Example:
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--foo", type=int, required=True,
help="foo")
parser.add_argument("-b", "--bar", type=int, default=42,
help="bar")
You can also return the args object, and accessing the args at a later point.

Categories

Resources