Python 2.7's argparse gives you two extension points where you can control how your command-line arguments are parsed: type functions and action classes.
Going from the built-in types and actions, the best practice seems to be that type functions should contain the validation/initialization code and actions should be concerned with storing values into namespace. The problem with this approach is when you have type-checking code that has side effects. Consider this simple example:
from argparse import ArgumentParser, FileType
argp = ArgumentParser()
argp.add_argument('-o', type=FileType('w'), default='myprog.out')
argp.parse_args(['-o', 'debug.out'])
If you run this, you find python will open two files on the system, myprog.out
and debug.out
. It would make more sense to only open debug.out
when the user doesn't supply the -o
argument.
Poking around a bit, it seems that argparse will only call your type function on passed arguments or default arguments of type str. This is unfortunate if your type-checker has side-effects as it will be called on the default even if a value was passed. So for initialization with side-effects, maybe it would be better to do it in the action. The problem with this is that the action won't be called if you supply a default value!
Consider the following code:
from argparse import ArgumentParser, Action
def mytype(arg):
print 'checking type for ' + repr(arg)
return arg
class OutputFileAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
print 'running action for ' + repr(values)
try:
outstream = open(values, 'w')
except IOError as e:
raise ArgumentError('error opening file ' + values)
setattr(namespace, self.dest, outstream)
argp = ArgumentParser()
argp.add_argument('-o', type=mytype, action=OutputFileAction, default='myprog.out')
Now try to use it:
>>> argp.parse_args([])
checking type for 'myprog.out'
Namespace(o='myprog.out')
>>> argp.parse_args(['-o', 'debug.out'])
checking type for 'myprog.out'
checking type for 'debug.out'
running action for 'debug.out'
Namespace(o=<open file 'debug.out', mode 'w' at 0x2b7fced07300>)
Who ordered that behavior? Is there a sane way to have default values behave exactly as if they were passed in by the user? Or to not typecheck defaults when values are supplied?
action defines how to handle command-line arguments: store it as a constant, append into a list, store a boolean value etc. There are several built-in actions available, plus it's easy to write a custom one.
required is a parameter of the ArugmentParser object's function add_argument() . By default, the arguments of type -f or --foo are optional and can be omitted. If a user is required to make an argument, they can set the keyword argument required to True .
The argparse module makes it easy to write user-friendly command-line interfaces. It parses the defined arguments from the sys. argv . The argparse module also automatically generates help and usage messages, and issues errors when users give the program invalid arguments.
The store_true option automatically creates a default value of False. Likewise, store_false will default to True when the command-line argument is not present. The source for this behavior is succinct and clear: http://hg.python.org/cpython/file/2.7/Lib/argparse.py#l861.
As far as I am aware, there is no "sensible" way to do this. Of course, it is trivial to leave the type
conversion off and then postprocess the Namespace
returned from parse_args
:
args = argp.parse_args()
args.o = open(args.o,'w')
but I suppose that isn't what you're looking for.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With