argparse is a useful package for creating command line scripts with optional arguments in python. It's a convenient package, but I made dozens of times to make arguments firmly with a function and write it again with argparse, and I was driven by the thought that this work was useless. So let's automate it.
In the comment, I was told that I was doing the same thing as click, and I thought that I had reinvented the wheel again for a while, but when I tried using click, it seems that it is different, so I will add it. .. The function of click is that the definitions of options and arguments written in argparse can be distributed to each function as a decorator, and the argument of the function is not automatically registered as an option. After all, instead of add_argument, just write @ click.option and the content you are writing does not change that much. That doesn't mean that click is bad, it's a different purpose. Click is a great package in terms of organizing and writing and improving readability. However, this time I wrote it roughly for the purpose of "automatically register all the arguments of the created function as command line arguments!", So the following consideration may still be useful for scripts that are simple but have many variables. unknown··. On the contrary, I tried using click and found many functions that might be useful in other scenes, so I will summarize the contents separately next time.
A package called inspect is used for function argument analysis. Since I am using python2.7, I use the old getargspec, but for python3, use getfullargspec.
from inspect import getargspec
def func(aaa=1, bbb="a", ccc=[1,2,3,]):
pass
args, varargs, keywords, defaults = getargspec(func)
print 'args: ', args
print 'varargs', varargs
print 'keywords: ', keywords
print 'defaults: ', defaults
When I run the above, I get the following output:
args: ['aaa', 'bbb', 'ccc']
varargs None
keywords: None
defaults: (1, 'a', [1, 2, 3])
Use this information to automatically register argparse options.
from inspect import getargspec
def func(aaa=1, bbb="a", ccc=[1,2,3,]):
pass
args, varargs, keywords, defaults = getargspec(func)
import argparse
parser = argparse.ArgumentParser()
for arg, default in zip(args, defaults):
parser.add_argument(
'--' + arg,
default=default,
type=type(default)
)
print parser.parse_args()
Doing the above will give you the following Namespace object:
Namespace(aaa=1, bbb='a', ccc=[1, 2, 3])
Actually, in the above case, the correspondence to variable length arguments is not enough. The code is a little longer, but with the addition of support for variadic arguments:
from inspect import getargspec
def func(aaa=1, bbb="a", ccc=[1,2,3,]):
pass
args, varargs, keywords, defaults = getargspec(func)
import argparse
parser = argparse.ArgumentParser()
for arg, default in zip(args, defaults):
if type(default) is tuple or type(default) is list:
parser.add_argument(
'--' + arg,
default=default,
type=type(default),
nargs='+'
)
else:
parser.add_argument(
'--' + arg,
default=default,
type=type(default)
)
Specifically, if the argument is a list or tuple, add the nargs argument to add_argument.
In the case of a bool value argument, the value is uniquely determined when it is not the default, so it is common to specify the acronym disable, enable instead of specifying True, False to make it an option that does not take a value. .. Including this correspondence, it becomes as follows.
from inspect import getargspec
def func(aaa=1, bbb="a", ccc=[1,2,3,], ddd=True, eee=False):
pass
args, varargs, keywords, defaults = getargspec(func)
import argparse
parser = argparse.ArgumentParser()
for arg, default in zip(args, defaults):
if type(default) is tuple or type(default) is list:
parser.add_argument(
'--' + arg,
default=default,
type=type(default),
nargs='+'
)
elif type(default) is bool and default:
parser.add_argument(
'--disable_' + arg,
dest=arg,
default=default,
action="store_false"
)
elif type(default) is bool and not default:
parser.add_argument(
'--enable_' + arg,
dest=arg,
default=default,
action="store_true"
)
else:
parser.add_argument(
'--' + arg,
default=default,
type=type(default)
)
Last but not least, arguments that do not have default values are also supported. However, keep in mind that this argument is recognized as a string because its type is unknown.
from inspect import getargspec
def func(x, aaa=1, bbb="a", ccc=[1,2,3,], ddd=True, eee=False):
pass
args, varargs, keywords, defaults = getargspec(func)
print 'args: ', args
print 'varargs', varargs
print 'keywords: ', keywords
print 'defaults: ', defaults
parser = argparse.ArgumentParser()
while len(args) > len(defaults):
l = list(defaults)
l.insert(0, None)
defaults = tuple(l)
for arg, default in zip(args, defaults):
if default is None:
parser.add_argument(dest=arg)
elif type(default) is tuple or type(default) is list:
parser.add_argument(
'--' + arg,
default=default,
type=type(default),
nargs='+'
)
elif type(default) is bool and default:
parser.add_argument(
'--disable_' + arg,
dest=arg,
default=default,
action="store_false"
)
elif type(default) is bool and not default:
parser.add_argument(
'--enable_' + arg,
dest=arg,
default=default,
action="store_true"
)
else:
parser.add_argument(
'--' + arg,
default=default,
type=type(default)
)
print parser.parse_args()
By using inspect and argparse, it was possible to automatically set the argument of the function to the argument of argparse. The final version of the code is attached below.
extract_arguments.py
#!/usr/bin/env python
import argparse
from inspect import getargspec
def set_arguments(parser, func):
args, varargs, keywords, defaults = getargspec(func)
print 'args: ', args
print 'varargs', varargs
print 'keywords: ', keywords
print 'defaults: ', defaults
while len(args) > len(defaults):
l = list(defaults)
l.insert(0, None)
defaults = tuple(l)
for arg, default in zip(args, defaults):
if default is None:
parser.add_argument(dest=arg)
elif type(default) is tuple or type(default) is list:
parser.add_argument(
'--' + arg,
default=default,
type=type(default),
nargs='+'
)
elif type(default) is bool and default:
parser.add_argument(
'--disable_' + arg,
dest=arg,
default=default,
action="store_false"
)
elif type(default) is bool and not default:
parser.add_argument(
'--enable_' + arg,
dest=arg,
default=default,
action="store_true"
)
else:
parser.add_argument(
'--' + arg,
default=default,
type=type(default)
)
return parser
def func(x, aaa=1, bbb="a", ccc=[1,2,3,], ddd=True, eee=False):
pass
parser = argparse.ArgumentParser()
parser = set_arguments(parser, func)
print parser.parse_args()
The following are the decorators for convenience.
register_arguments.py
#!/usr/bin/env python
import argparse
from inspect import getargspec
def register_arguments(parser, func):
args, varargs, keywords, defaults = getargspec(func)
while len(args) > len(defaults):
l = list(defaults)
l.insert(0, None)
defaults = tuple(l)
for arg, default in zip(args, defaults):
if default is None:
parser.add_argument(dest=arg)
elif type(default) is tuple or type(default) is list:
parser.add_argument(
'--' + arg,
default=default,
type=type(default),
nargs='+'
)
elif type(default) is bool and default:
parser.add_argument(
'--disable_' + arg,
dest=arg,
default=default,
action="store_false"
)
elif type(default) is bool and not default:
parser.add_argument(
'--enable_' + arg,
dest=arg,
default=default,
action="store_true"
)
else:
parser.add_argument(
'--' + arg,
default=default,
type=type(default)
)
return parser
def register_argparse(parser):
def wrapper(f):
register_arguments(parser, f)
return f
return wrapper
if __name__ == '__main__':
parser = argparse.ArgumentParser()
@register_argparse(parser)
def func(x, aaa=1, bbb="a", ccc=[1,2,3,], ddd=True, eee=False):
pass
print parser.parse_args()
When the above is executed, the following output is output.
$ python ./register_argments.py a
Namespace(aaa=1, bbb='a', ccc=[1, 2, 3], ddd=True, eee=False, x='a')
Recommended Posts