Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Argument Parsing Validation Best Practices

Is it possible when using the argparse module to add validation when parsing arguments?

from argparse import ArgumentParser  parser = ArgumentParser(description='Argument parser for PG restore')  parser.add_argument('--database', dest='database',                     default=None, required=False, help='Database to restore')  parser.add_argument('--backup', dest='backup',                     required=True, help='Location of the backup file')  parsed_args = parser.parse_args() 

Would it be possible, to add a validation check to this argument parser, to make sure the backup file / database exist? Rather than having to add an extra check after this for every parameter such as:

from os.path import exists if not database_exists(parsed_args.database):     raise DatabaseNotFoundError if not exists(parsed_args.backup):     raise FileNotFoundError 
like image 952
AK47 Avatar asked May 26 '16 21:05

AK47


1 Answers

The argparse.FileType is a type factory class that can open a file, and of course, in the process raise an error if the file does not exist or cannot be created. You could look at its code to see how to create your own class (or function) to test your inputs.

The argument type parameter is a callable (function, etc) that takes a string, tests it as needed, and converts it (as needed) into the kind of value you want to save to the args namespace. So it can do any kind of testing you want. If the type raises an error, then the parser creates an error message (and usage) and exits.

Now whether that's the right place to do the testing or not depends on your situation. Sometimes opening a file with FileType is fine, but then you have to close it yourself, or wait for the program to end. You can't use that open file in a with open(filename) as f: context. The same could apply to your database. In a complex program you may not want to open or create the file right away.

I wrote for a Python bug/issue a variation on FileType that created a context, an object that could be used in the with context. I also used os tests to check if the file existed or could be created, without actually doing so. But it required further tricks if the file was stdin/out that you don't want to close. Sometimes trying to do things like this in argparse is just more work than it's worth.

Anyways, if you have an easy testing method, you could wrap it in a simple type function like this:

def database(astring):     from os.path import exists     if not database_exists(astring):         raise ValueError  # or TypeError, or `argparse.ArgumentTypeError     return astring  parser.add_argument('--database', dest='database',                 type = database,                  default=None, required=False, help='Database to restore') 

I don't think it matters a whole lot whether you implement testing like this in the type or Action. I think the type is simpler and more in line with the developer's intentions.

like image 156
hpaulj Avatar answered Sep 24 '22 23:09

hpaulj