Currently, I am using argparse to parse arguments and store flags as boolean options. I then check to see which flag is set to true and execute that function. Argparse parses an input file, which is opened and passed to the called function as an argument.
So:
parser.add_argument('input_data', action='store', help='some help')
parser.add_argument('outputname', action='store',default=None, help='some help')
parser.add_argument('--flag','-f', action='store_true', dest='flag', default=False, help='help!')
I have to open the input_data to read some information from it before the flag function is called. This is currently implemented as:
if args.flag == True:
array_out = flag(array_read_from_input)
if args.outputname == None:
name = 'Flag.tif'
It is possible to subclass argparse to have the action keyword call a function.
Is it possible to parse the input_data option, perform some processing, and then call the flag function without having nested if loops for each argument, eg., by subclassing argparse's action parameter?
Is it possible to parse the input_data option, perform some
processing, and then call the flag function without having nested if
loops for each argument, eg., by subclassing argparse's action
parameter?
As per your question;
class FooAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
<< some processing of values >>
array_out = flag(values)
setattr(namespace, self.dest, array_out)
parser = argparse.ArgumentParser()
parser.add_argument('input_data', action=FooAction, help='some help')
Related
I have an argparse program with multiple subparsers. IF the value of a top-level argument is not specified, I would like it to depend on the subparser selection. This can be partially accomplished by using set_defaults() on each subparser. However, the passed value does NOT act like a true default in that it takes precedence even when the top-level argument is explicitly provided.
Am I missing something in the argparse module that would allow the subparser-specific defaults to yield to the top-level parser? Below is a toy example which demonstrates my desired use case
import argparse
# Top-level parser
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--time', type=str)
# Add subparsers with options
actions = parser.add_subparsers(dest='action', metavar='action')
actions.required = True
eat = actions.add_parser('eat')
eat.add_argument('-f', '--food', type=str, default='pizza')
order = actions.add_parser('order')
order.add_argument('-i', '--item', type=int, required=True)
# Attempt to set main parser's --time depending on subparser selection
eat.set_defaults(time='now')
order.set_defaults(time='later')
# Tests that work as expected
print(parser.parse_args(['order', '--item', 0])) # Namespace(action='order', item=0, time='later')
print(parser.parse_args(['eat'])) # Namespace(action='eat', food='pizza', time='now')
# Tests that DO NOT work
# Actual result: the --time flag of `parser` is ignored in preference of each subparser's default
# Desired result: explicit usage of --time should override the subparser defaults like the comments below
# Namespace(action='order', item=0, time='before')
# Namespace(action='eat', food='pizza', time='after')
print(parser.parse_args(['--time', 'before', 'order', '--item', 0]))
print(parser.parse_args(['--time', 'after', 'eat']))
It seems that the sub-parser args, overwrite the Namespace
entries created from the top-level parser, because they have
the same name. So the Namespace ends up with only one 'time'
entry (instead of one for the top-level parser, and one for
the sub-parser); its value is the last value written to it:
that of the sub-parser.
Alternatively, maybe only use the arg in the main parser,
and alter it's default value (in the main parser),
before using a specific subparser?
import argparse
parserMain = argparse.ArgumentParser()
parserMain.add_argument('--arg', type=str)
subparsers = parserMain.add_subparsers()
parserX = subparsers.add_parser('x')
# This would overwrite the value of 'arg' in the Namespace:
#parserX.set_defaults(arg='defaultValForX')
# So, set default in main parser, before using sub-parser:
parserMain.set_defaults(arg='defaultValForX')
print(parserMain.parse_args(['x']))
print(parserMain.parse_args(['--arg', 'explicitVal', 'x']))
parserY = subparsers.add_parser('y')
# This would overwrite the value of 'arg' in the Namespace:
#parserY.set_defaults(arg='defaultValForY')
# So, set default in main parser, before using sub-parser:
parserMain.set_defaults(arg='defaultValForY')
print(parserMain.parse_args(['y']))
print(parserMain.parse_args(['--arg', 'explicitVal', 'y']))
What's the best way to set a group argument as the default when no arguments are called.
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("--a", action="store_true") #call when no arguments are provided
group.add_argument("--b", action="store_true")
group.add_argument("--c", action="store_true")
Let's call my program argparse_ex.py. I want argparse.py (with no arguments) and argparse.py --a to return the same output.
I would just add a simple test after parsing
if not any([args.a, args.b, args.c]):
args.a=True
This is simpler than any attempt to make parse_args to do this. The parser will parse all arguments independently - and in any order. So you really can't tell until the parsing is all done whether any of the options has been selected or not.
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--pattern", help="Pattern file")
args = parser.parse_args()
Now is it possible to get back the string "--pattern" from args?
I need the string so that I can construct a cmd list to pass to Popen like Popen(['some_other_program', args.pattern.option_string, args.pattern], ...) without repeating it (and having to maintain it in two places) (Popen(['some_other_prog', '--pattern', args.pattern], ...)).
I need to create a wrapper for another program. Some of the args need to be passed to the wrapped program (via Popen) and some are required by the wrapper.
Is there a better method than the following example?
pass_1 = '--to-be-passed'
parser = argparse.ArgumentParser()
parser.add_argument("-p", pass_1, help="Pass me on")
parser.add_argument("-k", "--arg-for-wrapper")
args = parser.parse_args()
...
process = Popen(['wrapped_program', pass_1, args.pass_1], ...)
...
This method of keeping the args in variables is not very good as:
Maintaining short options along with long options becomes difficult.
Popen if called in another function requires passing these variables(or a dict of them) to the function. This seems redundant as args passed to it should be sufficient.
Add a dest to your add_argument call.
parser.add_argmument("p", "--pattern", dest="pattern", help="your help text")
args = parser.parse_args()
args = vars(args)
The you can reference the pattern with args["pattern"] .
There doesn't seem to be an easy way to get the original option strings from the result of a parser.parse_args(), but you can get them from the parser object. You just need to peek into its __dict__, in order to retrieve the parser settings after it's created. In your case you want the _option_string_actions field. Unfortunately this doesn't seem officially supported, as I couldn't find a ArgumentParser method dedicated to this, so YMMV. On Python 3:
Demo:
parser = argparse.ArgumentParser()
parser.add_argument('--foo', '-f', type=int, default=1000, help='intensity of bar')
parser.add_argument('--bar', '-b', type=str, default='bla', help='whatever')
store_action_dict=vars(parser)['_option_string_actions']
print(store_action_dict.keys()) # dict_keys(['--help', '-b', '-f', '-h', '--foo', '--bar'])
The deleted answers and comments indicate there is some confusion as to what you want. So I'll add to that confusion.
Normally the parser does not record the option string. However it is provided to the Action __call__ method. So a custom Action class could save it. The FooAction custom class example in the argparse docs illustrates this.
If I define this action subclass:
In [324]: class PassThru(argparse._StoreAction):
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, [values, option_string])
In [324]: p.add_argument('-o','--other',action=PassThru)
The option string is recorded along with the value ('-o' or '--other'):
In [322]: p.parse_args('-p test -o teseting'.split())
Out[322]: Namespace(other=['teseting', '-o'], pass_me_on='test')
In [323]: p.parse_args('-p test --other teseting'.split())
Out[323]: Namespace(other=['teseting', '--other'], pass_me_on='test')
Obviously the option_string and value could be recorded in a different order, in a dictionary, as seperate attributes in the Namespace, etc.
There are other ways of passing options to another program, particularly if the wrapping parser does not need to handle them itself.
argparse gets the arguments from sys.argv[1:], and does not change it. So even if your parser uses some of the arguments, you could pass that list on to popen (all or in part).
The argparse docs has an example, under nargs=REMAINDER, of parsing some arguments for itself, and collecting the rest to pass to another program. This is their example:
>>> 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')
So you could call popen with something like
plist = ['wrapped_program']
plist.extend(args.args)
popen(plist, ...)
Using parse.parse_known_args can also be used to collect unparsed words into an 'extras' list. That section of the docs talks about passing those strings on to another program (just as you are doing). In contrast with the REMAINDER case, the extra stuff does not have to be last.
These work, of course, only if this parser doesn't need --pattern for itself. If it parses it, then it won't appear appear in the REMAINDER or extras. In that case you will have to add it back to the list that you give popen
I would tweak your parser thus:
pass_1 = 'passed' # without the -- or internal -
dpass_` = '--'+pass_
parser = argparse.ArgumentParser()
parser.add_argument("-p", dpass_1, help="Pass me on")
parser.add_argument("-k", "--arg-for-wrapper")
args = parser.parse_args()
process = Popen(['wrapped_program', dpass_1, getattr(args, pass_1)], ...)
another option:
parser = argparse.ArgumentParser()
pass_action = parser.add_argument("-p", '--pass-me-on', help="Pass me on")
parser.add_argument("-k", "--arg-for-wrapper")
args = parser.parse_args()
If you print pass_action (in a shell) you'll get something like:
_StoreAction(option_strings=['-p', '--pass-me-on'], dest='pass_me_on', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
So you could pull the --name and dest from that object, thus:
process = Popen(['wrapped_program', pass_action.option_strings[-1], getattr(args, pass_action.dest), ...], ...)
You have to look in sys.argv to see which option_string was used (the long, short or other). The parser does not record that anywhere.
Note '--pass-me-on' produced dest='pass_me_on'. The conversion of - to _ can complicate deriving one string from the other.
If you have a dest string, you have to use getattr to pull it from the args namespace, or use vars(args)[dest] (dictionary access).
Another issue. If --patten has nargs='+', its value will be a list, as opposed to a string. You'd have to careful when merging that into thepopen` argument list.
I'm trying to disable same argument occurences within one command line, using argparse
./python3 --argument1=something --argument2 --argument1=something_else
which means this should raise an error, because value of argument1 is overriden, by default, argparse just overrides the value and continues like nothing happened... Is there any smart way how to disable this behaviour?
I don't think there is a native way to do it using argparse, but fortunately, argparse offers methods to report custom errors. The most elegant way is probably to define a custom action that checks for duplicates (and exits if there are).
class UniqueStore(argparse.Action):
def __call__(self, parser, namespace, values, option_string):
if getattr(namespace, self.dest, self.default) is not self.default:
parser.error(option_string + " appears several times.")
setattr(namespace, self.dest, values)
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--foo', action=UniqueStore)
args = parser.parse_args()
(Read the docs about cutom actions)
Another way is to use the append action and count the len of the list.
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--foo', action='append')
args = parser.parse_args()
if len(args.foo) > 1:
parser.error("--foo appears several times.")
There's no built in test or constraint. A positional argument will be handled only once, but the flagged (or optional) ones can, as you say, be repeated. This lets you collect multiple occurrences with append or count actions.
The override action is acceptable to most people. Why might your user use the option more than once? Why should the first be preferred over the last?
A custom Action may be the best choice. It could raise an error if the namespace[dest] already has a non-default value. Or this Action could add some other 'repeat' flag to the namespace.
I realize that the question is rather general but I didn't know exactly how to ask it for what I am doing, but here goes.
I want to create a tool that allows option in the following format which also uses custom actions:
tool.py {start|stop|restart|configure}
Each of the above commands are mutually exclusive and some can have separate unique options. All will call a custom action (subclassed argparse.Action).
tool.py start
The above will do nothing because no arguments (via "add_argument()") was defined.
I though about making a subparser, but doing so doesn't work initially unless you set default arguments, via "set_defaults()". However, doing this and setting:
class CustomAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
print('Args: %r %r %r' % (namespace, values, option_string))
setattr(namespace, self.dest, values)
parser = argparse.ArgumentParser(help="Basic daemon.")
subparsers = parser.add_subparsers()
start_parser = subparsers.add_parser("start")
start_parser.set_defaults(start=True, action=CustomAction)
doesn't seem to kick off the custom action as expected. Below is the output I get:
$ custom_parser.py start
Namespace(action=<class '__main__.BasicAction'>, start=True)
I can see that the values are being assigned, but NOT called.
I basically want to have exclusive parent options that can be specified without child argument but still allow exclusive sub-arguments like so, if desired:
tool.py configure {interval|recipients}
Any ideas?
You can use subparsers coupled with default functions
def start_something():
do_starting_actions()
def stop_something():
do_terminal_actions()
def parse_args():
parser = ArgumentParser()
subparsers = parser.add_subparsers()
start = subparsers.add_parser("start")
start.set_defaults(func=start_something)
stop = subparsers.add_parser("stop")
stop.set_defaults(func=stop_something)
# ...
return parser.parse_args()
def main():
args = parse_args()
args.func()
Then you can call the parser from the command line
mymodule.py start
If you wanted to extend the subparser you could do it like:
start = subparsers.add_parser("start")
start.add_argument("--foo")