I have to either store the command line argument in a variable or assign a default value to it.
What i am trying is the below
import sys
Var=sys.argv[1] or "somevalue"
I am getting the error out of index if i don't specify any argument. How to solve this?
Var=sys.argv[1] if len(sys.argv) > 1 else "somevalue"
The builtin argparse module is intended for exactly these sorts of tasks:
import argparse
# Set up argument parser
ap = argparse.ArgumentParser()
# Single positional argument, nargs makes it optional
ap.add_argument("thingy", nargs='?', default="blah")
# Do parsing
a = ap.parse_args()
# Use argument
print a.thingy
Or, if you are stuck with Python 2.6 or earlier, and don't wish to add a requirement on the backported argparse module, you can do similar things manually like so:
import optparse
opter = optparse.OptionParser()
# opter.add_option("-v", "--verbose") etc
opts, args = opter.parse_args()
if len(args) == 0:
var = "somevalue"
elif len(args) == 1:
var = args[0]
else:
opter.error("Only one argument expected, got %d" % len(args))
print var
Good question.
I think the best solution would be to do
try:
var = sys.argv[1]
except IndexError:
var = "somevalue"
Try the following with a command-line-processing template:
def process_command_line(argv):
...
# add your option here
parser.add_option('--var',
default="somevalue",
help="your help text")
def main(argv=None):
settings, args = process_command_line(argv)
...
print settings, args # <- print your settings and args
Running ./your_script.py with the template below and your modifications above prints {'var': 'somevalue'} []
For an example of a command-line-processing template see an example in Code Like a Pythonista: Idiomatic Python (http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#command-line-processing):
#!/usr/bin/env python
"""
Module docstring.
"""
import sys
import optparse
def process_command_line(argv):
"""
Return a 2-tuple: (settings object, args list).
`argv` is a list of arguments, or `None` for ``sys.argv[1:]``.
"""
if argv is None:
argv = sys.argv[1:]
# initialize the parser object:
parser = optparse.OptionParser(
formatter=optparse.TitledHelpFormatter(width=78),
add_help_option=None)
# define options here:
parser.add_option( # customized description; put --help last
'-h', '--help', action='help',
help='Show this help message and exit.')
settings, args = parser.parse_args(argv)
# check number of arguments, verify values, etc.:
if args:
parser.error('program takes no command-line arguments; '
'"%s" ignored.' % (args,))
# further process settings & args if necessary
return settings, args
def main(argv=None):
settings, args = process_command_line(argv)
# application code here, like:
# run(settings, args)
return 0 # success
if __name__ == '__main__':
status = main()
sys.exit(status)
Related
Below is an example code that uses argparse
import os
import numpy
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-C','--Chk',type=str, help='Choose arg')
parser.add_argument('-R','--ReC',type=str, help='Choose arg')
args = vars(parser.parse_args())
if args['Chk'] == 'compo1':
print('This is comp1')
elif args['Chk'] == 'compo2':
print('This is comp2')
else:
print('The specified comp does not exist')
if args['ReC'] == 'recompo':
print('This is second test')
else:
print('The specified second_T does not exist')
if __name__=='__main__':
main()
The above code works fine. Since both are optional arguments, I would like to have two features:
If invalid arguments are given, for -C or -R I would like to print/raise a message. I tried using raise argparse.ArgumentTypeError, see below.
if len(args) > 8 or len(args) < 3:
raise argparse.ArgumentTypeError('Print this error message')
return
Secondly, I would like to have situations where the code should not do anything if either of -C or -R are not given. In the above code, if no arguments are given in either case, it prints The specified comp does not exist which is not ideal.
Any better way to do the above tasks ? Thanks
If you use choices, argparse will test for a specific set of values:
In [44]: parser = argparse.ArgumentParser()
...: parser.add_argument('-C','--Chk',choices=['compo1','compo2'], help='Choose arg', default='foobar')
...: parser.add_argument('-R','--ReC',choices=['recompo'], help='Choose arg', default='xxx');
Acceptable:
In [45]: parser.parse_args('-C compo1 -R recompo'.split())
Out[45]: Namespace(Chk='compo1', ReC='recompo')
Defaults - I specified some strings; default default is None:
In [46]: parser.parse_args([])
Out[46]: Namespace(Chk='foobar', ReC='xxx')
A wrong choice raises an error with usage and exit:
In [47]: parser.parse_args('-C compo1 -R recomp1'.split())
usage: ipykernel_launcher.py [-h] [-C {compo1,compo2}] [-R {recompo}]
ipykernel_launcher.py: error: argument -R/--ReC: invalid choice: 'recomp1' (choose from 'recompo')
A type function could be used instead if you want to limit the string lengths instead.
Otherwise, post-parsing testing of values is best, even if the logic looks a bit messy.
for error handling, you can check this way:
class ArgumentParserError(Exception): pass
class ThrowingArgumentParser(argparse.ArgumentParser):
def error(self, message):
raise ArgumentParserError(message)
and this is maybe some help for you:
parser = ThrowingArgumentParser(description="YOUR DESCRIPTION")
parser.add_argument('func', nargs='?', choices=['C','R'], const='')
I am using argparse's action to add various data to a class. I would like to use that action on the default value if that arg is not provided at the command line. Is this possible?
Thanks!
argparse does not use the action when applying the default. It just uses setattr. It may use the type if the default is a string. But you can invoke the action directly.
Here I use a custom action class borrowed from the documentation. In the first parse_args nothing happens. Then I create a new namespace, and invoke the action on the default. Then I pass that namespace to parse_args. To understand this, you many need to import it into an interactive shell, and examine the attributes of the namespace and action.
# sample custom action from docs
class FooAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
print('Setting: %r %r %r' % (namespace, values, option_string))
setattr(namespace, self.dest, 'action:'+values)
p = argparse.ArgumentParser()
a1 = p.add_argument('--foo', action=FooAction, default='default')
print 'action:',a1
print p.parse_args([])
ns = argparse.Namespace()
a1(p, ns, a1.default, 'no string') # call action
print p.parse_args([],ns)
print p.parse_args(['--foo','1'],ns)
which produces:
action: FooAction(option_strings=['--foo'], dest='foo', nargs=None, const=None, default='default', type=None, choices=None, help=None, metavar=None)
Namespace(foo='default')
Setting: Namespace() 'default' 'no string'
Namespace(foo='action:default')
Setting: Namespace(foo='action:default') '1' '--foo'
Namespace(foo='action:1')
I tailored the output to highlight when the action is being used.
Here's a way of performing a special action on an argument that isn't given on the command line (or given with a value == to the default). It's a simplification of the class given in https://stackoverflow.com/a/24638908/901925.
class Parser1:
def __init__(self, desc):
self.parser = argparse.ArgumentParser(description=desc)
self.actions = []
def milestone(self, help_='milestone for latest release.', default=None):
action = self.parser.add_argument('-m', '--milestone', help=help_, default=default)
self.actions.append(action)
return self
def parse(self):
args = self.parser.parse_args()
for a in self.actions:
if getattr(args, a.dest) == a.default:
print 'Please specify', a.dest
values = raw_input('>')
setattr(args, a.dest, values)
return args
print Parser1('desc').milestone(default='PROMPT').parse()
The prompting is done after parse_args. I don't see any reason to call parse_args again.
I needed to prompt the user if an option was not specified - that's how I did it:
class _PromptUserAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if values == self.default:
print 'Please specify', self.dest
values = raw_input('>')
setattr(namespace, self.dest, values)
class Parser:
def __init__(self, desc, add_h=True):
self.parser = argparse.ArgumentParser(description=desc, add_help=add_h,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
#actions to be run on options if not specified (using default to check)
self.actions = []
#staticmethod
def new(description, add_help=True):
return Parser(description, add_help)
# ...
def milestone(self, help_='Specify the milestone for latest release.'):
action = self.parser.add_argument('-m', '--milestone',
dest='milestone',
action=_PromptUserAction,
default='PROMPT', # needed I think
type=str,
help=help_)
self.actions.append(action)
return self
def parse(self):
"""
Return an object which can be used to get the arguments as in:
parser_instance.parse().milestone
:return: ArgumentParser
"""
args = self.parser.parse_args()
# see: http://stackoverflow.com/a/21588198/281545
dic = vars(args)
ns = argparse.Namespace()
for a in self.actions:
if dic[a.dest] == a.default:
a(self.parser, ns, a.default) # call action
# duh - can I avoid it ?
import sys
return self.parser.parse_args(sys.argv[1:],ns)
I am interested if this can somehow be done without having to reparse the args (the import sys part). Maybe some constructor options for argparse.Action ?
This is more like a code design question. what are good default values for optional options that are of type string/directory/fullname of files?
Let us say I have code like this:
import optparse
parser = optparse.OptionParser()
parser.add_option('-i', '--in_dir', action = "store", default = 'n', help = 'this is an optional arg')
(options, args) = parser.parse_args()
Then I do:
if options.in_dir == 'n':
print 'the user did not pass any value for the in_dir option'
else:
print 'the user in_dir=%s' %(options.in_dir)
Basically I want to have default values that mean the user did not input such option versus the actual value. Using 'n' was arbitrary, is there a better recommendation?
You could use an empty string, "", which Python interprets as being False; you can simply test:
if options.in_dir:
# argument supplied
else:
# still empty, no arg
Alternatively, use None:
if options.in_dir is None:
# no arg
else:
# arg supplied
Note that the latter is, per the documentation the default for un-supplied arguments.
How about just None?
Nothing mandates that the default values must be of the same type as the option itself.
import optparse
parser = optparse.OptionParser()
parser.add_option('-i', '--in_dir', default=None, help='this is an optional arg')
(options, args) = parser.parse_args()
print vars(options)
(ps. action="store" isn't required; store is the default action.)
I have a script where I ask the user for a list of pre-defined actions to perform. I also want the ability to assume a particular list of actions when the user doesn't define anything. however, it seems like trying to do both of these together is impossible.
when the user gives no arguments, they receive an error that the default choice is invalid
acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']])
args = p.parse_args([])
>>> usage: [-h] [{clear,copy,dump,lock} [{clear,copy,dump,lock} ...]]
: error: argument action: invalid choice: [['dump', 'clear']] (choose from 'clear', 'copy', 'dump', 'lock')
and when they do define a set of actions, the resultant namespace has the user's actions appended to the default, rather than replacing the default
acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']])
args = p.parse_args(['lock'])
args
>>> Namespace(action=[['dump', 'clear'], ['dump']])
What you need can be done using a customized argparse.Action as in the following example:
import argparse
parser = argparse.ArgumentParser()
class DefaultListAction(argparse.Action):
CHOICES = ['clear','copy','dump','lock']
def __call__(self, parser, namespace, values, option_string=None):
if values:
for value in values:
if value not in self.CHOICES:
message = ("invalid choice: {0!r} (choose from {1})"
.format(value,
', '.join([repr(action)
for action in self.CHOICES])))
raise argparse.ArgumentError(self, message)
setattr(namespace, self.dest, values)
parser.add_argument('actions', nargs='*', action=DefaultListAction,
default = ['dump', 'clear'],
metavar='ACTION')
print parser.parse_args([])
print parser.parse_args(['lock'])
The output of the script is:
$ python test.py
Namespace(actions=['dump', 'clear'])
Namespace(actions=['lock'])
In the documentation (http://docs.python.org/dev/library/argparse.html#default), it is said :
For positional arguments with nargs equal to ? or *, the default value is used when no command-line argument was present.
Then, if we do :
acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', choices=acts, default='clear')
print p.parse_args([])
We get what we expect
Namespace(action='clear')
The problem is when you put a list as a default.
But I've seen it in the doc,
parser.add_argument('bar', nargs='*', default=[1, 2, 3], help='BAR!')
So, I don't know :-(
Anyhow, here is a workaround that does the job you want :
import sys, argparse
acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', choices=acts)
args = ['dump', 'clear'] # I set the default here ...
if sys.argv[1:]:
args = p.parse_args()
print args
I ended up doing the following:
no append
add the empty list to the possible choices or else the empty input breaks
without default
check for an empty list afterwards and set the actual default in that case
Example:
parser = argparse.ArgumentParser()
parser.add_argument(
'is',
type=int,
choices=[[], 1, 2, 3],
nargs='*',
)
args = parser.parse_args(['1', '3'])
assert args.a == [1, 3]
args = parser.parse_args([])
assert args.a == []
if args.a == []:
args.a = [1, 2]
args = parser.parse_args(['1', '4'])
# Error: '4' is not valid.
You could test whether the user is supplying actions (in which case parse it as a required, position argument), or is supplying no actions (in which case parse it as an optional argument with default):
import argparse
import sys
acts = ['clear', 'copy', 'dump', 'lock']
p = argparse.ArgumentParser()
if sys.argv[1:]:
p.add_argument('action', nargs = '*', choices = acts)
else:
p.add_argument('--action', default = ['dump', 'clear'])
args = p.parse_args()
print(args)
when run, yields these results:
% test.py
Namespace(action=['dump', 'clear'])
% test.py lock
Namespace(action=['lock'])
% test.py lock dump
Namespace(action=['lock', 'dump'])
You probably have other options to parse as well. In that case, you could use parse_known_args to parse the other options, and then handle the unknown arguments in a second pass:
import argparse
acts = ['clear', 'copy', 'dump', 'lock']
p = argparse.ArgumentParser()
p.add_argument('--foo')
args, unknown = p.parse_known_args()
if unknown:
p.add_argument('action', nargs = '*', choices = acts)
else:
p.add_argument('--action', default = ['dump', 'clear'])
p.parse_args(unknown, namespace = args)
print(args)
when run, yields these results:
% test.py
Namespace(action=['dump', 'clear'], foo=None)
% test.py --foo bar
Namespace(action=['dump', 'clear'], foo='bar')
% test.py lock dump
Namespace(action=['lock', 'dump'], foo=None)
% test.py lock dump --foo bar
Namespace(action=['lock', 'dump'], foo='bar')
The action was being appended because of the "action='append'" parameter you passed to argparse.
After removing this parameter, the arguments passed by a user would be displayed on their own, but the program would throw an error when no arguments were passed.
Adding a '--' prefix to the first parameter resolves this in the laziest way.
acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('--action', nargs='*', choices=acts, default=[['dump', 'clear']])
args = p.parse_args()
The downside to this approach is that the options passed by the user must now be preceded by '--action', like:
app.py --action clear dump copy
I'm trying to get to know optparse a bit better, but I'm struggling to understand why the following code behaves the way it does. Am I doing something stupid?
import optparse
def store_test(option, opt_str, value, parser, args=None, kwargs=None):
print 'opt_str:', opt_str
print 'value:', value
op = optparse.OptionParser()
op.add_option('-t', '--test', action='callback', callback=store_test, default='test',
dest='test', help='test!')
(opts, args) = op.parse_args(['test.py', '-t', 'foo'])
print
print 'opts:'
print opts
print 'args:'
print args
Output:
opt_str: -t
value: None
opts:
{'test': 'test'}
args:
['foo']
Why is 'foo' not being passed to store_test() and instead being interpreted as an extra argument? Is there something wrong with op.parse_args(['-t', 'foo'])?
↓
http://codepad.org/vq3cvE13
Edit:
Here's the example from the docs:
def store_value(option, opt_str, value, parser):
setattr(parser.values, option.dest, value)
[...]
parser.add_option("--foo",
action="callback", callback=store_value,
type="int", nargs=3, dest="foo")
You're missing a "type" or "nargs" option attribute:
op.add_option('-t', '--test', action='callback', callback=store_test, default='test',
dest='test', help='test!', type='str')
This option will cause it to consume the next argument.
Reference:
http://docs.python.org/library/optparse.html#optparse-option-callbacks
type
has its usual meaning: as with the "store" or "append" actions, it instructs optparse
to consume one argument and convert it to type. Rather than storing the converted
value(s) anywhere, though, optparse passes it to your callback function.
nargs
also has its usual meaning: if it is supplied and > 1, optparse will consume nargs
arguments, each of which must be convertible to type. It then passes a tuple of converted
values to your callback.
This seems to be the relevant code from optparse.py:
def takes_value(self):
return self.type is not None
def _process_short_opts(self, rargs, values):
[...]
if option.takes_value():
# Any characters left in arg? Pretend they're the
# next arg, and stop consuming characters of arg.
if i < len(arg):
rargs.insert(0, arg[i:])
stop = True
nargs = option.nargs
if len(rargs) < nargs:
if nargs == 1:
self.error(_("%s option requires an argument") % opt)
else:
self.error(_("%s option requires %d arguments")
% (opt, nargs))
elif nargs == 1:
value = rargs.pop(0)
else:
value = tuple(rargs[0:nargs])
del rargs[0:nargs]
else: # option doesn't take a value
value = None
option.process(opt, value, values, self)