import os
import re
import datetime
import sys
from functools import wraps
from decimal import Decimal, InvalidOperation
from voluptuous.schema_builder import Schema, raises, message
from voluptuous.error import (MultipleInvalid, CoerceInvalid, TrueInvalid, FalseInvalid, BooleanInvalid, Invalid,
AnyInvalid, AllInvalid, MatchInvalid, UrlInvalid, EmailInvalid, FileInvalid, DirInvalid,
RangeInvalid, PathInvalid, ExactSequenceInvalid, LengthInvalid, DatetimeInvalid,
DateInvalid, InInvalid, TypeInvalid, NotInInvalid, ContainsInvalid, NotEnoughValid,
TooManyValid)
if sys.version_info >= (3,):
import urllib.parse as urlparse
basestring = str
else:
import urlparse
# Taken from https://github.com/kvesteri/validators/blob/master/validators/email.py
USER_REGEX = re.compile(
# dot-atom
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+"
r"(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*$"
# quoted-string
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|'
r"""\\[\001-\011\013\014\016-\177])*"$)""",
re.IGNORECASE
)
DOMAIN_REGEX = re.compile(
# domain
r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+'
r'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?$)'
# literal form, ipv4 address (SMTP 4.1.3)
r'|^\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)'
r'(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$',
re.IGNORECASE)
__author__ = 'tusharmakkar08'
def truth(f):
"""Convenience decorator to convert truth functions into validators.
>>> @truth
... def isdir(v):
... return os.path.isdir(v)
>>> validate = Schema(isdir)
>>> validate('/')
'/'
>>> with raises(MultipleInvalid, 'not a valid value'):
... validate('/notavaliddir')
"""
@wraps(f)
def check(v):
t = f(v)
if not t:
raise ValueError
return v
return check
class Coerce(object):
"""Coerce a value to a type.
If the type constructor throws a ValueError or TypeError, the value
will be marked as Invalid.
Default behavior:
>>> validate = Schema(Coerce(int))
>>> with raises(MultipleInvalid, 'expected int'):
... validate(None)
>>> with raises(MultipleInvalid, 'expected int'):
... validate('foo')
With custom message:
>>> validate = Schema(Coerce(int, "moo"))
>>> with raises(MultipleInvalid, 'moo'):
... validate('foo')
"""
def __init__(self, type, msg=None):
self.type = type
self.msg = msg
self.type_name = type.__name__
def __call__(self, v):
try:
return self.type(v)
except (ValueError, TypeError, InvalidOperation):
msg = self.msg or ('expected %s' % self.type_name)
raise CoerceInvalid(msg)
def __repr__(self):
return 'Coerce(%s, msg=%r)' % (self.type_name, self.msg)
@message('value was not true', cls=TrueInvalid)
@truth
def IsTrue(v):
"""Assert that a value is true, in the Python sense.
>>> validate = Schema(IsTrue())
"In the Python sense" means that implicitly false values, such as empty
lists, dictionaries, etc. are treated as "false":
>>> with raises(MultipleInvalid, "value was not true"):
... validate([])
>>> validate([1])
[1]
>>> with raises(MultipleInvalid, "value was not true"):
... validate(False)
...and so on.
>>> try:
... validate([])
... except MultipleInvalid as e:
... assert isinstance(e.errors[0], TrueInvalid)
"""
return v
@message('value was not false', cls=FalseInvalid)
def IsFalse(v):
"""Assert that a value is false, in the Python sense.
(see :func:`IsTrue` for more detail)
>>> validate = Schema(IsFalse())
>>> validate([])
[]
>>> with raises(MultipleInvalid, "value was not false"):
... validate(True)
>>> try:
... validate(True)
... except MultipleInvalid as e:
... assert isinstance(e.errors[0], FalseInvalid)
"""
if v:
raise ValueError
return v
@message('expected boolean', cls=BooleanInvalid)
def Boolean(v):
"""Convert human-readable boolean values to a bool.
Accepted values are 1, true, yes, on, enable, and their negatives.
Non-string values are cast to bool.
>>> validate = Schema(Boolean())
>>> validate(True)
True
>>> validate("1")
True
>>> validate("0")
False
>>> with raises(MultipleInvalid, "expected boolean"):
... validate('moo')
>>> try:
... validate('moo')
... except MultipleInvalid as e:
... assert isinstance(e.errors[0], BooleanInvalid)
"""
if isinstance(v, basestring):
v = v.lower()
if v in ('1', 'true', 'yes', 'on', 'enable'):
return True
if v in ('0', 'false', 'no', 'off', 'disable'):
return False
raise ValueError
return bool(v)
class _WithSubValidators(object):
"""Base class for validators that use sub-validators.
Special class to use as a parent class for validators using sub-validators.
This class provides the `__voluptuous_compile__` method so the
sub-validators are compiled by the parent `Schema`.
"""
def __init__(self, *validators, **kwargs):
self.validators = validators
self.msg = kwargs.pop('msg', None)
def __voluptuous_compile__(self, schema):
self._compiled = [
schema._compile(v)
for v in self.validators
]
return self._run
def _run(self, path, value):
return self._exec(self._compiled, value, path)
def __call__(self, v):
return self._exec((Schema(val) for val in self.validators), v)
def __repr__(self):
return '%s(%s, msg=%r)' % (
self.__class__.__name__,
", ".join(repr(v) for v in self.validators),
self.msg
)
class Any(_WithSubValidators):
"""Use the first validated value.
:param msg: Message to deliver to user if validation fails.
:param kwargs: All other keyword arguments are passed to the sub-Schema constructors.
:returns: Return value of the first validator that passes.
>>> validate = Schema(Any('true', 'false',
... All(Any(int, bool), Coerce(bool))))
>>> validate('true')
'true'
>>> validate(1)
True
>>> with raises(MultipleInvalid, "not a valid value"):
... validate('moo')
msg argument is used
>>> validate = Schema(Any(1, 2, 3, msg="Expected 1 2 or 3"))
>>> validate(1)
1
>>> with raises(MultipleInvalid, "Expected 1 2 or 3"):
... validate(4)
"""
def _exec(self, funcs, v, path=None):
error = None
for func in funcs:
try:
if path is None:
return func(v)
else:
return func(path, v)
except Invalid as e:
if error is None or len(e.path) > len(error.path):
error = e
else:
if error:
raise error if self.msg is None else AnyInvalid(
self.msg, path=path)
raise AnyInvalid(self.msg or 'no valid value found',
path=path)
# Convenience alias
Or = Any
class All(_WithSubValidators):
"""Value must pass all validators.
The output of each validator is passed as input to the next.
:param msg: Message to deliver to user if validation fails.
:param kwargs: All other keyword arguments are passed to the sub-Schema constructors.
>>> validate = Schema(All('10', Coerce(int)))
>>> validate('10')
10
"""
def _exec(self, funcs, v, path=None):
try:
for func in funcs:
if path is None:
v = func(v)
else:
v = func(path, v)
except Invalid as e:
raise e if self.msg is None else AllInvalid(self.msg, path=path)
return v
# Convenience alias
And = All
class Match(object):
"""Value must be a string that matches the regular expression.
>>> validate = Schema(Match(r'^0x[A-F0-9]+$'))
>>> validate('0x123EF4')
'0x123EF4'
>>> with raises(MultipleInvalid, "does not match regular expression"):
... validate('123EF4')
>>> with raises(MultipleInvalid, 'expected string or buffer'):
... validate(123)
Pattern may also be a _compiled regular expression:
>>> validate = Schema(Match(re.compile(r'0x[A-F0-9]+', re.I)))
>>> validate('0x123ef4')
'0x123ef4'
"""
def __init__(self, pattern, msg=None):
if isinstance(pattern, basestring):
pattern = re.compile(pattern)
self.pattern = pattern
self.msg = msg
def __call__(self, v):
try:
match = self.pattern.match(v)
except TypeError:
raise MatchInvalid("expected string or buffer")
if not match:
raise MatchInvalid(self.msg or 'does not match regular expression')
return v
def __repr__(self):
return 'Match(%r, msg=%r)' % (self.pattern.pattern, self.msg)
class Replace(object):
"""Regex substitution.
>>> validate = Schema(All(Replace('you', 'I'),
... Replace('hello', 'goodbye')))
>>> validate('you say hello')
'I say goodbye'
"""
def __init__(self, pattern, substitution, msg=None):
if isinstance(pattern, basestring):
pattern = re.compile(pattern)
self.pattern = pattern
self.substitution = substitution
Loading ...