parsing argument to python script - python

I want to create a script like this
./myscript.py -g parser -a parserapp
and whenever the script is missing one option such as ./myscript.py -g parser, it will print out usage and exit the script
The script should be: ./myscript.py -g parser -a parserapp
So, my question is how can I check if option -a or-g is missing; so, it can print out the usage for the script and exit.
Thank you for your help

Use argparse
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-g", "--gggg", help="g parameter",required=True)
parser.add_argument("-a", "--aaaa", help="a parameter",required=True)
args = parser.parse_args()
print args.g, args.a
When you run it with -h it will show help:
~#:python myscript.py -h
usage: myscript.py [-h] -g GGGG -a AAAA
optional arguments:
-h, --help show this help message and exit
-g GGGG, --gggg GGGG g parameter
-a AAAA, --aaaa AAAA a parameter
If you miss a parameter it will print error:
myscript.py: error: argument -g/--gggg is required

For your need argparse can check wether an argument is missing or not like this :
import argparse
parser = argparse.ArgumentParser(description='My Super Script')
parser.add_argument(
'--parser', '-p',
required=True,
help='Parser Type'
)
parser.add_argument(
'--application', '-a',
required=True,
help='Application Name'
)
args = parser.parse_args()
By default argparse will consider a named argument optional unless you specify required=True
If the two args are not provided this will print a default help.
ref

Parsing of parameters can be done with optparse or argparse. You can make nifty help options and so on. It's also easy to make different kinds of parameters to be accepted.
Use argparse: https://docs.python.org/2/howto/argparse.html

You can use argparse module.
For Python3.x, you can look here.
For Python2.x, you can look here.
Apart from argparse, you can also use getopt module.
Use getopt:
https://docs.python.org/3.1/library/getopt.html(Python3.x)
https://docs.python.org/2/library/getopt.html(Python2.x)

Related

Configure argparse to accept quoted arguments

I am writing a program which, among other things, allows the user to specify through an argument a module to load (and then use to perform actions). I am trying to set up a way to easily pass arguments through to this inner module, and I was attempting to use ArgParse's action='append' to have it build a list of arguments that I would then pass through.
Here is a basic layout of the arguments that I am using
parser.add_argument('-M', '--module',
help="Module to run on changed files - should be in format MODULE:CLASS\n\
Specified class must have function with the signature run(src, dest)\
and return 0 upon success",
required=True)
parser.add_argument('-A', '--module_args',
help="Arg to be passed through to the specified module",
action='append',
default=[])
However - if I then try to run this program with python my_program -M module:class -A "-f filename" (where I would like to pass through the -f filename to my module) it seems to be parsing the -f as its own argument (and I get the error my_program: error: argument -A/--module_args: expected one argument
Any ideas?
With:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-M', '--module',
help="Module to run on changed files - should be in format MODULE:CLASS\n\
Specified class must have function with the signature run(src, dest)\
and return 0 upon success",
)
parser.add_argument('-A', '--module_args',
help="Arg to be passed through to the specified module",
action='append',
default=[])
import sys
print(sys.argv)
print(parser.parse_args())
I get:
1028:~/mypy$ python stack45146728.py -M module:class -A "-f filename"
['stack45146728.py', '-M', 'module:class', '-A', '-f filename']
Namespace(module='module:class', module_args=['-f filename'])
This is using a linux shell. The quoted string remains one string, as seen in the sys.argv, and is properly interpreted as an argument to -A.
Without the quotes the -f is separate and interpreted as a flag.
1028:~/mypy$ python stack45146728.py -M module:class -A -f filename
['stack45146728.py', '-M', 'module:class', '-A', '-f', 'filename']
usage: stack45146728.py [-h] [-M MODULE] [-A MODULE_ARGS]
stack45146728.py: error: argument -A/--module_args: expected one argument
Are you using windows or some other OS/shell that doesn't handle quotes the same way?
In Argparse `append` not working as expected
you asked about a slightly different command line:
1032:~/mypy$ python stack45146728.py -A "-k filepath" -A "-t"
['stack45146728.py', '-A', '-k filepath', '-A', '-t']
usage: stack45146728.py [-h] [-M MODULE] [-A MODULE_ARGS]
stack45146728.py: error: argument -A/--module_args: expected one argument
As I already noted -k filepath is passed through as one string. Because of the space, argparse does not interpret that as a flag. But it does interpret the bare '-t' as a flag.
There was a bug/issue about the possibility of interpreting undefined '-xxx' strings as arguments instead of flags. I'd have to look that up to see whether anything made it into to production.
Details of how strings are categorized as flag or argument can be found in argparse.ArgumentParser._parse_optional method. It contains a comment:
# if it contains a space, it was meant to be a positional
if ' ' in arg_string:
return None
http://bugs.python.org/issue9334 argparse does not accept options taking arguments beginning with dash (regression from optparse) is an old and long bug/issue on the topic.
The solution is to accept arbitrary arguments - there's an example in argparse's doc here:
argparse.REMAINDER. All the remaining command-line arguments are gathered into a list. This is commonly useful for command line utilities that dispatch to other command line utilities:
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--foo')
>>> parser.add_argument('command')
>>> parser.add_argument('args', nargs=argparse.REMAINDER)
>>> print(parser.parse_args('--foo B cmd --arg1 XX ZZ'.split()))
Namespace(args=['--arg1', 'XX', 'ZZ'], command='cmd', foo='B')

python argparse: print epilog only when verbose

I define a parser with a description, options, and an epilog. When I run the app with --help, it outputs help with the epilog as expected. However, I only want to see the epilog if --help is accompanied with --verbose. What is the proper way to achieve this with argparse?
# example code in file test
import argparse
parser = argparse.ArgumentParser( description='description', epilog='epilog' )
parser.add_argument('-v', '--verbose', action='store_true', help='verbose help')
parser.parse_args()
When I run test as follows
$ python test -h
it yields
usage: test [-h] [-v]
description
optional arguments:
-h, --help show this help message and exit
-v, --verbose verbose help
epilog
However, what I want to see is
usage: test [-h] [-v]
description
optional arguments:
-h, --help show this help message and exit
-v, --verbose verbose help
with the epilog shown only when I run
$ python test -h -v
Ick. The only way I know of doing this is by writing the help output by yourself:
import argparse
parser = argparse.ArgumentParser(
description='description',
add_help=False )
parser.add_argument(
'-h', '--help',
action=store_true,
dest='show_help')
parser.add_argument(
'-v', '--verbose',
action='store_true',
help='verbose help')
args = parser.parse_args()
if args.show_help:
if args.verbose:
print '%s\n%s' % (parser.format_help(), 'epilog')
else
parser.print_help()
sys.exit(0)
There's no provision in argparse for this. So you will have to write your own code to change the epilog before parsing, or perform your own help after parsing, or conceivably modifying the format_help method.
You can view and change the epilog attribute of the parser after creation.
parser = argparse.ArgumentParser(epilog='test')
print parser.epilog # should see 'test'
parser.epilog = None # or ''
One deleted answer suggested looking at sys.argv before parsing, and if the --verbose is present, modify the the epilog attribute. That may miss some ways of specifying the value (e.g. -hv), but it is relatively simple.
Acting on the --verbose during parsing is difficult. The parser will act on the -h as soon as it parses it, displaying the message and exiting. Thus any -v after -h will be missed.
Doing your own help after parsing is a viable option, if you turn off the regular help (thus preventing that print and exit action). You will know the final values of both help and verbose. But you will be responsible for your own exit.
Using the ideas suggested, here's what I came up with:
import argparse
parser = argparse.ArgumentParser( description='description', epilog='', add_help=False )
parser.add_argument('-h', '--help', action='store_true', help='show help')
parser.add_argument('-v', '--verbose', action='store_true', help='more help')
args = parser.parse_args()
if args.help:
if args.verbose:
parser.epilog += "epilog for %(prog)s"
else:
parser.epilog += "\nfor more help run '%(prog)s -h -v'"
parser.print_help()
parser.exit(0)
print 'the end'
The only difficulty I found with this approach is that it is no longer possible to add required options or positional arguments. A workaround for positional arguments is to use nargs='?' and do the checking manually.
I would suggest a different approach.
1 Build the parser as you have done right now.
do a pretty print on the parser and figure out how epilog is stored in an option. Or put a debug via pdb.set_trace() and use dirs and vars to look around.
i.e. figure out what the option data structure looks like with an epilog and without an epilog.
2 Instead of calling parser.parse_args() (standard use):
look at sys.argv yourself. If -h and -v leave the parser as is.
if -h but not -v, adjust your parser before calling it to look as if it had no epilog.
3 call with parser.parse_args()
You could even build 2 parsers, one with epilog, one without and dynamically decide which one to call depending on -v flag.
p.s. actually, you want to check
if "-h" in sys.argv and not "-v" in sys.argv
I also see the value of verbose help to add examples.
From Python 2.7 argparse printing help and Python 2.7 argument parser objects and comments above, I settled upon the following method:
import argparse
. . .
if __name__ == '__main__':
description_text = """
DESCRIPTION
This command ...
"""
epilog_text = """
After execution, the user can ...
"""
example_text = """
EXAMPLES
The following examples ...
"""
parser = argparse.ArgumentParser(
description=description_text,
epilog=epilog_text,
formatter_class=argparse.RawDescriptionHelpFormatter,
add_help=False)
parser.add_argument('-h', '--help', dest='help', action='store_true',
help='Show help and exit; see also --verbose')
parser.add_argument('--usage', dest='usage', action='store_true',
help='Show usage and exit')
. . .
parser.add_argument('-v', '--verbose', dest='verbose',
action='store_true',
help='Display additional help or logging')
arguments = parser.parse_args()
if arguments.usage:
print(parser.format_usage())
sys.exit(0)
if arguments.help:
help_string = parser.format_help()
if arguments.verbose:
help_string += example_text
print(help_string)
sys.exit(0)
. . .
The result is a flexible output which supports --help, --help --verbose and --usage controls for the command. Thanks to others above for the inspiration.

How to use argsparse so the script options with the same affect will appear in the same 'help' line

I'm using argsparse to parse the options passed to my python scripts.
I want to enable passing '-a', and to allow passing '-b' with the same affect.
No problem, I'll call parser.add_argument() twice, with the same description:
parser.add_argument('-a', help='do something')
parser.add_argument('-b', help='do something')
But now when displaying the script help, I will see both, as such:
-a do something
-b do something
This is ugly.
I would prefer to have {-a, -b} or {-a|b).
I could not find in argsparse documentation any way around this (admittedly, not critical) issue.
You could try to pass both arguments to the same add_argument call:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-a', '-b', help='do something')
parser.parse_args(['--help'])
output
usage: test.py [-h] [-a A]
optional arguments:
-h, --help show this help message and exit
-a A, -b A do something

argparse, two arguments depend on each other

I would like to make the parser like cmd [-a xxx -b xxx] -c xxx -d xxx
When -a is used, I want -b to be used too. likewise, if -b is used, -a must be used too. It's ok both -a and -b are not used.
How do I do that? I have tried custom actions, but it does not go well.
A better design would be to have a single option that takes two arguments:
parser.add_argument('-a', nargs=2)
Then you either specify the option with 2 arguments, or you don't specify it at all.
$ script -a 1 2
or
$ script
A custom action (or postprocessing) can split the tuple args.a into two separate values args.a and args.b.
Argparse doesn't natively support this type of use.
The most effective thing to do is check and see if those types of conditions are met after parsing:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-b')
parser.add_argument('-c')
args = parser.parse_args()
required_together = ('b','c')
# args.b will be None if b is not provided
if not all([getattr(args,x) for x in required_together]):
raise RuntimeError("Cannot supply -c without -b")

In Python, is there a better way to deal with command line options when each option could have several arguments

I've a Python app like this:
$ app.py -a arg1 -a arg2 -b file1.b -b file2.b
Depending on the option, in app.py, I use two different functions to process the input files. As each option can have several file arguments, I would like something like this:
$ app.py -a arg1 arg2 -b *.b
After searching on the net, I find these three modules: getopt, argparse, optparse
And I wrote a simple one for my app:
optionDict= {'-a':aFunction, '-b':bFunction}
for arg in sys.argv[1:]:
if arg in optionDict: # arg is an option here.
funcName = optionDict[arg]
else: # arg is not an option, then it's a fileName. Deal with this file with the function found according to previous arg.
funcName(arg)
My quesion: Are there any other modules or better ways to do this?
Since 2012 Python has an easy, powerful and very cool module for argument parsing called docopt. It works with Python from 2.5 to 3.3 and needs no installation. Here is the code for your particular case:
'''app.py
Usage:
app.py -a ARG ... -b FILE ...
app.py (-h | -v)
'''
from docopt import docopt
args = docopt(__doc__, version='1.0')
# now do something based on the value of args dict
So this is it: one line of code plus your doc string which is essential. I told you it's cool -- didn't I ;-)
If you found argparse to be too hard to get started with because of the complexity, then you would probably like plac which is available on PyPi. It is a wrapper around argparse which hides the complexity, and it takes care of a lot of boilerplate argument checking for you.
It works for both Python 2 and 3, and has a decorator that allows Python 2 users to get access to a nice Python 3 feature for function parameters. And you can even nest commands in one another so that you could have -arguments (beginning with dash) and .arguments (beginning with dot) on one command line.
Here are the docs http://plac.googlecode.com/hg/doc/plac.html
I think that argparse is the way to go. It does a great job, it is easy to use and in the standard library since 2.7. Take a look a the nargs argument. From the docs
Fixed elements to consume
parser = argparse.ArgumentParser()
parser.add_argument('--foo', nargs=2)
parser.add_argument('bar', nargs=1)
parser.parse_args('c --foo a b'.split())
Namespace(bar=['c'], foo=['a', 'b'])
One arg will be consumed from the command line if possible, and produced as a single item. If no command-line arg is present, the value from default will be produced
parser = argparse.ArgumentParser()
parser.add_argument('--foo', nargs='?', const='c', default='d')
parser.add_argument('bar', nargs='?', default='d')
parser.parse_args('XX --foo YY'.split())
Namespace(bar='XX', foo='YY')
parser.parse_args('XX --foo'.split())
Namespace(bar='XX', foo='c')
parser.parse_args(''.split())
Namespace(bar='d', foo='d')
All command-line args present are gathered into a list.
parser = argparse.ArgumentParser()
parser.add_argument('--foo', nargs='*')
parser.add_argument('--bar', nargs='*')
parser.add_argument('baz', nargs='*')
parser.parse_args('a b --foo x y --bar 1 2'.split())
Namespace(bar=['1', '2'], baz=['a', 'b'], foo=['x', 'y'])

Categories

Resources