summaryrefslogtreecommitdiffstats
path: root/devshell
diff options
context:
space:
mode:
authorYaakov M. Nemoy <[email protected]>2009-05-03 07:19:31 (GMT)
committer Yaakov M. Nemoy <[email protected]>2009-05-03 07:19:31 (GMT)
commit1c7fd121639331ebd649e93ace9a7ada21572100 (patch)
tree1d485f3a66c8ed1c21982cef25bff896eed37873 /devshell
parent77af1df86ff2055de04dd8a30d3f332e0e6fdb8a (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.py10
-rw-r--r--devshell/base/validators.py92
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']