I want to have an argument --foobar using Python argparse, so that whenever this argument appears, the program prints a particular string and exits. I don't want to consume any other arguments, I don't want to check other arguments, nothing.
I have to call add_argument somehow, and then perhaps, from parse_args() get some information and based on that, print my string.
But even though I successfully used argparse before, I am surprised to find I have trouble with this one.
For example, none of the nargs values seem to do what I want, and none of the action values seem to fit. They mess up with the other arguments, which I want to ignore once this one is seen.
How to do it?
Use a custom action= parameter:
import argparse
class FoobarAction(argparse.Action):
def __init__(self, option_strings, dest, **kw):
self.message = kw.pop('message', 'Goodbye!')
argparse.Action.__init__(self, option_strings, dest, **kw)
self.nargs = 0
def __call__(self, parser, *args, **kw):
print self.message
parser.exit()
p = argparse.ArgumentParser()
p.add_argument('--ip', nargs=1, help='IP Address')
p.add_argument('--foobar',
action=FoobarAction,
help='Abort!')
p.add_argument('--version',
action=FoobarAction,
help='print the version number and exit!',
message='1.2.3')
args = p.parse_args()
print args
Reference: https://docs.python.org/2.7/library/argparse.html#action-classes
EDIT:
It looks like there is already an action= that does exactly what FoobarAction does. action='version' is the way to go:
import argparse
p = argparse.ArgumentParser()
p.add_argument('--foobar',
action='version',
version='Goodbye!',
help='Abort!')
args = p.parse_args()
print args
I'm just going to post this here, if it helps then great!
import argparse
parser = argparse.ArgumentParser(description='')
parser.add_argument('-foobar', '--foobar', help='Description for foobar argument',
required=False)
args = vars(parser.parse_args())
if args['foobar'] == 'yes':
foobar()
Usage:
python myscrip.py -foobar yes
Use action='store_true' (see the docs).
arg_test.py:
import argparse
import sys
p = argparse.ArgumentParser()
p.add_argument('--foobar', action='store_true')
args = p.parse_args()
if args.foobar:
print "foobar"
sys.exit()
Usage:
python arg_test.py --foobar
Result:
foobar
Related
I have a python program that takes in many arguments and run. With argparse, I can define the arguments, their default values, their explanations, and it can be used as a convenient container. So it's all good for passing arguments from command line.
But can I also use it to pass the arguments from code, for API call?
Yes, certainly argparse can be used to pass arguments both from command line and from code.
For example:
import argparse
# Define default values
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default=1, type=float, help='foo')
# Get the args container with default values
if __name__ == '__main__':
args = parser.parse_args() # get arguments from command line
else:
args = parser.parse_args('') # get default arguments
# Modify the container, add arguments, change values, etc.
args.foo = 2
args.bar = 3
# Call the program with passed arguments
program(args)
With vars() you can use your args like dictionaries.
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-f", "--foo")
parser.add_argument("-b", "--bar")
args = vars(parser.parse_args())
print(f"args foo is {args['foo']}")
print(f"args bar is {args['bar']}")
Result when you execute and parse some arguments look like.
python3 test.py --foo cat --bar dog
args foo is cat
args bar is dog
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
help="write report to FILE", metavar="FILE")
parser.add_argument("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="don't print status messages to stdout")
args = parser.parse_args()
When running this file from terminal, you can pass the arguments in the following manner:
python36 commandline_input.py 10
python36 commandline_input.py 10 --foo 12
The first positional argument is mandatory, the second is optional therefore you need a flag (--foo).
commandline_input.py:
import argparse
def main(mandatory_arg, optional_arg):
# your program
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser()
# mandatory
parser.add_argument('bar', help='Some Bar help')
# optional
parser.add_argument('-f', '--foo', default='default foo value',
help='Some foo help')
args = parser.parse_args()
# mandatory args
print(args.bar, '(man)')
# optional args
if args.foo:
print(args.foo, '(opt)')
# your API call
main(args.bar, [args.foo])
How to call a function using optparse? I do not want to use argparse.
Here is my code:
from optparse import OptionParser
def print_stuff():
a = "Hello Word"
print a
parser = OptionParser()
parser.add_option("-c",
action = "callback",
callback = print_stuff()
)
(options, args) = parser.parse_args()
But it shows me an error:
optparse.OptionError: option -c: callback not callable: None
What to do?
What if I wanted to do:
from optparse import OptionParser
def print_stuff(a):
a = "Hello Word"
return a
parser = OptionParser()
parser.add_option("-c",
action = "callback",
callback = print_stuff()
)
(options, args) = parser.parse_args()
You want
callback = print_stuff
without the parens. Including the parens invokes the function, and sets callback to the result (the string "Hello Word"), which is not callable. Without the parens, you're referring to the function itself.
The idea is to add a flag (--slack, or -s) when running the script, so that I don't have to comment out the rep.post_report_to_slack() method every time I don't want to use it. When I run:
$ python my_script.py --slack
I get the error:
my_script.py: error: unrecognized arguments: --slack
Here's the code:
def main():
gc = Google_Connection()
meetings = gc.meetings
rep = Report(meetings)
if args.slack:
rep.post_report_to_slack()
print('posted to slack')
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--slack', help='post to slack',
action='store_true')
args = parser.parse_args()
main()
Your code works, but it relies on args being available in the module namespace, which isn't great because, for one thing, it means you can't use your function without calling the script from the command line. A more flexible and conventional approach would be to write the function to accept whatever arguments it needs, and then pass everything you get from argparse to the function:
# imports should usually go at the top of the module
import argparse
def get_meeting_report(slack=False):
gc = Google_Connection()
meetings = gc.meetings
rep = Report(meetings)
if slack:
rep.post_report_to_slack()
print('posted to slack')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--slack', help='post to slack',
action='store_true')
args = parser.parse_args()
args = vars(args)
get_meeting_report(**args)
Now you can also more easily use your function outside of argparse by calling it directly.
I am interested in hook extra arguments parsed using argparse in one class to another method in another class which already has few arguments parsed using argparse module.
Project 1
def x():
parser = argparse.ArgumentParser()
parser.add_argument('--abc')
Project 2
def y():
parser = argparse.ArgumentParser()
parser.add_argument('--temp1')
parser.add_argument('--temp2')
When I run x(), I want to add the "--abc" argument to the list of argument y() has which is "temp1", "temp2" at runtime. Is inheritance the best way to go and defining the constructors accordingly ? Could someone provide some sample code snippet ?
Thanks !
argparse implements a parents feature that lets you add the arguments of one parser to another. Check the documentation. Or to adapt your case:
parser_x = argparse.ArgumentParser(add_help=False)
parser_x.add_argument('--abc')
parser_y = argparse.ArgumentParser(parents=[parser_x])
parser_y.add_argument('--temp1')
parser_y.add_argument('--temp2')
parser_y.print_help()
prints:
usage: ipython [-h] [--abc ABC] [--temp1 TEMP1] [--temp2 TEMP2]
optional arguments:
-h, --help show this help message and exit
--abc ABC
--temp1 TEMP1
--temp2 TEMP2
The add_help=False is needed to avoid a conflict between the -h that parser_x would normally add with the one that parser_y gets.
Another way is to let x add its argument to a predefined parser:
def x(parser=None):
if parser is None:
parser = argparse.ArgumentParser()
parser.add_argument('--abc')
return parser
def y():
....
return parser
parsery = y()
parserx = x(parsery)
It might also be useful to know that add_argument returns a reference to the argument (Action object) that it created.
parser = argparse.ArgumentParser()
arg1 = parser.add_argument('--abc')
Do this in a shell and you'll see that arg1 displays as:
_StoreAction(option_strings=['--abc'], dest='abc', nargs=None,
const=None, default=None, type=None, choices=None,
help=None, metavar=None)
arg1 is an object that you can place in lists, dictionaries. You could even, in theory, add it to another parser. That's in effect what the parents mechanism does (i.e. copy action references from the parent to the child).
You can inspire yourself from Django's management commands. They are basically setup as follow:
The entry point is run_from_argv which calls create_parser, parse the command line, extract the parsed arguments and provide them to execute;
The create_parser method creates an argparse parser and uses add_argument to prepopulate default options available for all commands. This function then calls the add_arguments method of the class which is meant to be overloaded by subclasses;
The execute method is responsible to handle the various behaviours associated to the default options. It then calls handle which is meant to be overloaded by subclasses to handle the specific options introduced by add_arguments.
Your requirements are not completely clear but I think that in your case you don't need to bother with an execute method. I’d go with:
import argparse
import sys
class BaseParser:
def create_parser(self, progname):
parser = argparse.ArgumentParser(prog=progname)
parser.add_argument('--temp1')
parser.add_argument('--temp2')
self.add_arguments(parser)
return parser
def add_arguments(self, parser):
pass # to be optionnally defined in subclasses
def parse_command_line(self, argv):
parser = create_parser(argv[0])
options = parser.parse_args(argv[1:])
parsed_options = vars(options)
self.handle(**parsed_options) # HAS TO be defined in subclasses
class X(BaseParser):
def add_arguments(self, parser):
parser.add_argument('--abc')
def handle(self, **options):
abc = options['abc']
temp1 = options['temp1']
temp2 = options['temp2']
# do stuff with thoses variables
class Y(BaseParser):
def handle(self, **options):
temp1 = options['temp1']
temp2 = options['temp2']
# do stuff
x = X()
y = Y()
args = sys.argv
x.parse_command_line(args)
y.parse_command_line(args)
You could simplify the code further if X is a subclass of Y.
where report this error : TypeError: 'Namespace' object is not iterable
import argparse
def parse_args():
parser = argparse.ArgumentParser(add_help=True)
parser.add_argument('-a', '--aa', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)
return parser.parse_args()
def main():
(options, args) = parse_args()
if __name__ == '__main__':
main()
Your issue has to do with this line:
(options, args) = parse_args()
Which seems to be an idiom from the deprecated "optparse".
Use the argparse idiom without "options":
import argparse
parser = argparse.ArgumentParser(description='Do Stuff')
parser.add_argument('--verbosity')
args = parser.parse_args()
Try:
args = parse_args()
print args
Results:
$ python x.py -b B -aa
Namespace(aa=True, b='B', c=None)
It's exactly like the error message says: parser.parse_args() returns a Namespace object, which is not iterable. Only iterable things can be 'unpacked' like options, args = ....
Though I have no idea what you were expecting options and args, respectively, to end up as in your example.
The error is in that parse_argv option is not required or used, only argv is passed.
Insted of:
(options, args) = parse_args()
You need to pass
args = parse_args()
And the rest remains same.
For calling any method just make sure of using argv instead of option.
For example:
a = argv.b
The best way (for me) to operate on args, where args = parser.parse_args()
is using args.__dict__. It's good, for example, when you want to edit arguments.
Moreover, it's appropriate to use long notation in your arguments, for example '--second' in '-a' and '--third' in '-b', as in first argument.
If you want to run 'main' you can should miss 'options', but it was said earlier.