From 03371459f0372f312073b7af84f5754526d04f10 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 10 Jan 2017 16:11:22 -0800 Subject: alot/utils/argparse: Add a new argparse validators infrastructure This adds a new argparse.Action class validates input using a new keyword argument that takes a validator function. This will allow us to replace the use the type keyword as a validator, which is both more correct, and frees up the type keyword to do what it's actually meant to do, convert the input from one type to another. It also adds 3 new validator functions that will be enabled in the next commit. One that checks for a required file, one that checks for an optional directory, and one that looks for a required file, fifo, or block special device (/dev/null). --- alot/utils/argparse.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'alot/utils') diff --git a/alot/utils/argparse.py b/alot/utils/argparse.py index 4922c247..adacdd6e 100644 --- a/alot/utils/argparse.py +++ b/alot/utils/argparse.py @@ -20,13 +20,22 @@ from __future__ import absolute_import import argparse +import collections +import functools import itertools +import os +import stat _TRUEISH = ['true', 'yes', 'on', '1', 't', 'y'] _FALSISH = ['false', 'no', 'off', '0', 'f', 'n'] +class ValidationFailed(Exception): + """Exception raised when Validation fails in a ValidatedStoreAction.""" + pass + + def _boolean(string): string = string.lower() if string in _FALSISH: @@ -38,6 +47,56 @@ def _boolean(string): ', '.join(itertools.chain(iter(_TRUEISH), iter(_FALSISH))))) +def _path_factory(check): + """Create a function that checks paths.""" + + @functools.wraps(check) + def validator(paths): + if isinstance(paths, basestring): + check(paths) + elif isinstance(paths, collections.Sequence): + for path in paths: + check(path) + else: + raise Exception('expected either basestr or sequenc of basstr') + + return validator + + +@_path_factory +def require_file(path): + """Validator that asserts that a file exists. + + This fails if there is nothing at the given path. + """ + if not os.path.isfile(path): + raise ValidationFailed('{} is not a valid file.'.format(path)) + + +@_path_factory +def optional_file_like(path): + """Validator that ensures that if a file exists it regular, a fifo, or a + character device. The file is not required to exist. + + This includes character special devices like /dev/null. + """ + if (os.path.exists(path) and not (os.path.isfile(path) or + stat.S_ISFIFO(os.stat(path).st_mode) or + stat.S_ISCHR(os.stat(path).st_mode))): + raise ValidationFailed( + '{} is not a valid file, character device, or fifo.'.format(path)) + + +@_path_factory +def require_dir(path): + """Validator that asserts that a directory exists. + + This fails if there is nothing at the given path. + """ + if not os.path.isdir(path): + raise ValidationFailed('{} is not a valid directory.'.format(path)) + + class BooleanAction(argparse.Action): """Argparse action that can be used to store boolean values.""" def __init__(self, *args, **kwargs): @@ -47,3 +106,31 @@ class BooleanAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, values) + + +class ValidatedStoreAction(argparse.Action): + """An action that allows a validation function to be specificied. + + The validator keyword must be a function taking exactly one argument, that + argument is a list of strings or the type specified by the type argument. + It must raise ValidationFailed with a message when validation fails. + """ + + def __init__(self, option_strings, dest=None, nargs=None, default=None, + required=False, type=None, metavar=None, help=None, + validator=None): + super(ValidatedStoreAction, self).__init__( + option_strings=option_strings, dest=dest, nargs=nargs, + default=default, required=required, metavar=metavar, type=type, + help=help) + + self.validator = validator + + def __call__(self, parser, namespace, values, option_string=None): + if self.validator: + try: + self.validator(values) + except ValidationFailed as e: + raise argparse.ArgumentError(self, str(e)) + + setattr(namespace, self.dest, values) -- cgit v1.2.3