I tried to use this statement :
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--music', nargs='*', default=False, const=True)
args = parser.parse_args()
print(args.music)
But got this error:
`builtins.ValueError: nargs must be '?' to supply const`
what i want to do is :
if -m is in args list but whithout any value, args.music will give me True
if -m is in args list and have 'N' values, args.music will give me a list of all values
if -m is not in args list, args,music will return False
the second and lastOne worked but, when i try to use const i got an error
You could use a custom action:
import argparse
class EmptyIsTrue(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if len(values) == 0:
values = True
setattr(namespace, self.dest, values)
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--music', nargs='*', default=False, action=EmptyIsTrue)
print(parser.parse_args([]))
# Namespace(music=False)
print(parser.parse_args(['-m']))
# Namespace(music=True)
print(parser.parse_args('-m 1 2'.split()))
# Namespace(music=['1', '2'])
If you have only one argument to handle this way, then
arg.music = True if len(arg.music) == 0 else arg.music
is simpler. If you have many such arguments, then defining a custom action could reduce the repetition, and help ensure all those arguments are treated the same way.
What about :
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--music', nargs='*', default=False)
args = parser.parse_args()
if vars(args).get('music', False) is not False:
if not args.music:
args.music = True
print args.music
Output:
tmp:/>python arg.py
False
tmp:/>python arg.py -m
True
tmp:/>python arg.py -m 1 2 3
['1', '2', '3']
tmp:/>
The following hack after the argparse section solves your problem:
import argparse
# Same as your code above
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--music', nargs='*', default=False)
args = parser.parse_args()
# Modifies args.music: [] -> True
args.music = True if args.music==[] else args.music
print(args.music)
Tested in the command line, it gives:
$ python /tmp/blah.py -m
True
$ python /tmp/blah.py -m 1 -m 2
['2']
$ python /tmp/blah.py -m 1 2 3
['1', '2', '3']
$ python /tmp/blah.py
False
I have a python script to parse input argument from user .my code is like this
def get_arg(argv):
result = {}
input_file=stag_db=main_tb=stag_table=main_table = "";
debug="N"
msg = ''' Syntax: dt transaction date
-i input_file (E.g. input_file.tar.gz)
-ds staging_database
-dm main_database
-ts staging_table
-tm main_table
-db debug (Y/N)'''
try:
opts, args = getopt.getopt(argv,"hd:i:ds:dm:db:ts:tm:",["ifile=","ofile="])
print args
print opts
except getopt.GetoptError:
f_end_process(msg)
for opt, arg in opts:
if opt == '-h':
f_end_process(msg)
elif opt == "-i":
input_file = arg
elif opt == "-ds":
stag_db = arg
elif opt == "-dm":
main_tb = arg
elif opt == "-ts":
stag_table = arg
elif opt == "-tm":
main_table = arg
elif opt == "-db":
debug = arg
result = {'input_file':input_file,'stag_db':stag_db,'main_tb':main_tb,'stag_table':stag_table,'main_table':main_table}
print result
if '' in result.values():
exit_status=-1
f_end_process(msg)
result['debug']= debug
return result
def main():
global input_arg
input_arg = get_arg(sys.argv[1:])
print "process started at " +strftime("%Y-%m-%d %H:%M:%S")
print input_arg
i am running code like this
python main.py -i ok.txt -ds ds_val -dm dm_val -ts tas_val -tm tm_val
i want to parse all input arguments to a list. i imported all required modules to my script
now i am able to parse only -i input.How can i parse -tm,-ts ,-dm,-ds iputs?
In an interactive Python experiment with passing various argv arrays to getopt
>>> getopt.getopt(['-hd','1'],"hd:i:ds:dm:db:ts:tm:")
([('-h', ''), ('-d', '1')], [])
You did not tell it to look for an -hd option, but rather a -h and a -d that takes an argument.
Generally for multiletter options, argument parsers expect you to use --. Your getopt should be
>>> getopt.getopt(['--hd','1'],"i:",["hd=","ds=",...])
([('--hd', '1')], [])
But, do consider argparse.
You can manage using argparse, in just a few lines.
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--input-file', '-i', type=str,
help='input file (eg: input_file.tar.gz)')
parser.add_argument('--staging-db', '-S', type=str,
help='staging database name')
parser.add_argument('--main-db', '-M', type=str,
help='main database name')
parser.add_argument('--staging-table', '-s', type=str,
help='staging table name')
parser.add_argument('--main-table', '-m', type=str,
help='main table name')
parser.add_argument('--debug', '-d', type=bool, default=False,
help="activate debug mode (defaults to False)")
args = parser.parse_args()
The parser help is generated by argparse. You can output it by typing
$ python YOURSCRIPT.py --help
If c is not know to the user and a = 3 and b = 2 I want to call the program like this:
$ python my_program 3 2
if c is know to the user and c = 9 I want to call the program like this:
$ python my_program -c 6
Since the calculation represented by c = results.a + results.b takes about 30 minutes complete I would like the ability to skip over it if c is known.
Would this be possible? I have two different Code examples below but this is not exactly what I want.
Code example 1
import argparse
def my_function():
if results.c == 0:
c = results.a + results.b
d = c + 5
else:
d = results.c + 5
if __name__=='__main__':
parser = argparse.ArgumentParser()
parser.add_argument('a', action="store", type=int)
parser.add_argument('b', action="store", type=int)
parser.add_argument('-c', action="store", default=0, type=int)
results = parser.parse_args()
my_function()
My code example 1 required calls would be:
$ python my_program 3 2
or
$ python my_program 3 2 -c 6
Code Example 2
import argparse
def my_function():
if results.c == 0:
c = results.a + results.b
d = c + 5
else:
d = results.c + 5
if __name__=='__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-a', action="store", default=0, type=int)
parser.add_argument('-b', action="store", default=0, type=int)
parser.add_argument('-c', action="store", default=0, type=int)
results = parser.parse_args()
my_function()
My code example 2 required calls would be:
$ python my_program -a 3 -b 2
or
$ python my_program -c 6
Thank you for any help. Have a good day.
You'll have to make all arguments optional and validate them yourself:
ap=argparse.ArgumentParser()
ap.add_argument('-c', required=False)
ap.add_argument('a', nargs='?')
ap.add_argument('b', nargs='?')
args = ap.parse_args()
if (args.c is not None):
# process c
elif (args.a is not None and args.b is not None):
# process a and b
else:
# validation errors
If you were willing to make a and b a single named parameter instead of positionals, you could use mutual exclusion to do this for you. Mutually exclusive groups require all parameters in them to be optional, and named parameters can only be required.
ap=argparse.ArgumentParser()
g = ap.add_mutually_exclusive_group(required=True)
g.add_argument('-ab', nargs=2, required=False)
g.add_argument('-c', required=False)
args = ap.parse_args()
print('args={}'.format(repr(args)))
if (args.c is not None):
# process c
elif (args.ab is not None):
a, b = args.ab
# process a and b
else:
raise Exception("should never get here")
I am implementing a command line program which has interface like this:
cmd [GLOBAL_OPTIONS] {command [COMMAND_OPTS]} [{command [COMMAND_OPTS]} ...]
I have gone through the argparse documentation. I can implement GLOBAL_OPTIONS as optional argument using add_argument in argparse. And the {command [COMMAND_OPTS]} using Sub-commands.
From the documentation it seems I can have only one sub-command. But as you can see I have to implement one or more sub-commands. What is the best way to parse such command line arguments useing argparse?
I came up with the same qustion, and it seems i have got a better answer.
The solution is we shall not simply nest subparser with another subparser, but we can add subparser following with a parser following another subparser.
Code tell you how:
parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument('--user', '-u',
default=getpass.getuser(),
help='username')
parent_parser.add_argument('--debug', default=False, required=False,
action='store_true', dest="debug", help='debug flag')
main_parser = argparse.ArgumentParser()
service_subparsers = main_parser.add_subparsers(title="service",
dest="service_command")
service_parser = service_subparsers.add_parser("first", help="first",
parents=[parent_parser])
action_subparser = service_parser.add_subparsers(title="action",
dest="action_command")
action_parser = action_subparser.add_parser("second", help="second",
parents=[parent_parser])
args = main_parser.parse_args()
#mgilson has a nice answer to this question. But problem with splitting sys.argv myself is that i lose all the nice help message Argparse generates for the user. So i ended up doing this:
import argparse
## This function takes the 'extra' attribute from global namespace and re-parses it to create separate namespaces for all other chained commands.
def parse_extra (parser, namespace):
namespaces = []
extra = namespace.extra
while extra:
n = parser.parse_args(extra)
extra = n.extra
namespaces.append(n)
return namespaces
argparser=argparse.ArgumentParser()
subparsers = argparser.add_subparsers(help='sub-command help', dest='subparser_name')
parser_a = subparsers.add_parser('command_a', help = "command_a help")
## Setup options for parser_a
## Add nargs="*" for zero or more other commands
argparser.add_argument('extra', nargs = "*", help = 'Other commands')
## Do similar stuff for other sub-parsers
Now after first parse all chained commands are stored in extra. I reparse it while it is not empty to get all the chained commands and create separate namespaces for them. And i get nicer usage string that argparse generates.
parse_known_args returns a Namespace and a list of unknown strings. This is similar to the extra in the checked answer.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
sub = parser.add_subparsers()
for i in range(1,4):
sp = sub.add_parser('cmd%i'%i)
sp.add_argument('--foo%i'%i) # optionals have to be distinct
rest = '--foo 0 cmd2 --foo2 2 cmd3 --foo3 3 cmd1 --foo1 1'.split() # or sys.argv
args = argparse.Namespace()
while rest:
args,rest = parser.parse_known_args(rest,namespace=args)
print args, rest
produces:
Namespace(foo='0', foo2='2') ['cmd3', '--foo3', '3', 'cmd1', '--foo1', '1']
Namespace(foo='0', foo2='2', foo3='3') ['cmd1', '--foo1', '1']
Namespace(foo='0', foo1='1', foo2='2', foo3='3') []
An alternative loop would give each subparser its own namespace. This allows overlap in positionals names.
argslist = []
while rest:
args,rest = parser.parse_known_args(rest)
argslist.append(args)
The solution provide by #Vikas fails for subcommand-specific optional arguments, but the approach is valid. Here is an improved version:
import argparse
# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
# create the parser for the "command_a" command
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')
# create the parser for the "command_b" command
parser_b = subparsers.add_parser('command_b', help='command_b help')
parser_b.add_argument('--baz', choices='XYZ', help='baz help')
# parse some argument lists
argv = ['--foo', 'command_a', '12', 'command_b', '--baz', 'Z']
while argv:
print(argv)
options, argv = parser.parse_known_args(argv)
print(options)
if not options.subparser_name:
break
This uses parse_known_args instead of parse_args. parse_args aborts as soon as a argument unknown to the current subparser is encountered, parse_known_args returns them as a second value in the returned tuple. In this approach, the remaining arguments are fed again to the parser. So for each command, a new Namespace is created.
Note that in this basic example, all global options are added to the first options Namespace only, not to the subsequent Namespaces.
This approach works fine for most situations, but has three important limitations:
It is not possible to use the same optional argument for different subcommands, like myprog.py command_a --foo=bar command_b --foo=bar.
It is not possible to use any variable length positional arguments with subcommands (nargs='?' or nargs='+' or nargs='*').
Any known argument is parsed, without 'breaking' at the new command. E.g. in PROG --foo command_b command_a --baz Z 12 with the above code, --baz Z will be consumed by command_b, not by command_a.
These limitations are a direct limitation of argparse. Here is a simple example that shows the limitations of argparse -even when using a single subcommand-:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('spam', nargs='?')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
# create the parser for the "command_a" command
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')
# create the parser for the "command_b" command
parser_b = subparsers.add_parser('command_b', help='command_b help')
options = parser.parse_args('command_a 42'.split())
print(options)
This will raise the error: argument subparser_name: invalid choice: '42' (choose from 'command_a', 'command_b').
The cause is that the internal method argparse.ArgParser._parse_known_args() it is too greedy and assumes that command_a is the value of the optional spam argument. In particular, when 'splitting' up optional and positional arguments, _parse_known_args() does not look at the names of the arugments (like command_a or command_b), but merely where they occur in the argument list. It also assumes that any subcommand will consume all remaining arguments.
This limitation of argparse also prevents a proper implementation of multi-command subparsers. This unfortunately means that a proper implementation requires a full rewrite of the argparse.ArgParser._parse_known_args() method, which is 200+ lines of code.
Given these limitation, it may be an options to simply revert to a single multiple-choice argument instead of subcommands:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--bar', type=int, help='bar help')
parser.add_argument('commands', nargs='*', metavar='COMMAND',
choices=['command_a', 'command_b'])
options = parser.parse_args('--bar 2 command_a command_b'.split())
print(options)
#options = parser.parse_args(['--help'])
It is even possible to list the different commands in the usage information, see my answer https://stackoverflow.com/a/49999185/428542
You can always split up the command-line yourself (split sys.argv on your command names), and then only pass the portion corresponding to the particular command to parse_args -- You can even use the same Namespace using the namespace keyword if you want.
Grouping the commandline is easy with itertools.groupby:
import sys
import itertools
import argparse
mycommands=['cmd1','cmd2','cmd3']
def groupargs(arg,currentarg=[None]):
if(arg in mycommands):currentarg[0]=arg
return currentarg[0]
commandlines=[list(args) for cmd,args in intertools.groupby(sys.argv,groupargs)]
#setup parser here...
parser=argparse.ArgumentParser()
#...
namespace=argparse.Namespace()
for cmdline in commandlines:
parser.parse_args(cmdline,namespace=namespace)
#Now do something with namespace...
untested
Improving on the answer by #mgilson, I wrote a small parsing method which splits argv into parts and puts values of arguments of commands into hierarchy of namespaces:
import sys
import argparse
def parse_args(parser, commands):
# Divide argv by commands
split_argv = [[]]
for c in sys.argv[1:]:
if c in commands.choices:
split_argv.append([c])
else:
split_argv[-1].append(c)
# Initialize namespace
args = argparse.Namespace()
for c in commands.choices:
setattr(args, c, None)
# Parse each command
parser.parse_args(split_argv[0], namespace=args) # Without command
for argv in split_argv[1:]: # Commands
n = argparse.Namespace()
setattr(args, argv[0], n)
parser.parse_args(argv, namespace=n)
return args
parser = argparse.ArgumentParser()
commands = parser.add_subparsers(title='sub-commands')
cmd1_parser = commands.add_parser('cmd1')
cmd1_parser.add_argument('--foo')
cmd2_parser = commands.add_parser('cmd2')
cmd2_parser.add_argument('--foo')
cmd2_parser = commands.add_parser('cmd3')
cmd2_parser.add_argument('--foo')
args = parse_args(parser, commands)
print(args)
It behaves properly, providing nice argparse help:
For ./test.py --help:
usage: test.py [-h] {cmd1,cmd2,cmd3} ...
optional arguments:
-h, --help show this help message and exit
sub-commands:
{cmd1,cmd2,cmd3}
For ./test.py cmd1 --help:
usage: test.py cmd1 [-h] [--foo FOO]
optional arguments:
-h, --help show this help message and exit
--foo FOO
And creates a hierarchy of namespaces containing the argument values:
./test.py cmd1 --foo 3 cmd3 --foo 4
Namespace(cmd1=Namespace(foo='3'), cmd2=None, cmd3=Namespace(foo='4'))
You could try arghandler. This is an extension to argparse with explicit support for subcommands.
Built a full Python 2/3 example with subparsers, parse_known_args and parse_args (running on IDEone):
from __future__ import print_function
from argparse import ArgumentParser
from random import randint
def main():
parser = get_parser()
input_sum_cmd = ['sum_cmd', '--sum']
input_min_cmd = ['min_cmd', '--min']
args, rest = parser.parse_known_args(
# `sum`
input_sum_cmd +
['-a', str(randint(21, 30)),
'-b', str(randint(51, 80))] +
# `min`
input_min_cmd +
['-y', str(float(randint(64, 79))),
'-z', str(float(randint(91, 120)) + .5)]
)
print('args:\t ', args,
'\nrest:\t ', rest, '\n', sep='')
sum_cmd_result = args.sm((args.a, args.b))
print(
'a:\t\t {:02d}\n'.format(args.a),
'b:\t\t {:02d}\n'.format(args.b),
'sum_cmd: {:02d}\n'.format(sum_cmd_result), sep='')
assert rest[0] == 'min_cmd'
args = parser.parse_args(rest)
min_cmd_result = args.mn((args.y, args.z))
print(
'y:\t\t {:05.2f}\n'.format(args.y),
'z:\t\t {:05.2f}\n'.format(args.z),
'min_cmd: {:05.2f}'.format(min_cmd_result), sep='')
def get_parser():
# create the top-level parser
parser = ArgumentParser(prog='PROG')
subparsers = parser.add_subparsers(help='sub-command help')
# create the parser for the "sum" command
parser_a = subparsers.add_parser('sum_cmd', help='sum some integers')
parser_a.add_argument('-a', type=int,
help='an integer for the accumulator')
parser_a.add_argument('-b', type=int,
help='an integer for the accumulator')
parser_a.add_argument('--sum', dest='sm', action='store_const',
const=sum, default=max,
help='sum the integers (default: find the max)')
# create the parser for the "min" command
parser_b = subparsers.add_parser('min_cmd', help='min some integers')
parser_b.add_argument('-y', type=float,
help='an float for the accumulator')
parser_b.add_argument('-z', type=float,
help='an float for the accumulator')
parser_b.add_argument('--min', dest='mn', action='store_const',
const=min, default=0,
help='smallest integer (default: 0)')
return parser
if __name__ == '__main__':
main()
I had more or less the same requirements: Being able to set global arguments and being able to chain commands and execute them in order of command line.
I ended up with the following code. I did use some parts of the code from this and other threads.
# argtest.py
import sys
import argparse
def init_args():
def parse_args_into_namespaces(parser, commands):
'''
Split all command arguments (without prefix, like --) in
own namespaces. Each command accepts extra options for
configuration.
Example: `add 2 mul 5 --repeat 3` could be used to a sequencial
addition of 2, then multiply with 5 repeated 3 times.
'''
class OrderNamespace(argparse.Namespace):
'''
Add `command_order` attribute - a list of command
in order on the command line. This allows sequencial
processing of arguments.
'''
globals = None
def __init__(self, **kwargs):
self.command_order = []
super(OrderNamespace, self).__init__(**kwargs)
def __setattr__(self, attr, value):
attr = attr.replace('-', '_')
if value and attr not in self.command_order:
self.command_order.append(attr)
super(OrderNamespace, self).__setattr__(attr, value)
# Divide argv by commands
split_argv = [[]]
for c in sys.argv[1:]:
if c in commands.choices:
split_argv.append([c])
else:
split_argv[-1].append(c)
# Globals arguments without commands
args = OrderNamespace()
cmd, args_raw = 'globals', split_argv.pop(0)
args_parsed = parser.parse_args(args_raw, namespace=OrderNamespace())
setattr(args, cmd, args_parsed)
# Split all commands to separate namespace
pos = 0
while len(split_argv):
pos += 1
cmd, *args_raw = split_argv.pop(0)
assert cmd[0].isalpha(), 'Command must start with a letter.'
args_parsed = commands.choices[cmd].parse_args(args_raw, namespace=OrderNamespace())
setattr(args, f'{cmd}~{pos}', args_parsed)
return args
#
# Supported commands and options
#
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--print', action='store_true')
commands = parser.add_subparsers(title='Operation chain')
cmd1_parser = commands.add_parser('add', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
cmd1_parser.add_argument('add', help='Add this number.', type=float)
cmd1_parser.add_argument('-r', '--repeat', help='Repeat this operation N times.',
default=1, type=int)
cmd2_parser = commands.add_parser('mult', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
cmd2_parser.add_argument('mult', help='Multiply with this number.', type=float)
cmd2_parser.add_argument('-r', '--repeat', help='Repeat this operation N times.',
default=1, type=int)
args = parse_args_into_namespaces(parser, commands)
return args
#
# DEMO
#
args = init_args()
# print('Parsed arguments:')
# for cmd in args.command_order:
# namespace = getattr(args, cmd)
# for option_name in namespace.command_order:
# option_value = getattr(namespace, option_name)
# print((cmd, option_name, option_value))
print('Execution:')
result = 0
for cmd in args.command_order:
namespace = getattr(args, cmd)
cmd_name, cmd_position = cmd.split('~') if cmd.find('~') > -1 else (cmd, 0)
if cmd_name == 'globals':
pass
elif cmd_name == 'add':
for r in range(namespace.repeat):
if args.globals.print:
print(f'+ {namespace.add}')
result = result + namespace.add
elif cmd_name == 'mult':
for r in range(namespace.repeat):
if args.globals.print:
print(f'* {namespace.mult}')
result = result * namespace.mult
else:
raise NotImplementedError(f'Namespace `{cmd}` is not implemented.')
print(10*'-')
print(result)
Below an example:
$ python argstest.py --print add 1 -r 2 mult 5 add 3 mult -r 5 5
Execution:
+ 1.0
+ 1.0
* 5.0
+ 3.0
* 5.0
* 5.0
* 5.0
* 5.0
* 5.0
----------
40625.0
Another package which supports parallel parsers is "declarative_parser".
import argparse
from declarative_parser import Parser, Argument
supported_formats = ['png', 'jpeg', 'gif']
class InputParser(Parser):
path = Argument(type=argparse.FileType('rb'), optional=False)
format = Argument(default='png', choices=supported_formats)
class OutputParser(Parser):
format = Argument(default='jpeg', choices=supported_formats)
class ImageConverter(Parser):
description = 'This app converts images'
verbose = Argument(action='store_true')
input = InputParser()
output = OutputParser()
parser = ImageConverter()
commands = '--verbose input image.jpeg --format jpeg output --format gif'.split()
namespace = parser.parse_args(commands)
and namespace becomes:
Namespace(
input=Namespace(format='jpeg', path=<_io.BufferedReader name='image.jpeg'>),
output=Namespace(format='gif'),
verbose=True
)
Disclaimer: I am the author. Requires Python 3.6. To install use:
pip3 install declarative_parser
Here is the documentation and here is the repo on GitHub.
In order to parse the sub commands, I used the following (referred from argparse.py code). It parses the sub parser arguments and retains the help for both. Nothing additional passed there.
args, _ = parser.parse_known_args()
you can use the package optparse
import optparse
parser = optparse.OptionParser()
parser.add_option("-f", dest="filename", help="corpus filename")
parser.add_option("--alpha", dest="alpha", type="float", help="parameter alpha", default=0.5)
(options, args) = parser.parse_args()
fname = options.filename
alpha = options.alpha