diff options
| author | Yaakov M. Nemoy <[email protected]> | 2009-05-03 07:19:31 (GMT) |
|---|---|---|
| committer | Yaakov M. Nemoy <[email protected]> | 2009-05-03 07:19:31 (GMT) |
| commit | 1c7fd121639331ebd649e93ace9a7ada21572100 (patch) | |
| tree | 1d485f3a66c8ed1c21982cef25bff896eed37873 /devshell | |
| parent | 77af1df86ff2055de04dd8a30d3f332e0e6fdb8a (diff) | |
adds some new validators for path, chaining, and a fallback option
also fixes up validation code to be more robust, report errors more nicely and handles adding new params where they weren't provided for
Diffstat (limited to 'devshell')
| -rw-r--r-- | devshell/base/util.py | 10 | ||||
| -rw-r--r-- | devshell/base/validators.py | 92 |
2 files changed, 89 insertions, 13 deletions
diff --git a/devshell/base/util.py b/devshell/base/util.py index 53a3449..fd17aac 100644 --- a/devshell/base/util.py +++ b/devshell/base/util.py @@ -139,7 +139,15 @@ def flip(f): def strip_slash(path): return path[:-1] if path.endswith(sep) else path +def dict_list(iter): + '''[item] -> {index: item}''' + return dict(zip(range(len(iter)), iter)) + +def list_dict(d): + '''{index: item} -> [item]''' + return [d[key] for key in sorted(d)] __all__ = ['pwd', 'copy', 'with_sudo', 'with_su', 'symlink', 'move', 'log_file', 'one', 'remove_all', 'flatten', 'base_dir', - 'close_later', 'close_all', 'flip', 'strip_slash'] + 'close_later', 'close_all', 'flip', 'strip_slash', 'dict_list', + 'list_dict'] diff --git a/devshell/base/validators.py b/devshell/base/validators.py index b2977e5..f6aae2a 100644 --- a/devshell/base/validators.py +++ b/devshell/base/validators.py @@ -17,10 +17,14 @@ # import copy +import traceback from functools import wraps from inspect import isclass +from os.path import abspath +from devshell.base.base import log +from devshell.base.util import dict_list, list_dict class UnpositionedValidatorException(Exception): '''Raised when a validator doesn't know which position of an argument list to act on''' @@ -29,14 +33,21 @@ class UnpositionedValidatorException(Exception): class Validator(object): '''Basic core validator, handles argument positions by number or string''' - def __init__(self): - self.pos = False +# def __init__(self): +# self.pos = False + + @property + def pos(self): + if hasattr(self, '_pos'): + return self._pos + else: + raise UnpositionedValidatorException def __call__(self, pos): '''creates a clone of a validator bound to a specific location ''' new_validator = copy.copy(self) - new_validator.pos = pos + new_validator._pos = pos return new_validator def pos_validate(self, args, keys): @@ -45,16 +56,18 @@ class Validator(object): args : the arguments tuple passed to some function keys : they keywords dictionary passed to some function ''' - if not self.pos: - raise UnpositionedValidatorException - args = list(args) + args = dict_list(args) keys = dict(keys) if type(self.pos) is int: cont = args elif type(self.pos) is str: cont = keys - cont[self.pos] = self.validate(cont[self.pos]) - return args, keys + log.debug('cont is %s of type %s' % (cont, type(cont))) + try: + cont[self.pos] = self.validate(cont[self.pos]) + except KeyError, e: + cont[self.pos] = self.validate(None) + return list_dict(args), keys def validate(self, obj): '''Returns None always @@ -68,10 +81,16 @@ class Identity(Validator): def validate(obj): return obj - id = Identity() +class PathValidator(Validator): + '''Converts a path into an absolute path''' + def validate(self, path): + return abspath(path) + +path = PathValidator() + class TypeValidator(Validator): '''Basic type validator, makes sure the object is of a certain type, or uses a factory to recreate it as that type''' def __init__(self, base, factory): @@ -100,7 +119,7 @@ class TypeValidator(Validator): class LimitedTypeValidator(Validator): '''Limited type validator, makes sure the object is of a certain type, or uses a factory to recreate it as that type - + While a well written factory should do it's own type bounds checking, this lets you put additional restrictions on the factory. It could also be used for showing who is responsible for type checking ''' def __init__(self, base, factory, *types): @@ -116,9 +135,16 @@ class LimitedTypeValidator(Validator): self.types = types def validate(self, obj): - '''checks to see if first the obj is an instance of base, then to see if it's of one of the valid types, or an instance of some other subclass, and runs it through the factory until it's an instance of base. If it's none, it raises a TypeError + '''checks to see if first the obj is an instance of base, then to + see if it's of one of the valid types, or an instance of some + other subclass, and runs it through the factory until it's an + instance of base. If it's none, it raises a TypeError ''' + log.debug('object is ' + str(obj)) + log.debug('expected base is ' + str(self.base)) + log.debug(isinstance(obj, self.base)) if isinstance(obj, self.base): + log.debug('%s is an instance of %s' % (obj, self.base)) return obj elif type(obj) in self.types or any(isinstance(obj, type) for type in self.types): return self.validate(self.factory(obj)) @@ -127,6 +153,31 @@ class LimitedTypeValidator(Validator): (obj.__class__, type(obj), self.types)) + +class Fallback(Validator): + def __init__(self, val, pos_val, new_val): + self.val = val + self.pos_val = pos_val + self.new_val = new_val + + def validate(self, obj): + if obj == self.pos_val: + return self.new_val + else: + return self.val.validate(obj) + +class Chain(Validator): + def __init__(self, first, second): + self.first = first + self.second = second + + def validate(self, obj): + try: + return self.first.validate(obj) + except ValidationError, e: + return self.second.validate(obj) + + def isvalidator(validator): '''checks to see if an object is a validator''' if isclass(validator): @@ -135,13 +186,30 @@ def isvalidator(validator): return isinstance(validator, Validator) +class ValidationError(Exception): + def __init__(self, message, e): + self.message = message + self.e = e + + def __str__(self): + return self.message + def validate(validator): '''decorator to validate a function with some validator ''' def decorator(f): @wraps(f) def decorated(*args, **keys): - new_args, new_keys = validator.pos_validate(args, keys) +# log.debug('trying to validate on %s these: %s %s' % (f, args, keys)) + try: + new_args, new_keys = validator.pos_validate(args, keys) + except TypeError, e: + traceback.print_exc() + raise ValidationError('TypeError caught on %s from %s: %s' % (f.func_name, f.__module__, e.message), e) return f(*new_args, **new_keys) return decorated return decorator + +__all__ = ['UnpositionedValidatorException', 'Validator', 'Identity', 'id', + 'PathValidator', 'path', 'TypeValidator', 'LimitedTypeValidator', + 'isvalidator', 'ValidationError', 'validate'] |

