Automatically register function arguments to argparse in Python

Introduction

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.

Difference from Click

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.

Function argument parsing

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])

Automatic registration of arguments to argparse

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])

Support for variadic arguments

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.

Correspondence to bool value, enable, disable ...

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)
		)

Argument support that does not have a default value

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()

Conclusion

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

Automatically register function arguments to argparse in Python
Included notation in Python function arguments
Precautions when giving default values to arguments in Python function definitions
About function arguments (python)
How to receive command line arguments in Python
Try auto to automatically price Enums in Python 3.6
To execute a Python enumerate function in JavaScript
Python: About function arguments
[Road to Python Intermediate] Define __getattr__ function in class
Set constructor keyworded arguments to mock attributes in Python
Execute Python function from Powershell (how to pass arguments)
Create a function in Python
To flush stdout in Python
Use callback function in Python
ntile (decile) function in python
Login to website in Python
Receive runtime arguments in Python 2.7
How to use Python argparse
Nonlinear function modeling in Python
Draw implicit function in python
Speech to speech in python [text to speech]
Immediate function in python (lie)
How to develop in Python
Post to Slack in Python
Create a Mastodon bot with a function to automatically reply with Python
How to sample from any probability density function in Python
To return char * in a callback function using ctypes in Python
I tried to implement the mail sending function in Python
How to log in to AtCoder with Python and submit automatically
[Python] How to do PCA in Python
Implement R's power.prop.test function in python
Function argument type definition in python
Convert markdown to PDF in Python
How to collect images in Python
Automatically format Python code in Vim
How to use SQLite in Python
Write AWS Lambda function in Python
Measure function execution time in Python
In the python command python points to python3.8
Try to calculate Trace in Python
Try PLC register access in Python
How to use Mysql in python
How to wrap C in Python
How to use ChemSpider in Python
6 ways to string objects in Python
How to use PubChem in Python
Function synthesis and application in Python
Pass arguments to Task in discord.py
Include "%" in argparse help to die
How to use python zip function
How to handle Japanese in Python
An alternative to `pause` in Python
[Python] Function arguments * (star) and ** (double star)
Things to watch out for when using default arguments in Python
How to pass arguments to a Python script in SPSS Modeler Batch
I wrote a function to load a Git extension script in Python
[For beginners] How to register a library created in Python in PyPI
Try to log in to Netflix automatically using python on your PC
I tried to implement PLSA in Python
[Introduction to Python] How to use class in Python?
Try logging in to qiita with Python