Are there best practices or style guidelines for working with Python's argparse
module?
I work with argparse
on a regular basis, and it quickly takes up a respectable number of lines to handle all the configuration. For almost everything I find that sticking close to PEP 8 results in clean, readable code, but not here. The end result is always an ugly block of code that is painful to read.
Painful to read is not Pythonic:
Beautiful is better than ugly ... Readibilty counts
So is there a PEP or some other resource that provides guidelines for how to better format this code?
A sample of the ugliness (mostly following PEP 8):
parser = argparse.ArgumentParser(description='A nontrivial modular command')
subparsers = parser.add_subparsers(help='sub-command help')
parser_load = subparsers.add_parser('load', help='Load something somewhere')
parser_load.add_argument('--config',
help='Path to configuration file for special settings')
parser_load.add_argument('--dir', default=os.getcwd(),
help='The directory to load')
parser_load.add_argument('book', help='The book to load into this big thing')
parser_load.add_argument('chapter', nargs='?', default='',
help='Optionally specify a chapter')
parser_load.add_argument('verse', nargs='*',
help='Optionally pick as many verses as you want to'
' load')
parser_load.set_defaults(command='load')
parser_write = subparsers.add_parser(
'write', help='Execute commands defined in a config file')
parser_write.add_argument('config', help='The path to the config file')
parser_write.set_defaults(command='write')
parser_save = subparsers.add_parser(
'save',
help='Save this big thing for use somewhere later')
parser_save.add_argument('-n', '--name', default=None,
help='The name of the component to save')
parser_save.add_argument('path', help="The way out of Plato's cave")
parser_save.set_defaults(command='save')
...
args = parser.parse_args()
If you plan to be a software developer with Python, you'll want to be able to use argparse for your scripting needs. If you're a data scientist, you'll likely find yourself needing to port your code from a Jupyter Notebook to a reproducible script.
Using the nargs parameter in add_argument() , you can specify the number (or arbitrary number) of inputs the argument should expect. In this example named sum.py , the --value argument takes in 3 integers and will print the sum.
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.
It's fine to put the import argparse within the if __name__ == '__main__' block if argparse is only referred to within that block.
There is nothing wrong with your code, that's just the consequence of using the argparse
module. My personal preference is to break up the creation of the parser into functions. In this case, you can create a function for each subparser you create.
def parse_args(args=sys.argv[1:]):
parser = argparse.ArgumentParser(description='A nontrivial modular command')
subparsers = parser.add_subparsers(help='sub-command help')
add_load_subparser(subparsers)
add_write_subparser(subparsers)
add_save_subparser(subparsers)
return parser.parse_args(args)
def add_load_subparser(subparsers):
parser = subparsers.add_parser('load', help='Load something somewhere')
parser.add_argument('--config',
help='Path to configuration file for special settings')
parser.add_argument('--dir', default=os.getcwd(),
help='The directory to load')
parser.add_argument('book', help='The book to load into this big thing')
parser.add_argument('chapter', nargs='?', default='',
help='Optionally specify a chapter')
parser.add_argument('verse', nargs='*',
help='Optionally pick as many verses as you want to'
' load')
parser.set_defaults(command='load')
def add_write_subparser(subparsers):
parser = subparsers.add_parser(
'write', help='Execute commands defined in a config file')
parser.add_argument('config', help='The path to the config file')
parser.set_defaults(command='write')
def add_save_subparser(subparsers):
parser = subparsers.add_parser(
'save',
help='Save this big thing for use somewhere later')
parser.add_argument('-n', '--name', default=None,
help='The name of the component to save')
parser.add_argument('path', help="The way out of Plato's cave")
parser.set_defaults(command='save')
args = parse_args()
As commented by TemporalWolf, I would use line breaks more consistently, and more of them. Even if the code now appears longer, I find it easier to read:
help
string) requiredAdditionally, by renaming parser_X
/parser_Y
→ X_parser
/Y_parser
you could make it easier to distinguish X
/Y
.
parser = argparse.ArgumentParser(
description='A nontrivial modular command'
)
subparsers = parser.add_subparsers(
help='sub-command help'
)
load_parser = subparsers.add_parser(
'load',
help='Load something somewhere'
)
load_parser.add_argument(
'--config',
help='Path to configuration file for special settings'
)
load_parser.add_argument(
'--dir',
default=os.getcwd(),
help='The directory to load'
)
load_parser.add_argument(
'book',
help='The book to load into this big thing'
)
load_parser.add_argument(
'chapter',
nargs='?',
default='',
help='Optionally specify a chapter'
)
load_parser.add_argument(
'verse',
nargs='*',
help='Optionally pick as many verses as you want to load'
)
load_parser.set_defaults(
command='load'
)
write_parser = subparsers.add_parser(
'write',
help='Execute commands defined in a config file'
)
write_parser.add_argument(
'config',
help='The path to the config file'
)
write_parser.set_defaults(
command='write'
)
save_parser = subparsers.add_parser(
'save',
help='Save this big thing for use somewhere later'
)
save_parser.add_argument(
'-n', '--name',
default=None,
help='The name of the component to save'
)
save_parser.add_argument(
'path',
help="The way out of Plato's cave"
)
save_parser.set_defaults(
command='save'
)
...
args = parser.parse_args()
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