Skip to content

Commit

Permalink
Merge branch 'schema_valdation_#201'
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaiarocci committed Feb 13, 2016
2 parents 357bc86 + f566bd2 commit dc2f74a
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 195 deletions.
86 changes: 49 additions & 37 deletions cerberus/cerberus.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
def dummy_for_rule_validation(rule_constraints):
def dummy(self, constraint, field, value):
raise RuntimeError('Dummy method called. Its purpose is to hold just'
'validation constraints for a rule.')
'validation constraints for a rule in its '
'docstring.')
f = dummy
f.__doc__ = rule_constraints
return f
Expand Down Expand Up @@ -181,16 +182,16 @@ class Validator(object):
Support for addition and validation of custom data types.
"""

_inspected_classed = set()
_inspected_classes = set()
is_child = False
mandatory_validations = ('nullable', )
priority_validations = ('nullable', 'readonly', 'type')
_valid_schemas = set()

def __new__(cls, *args, **kwargs):
if cls not in cls._inspected_classed:
if cls not in cls._inspected_classes:
cls.__set_introspection_properties()
cls._inspected_classed.add(cls)
cls._inspected_classes.add(cls)
return super(Validator, cls).__new__(cls)

@classmethod
Expand Down Expand Up @@ -248,8 +249,6 @@ def __init__(self, *args, **kwargs):
self._errors = errors.ErrorsList()
self.document_error_tree = errors.DocumentErrorTree()
self.schema_error_tree = errors.SchemaErrorTree()
self.root_document = None
self.root_schema = None
self.document_path = ()
self.schema_path = ()
self.update = False
Expand All @@ -260,7 +259,7 @@ def __init__(self, *args, **kwargs):

def __init_error_handler(self, kwargs):
error_handler = kwargs.pop('error_handler', errors.BasicErrorHandler)
eh_config = kwargs.pop('error_handler_config', dict())
eh_config = kwargs.pop('error_handler_config', {})
if isclass(error_handler) and \
issubclass(error_handler, errors.BaseErrorHandler):
self.error_handler = error_handler(**eh_config)
Expand Down Expand Up @@ -340,8 +339,8 @@ def _error(self, *args):
value, info)
self._error([error])

def __get_child_validator(self, document_crumb=None, schema_crumb=None,
**kwargs):
def _get_child_validator(self, document_crumb=None, schema_crumb=None,
**kwargs):
""" Creates a new instance of Validator-(sub-)class. All initial
parameters of the parent are passed to the initialization, unless
a parameter is given as an explicit *keyword*-parameter.
Expand All @@ -353,11 +352,11 @@ def __get_child_validator(self, document_crumb=None, schema_crumb=None,
if not self.is_child:
child_config['is_child'] = True
child_config['error_handler'] = toy_error_handler
child_config['root_allow_unknown'] = self.allow_unknown
child_config['root_document'] = self.document
child_config['root_schema'] = self.schema
child_validator = self.__class__(**child_config)

child_validator.root_document = self.root_document or self.document
child_validator.root_schema = self.root_schema or self.schema

if document_crumb is None:
child_validator.document_path = self.document_path
else:
Expand Down Expand Up @@ -408,7 +407,7 @@ def allow_unknown(self):

@allow_unknown.setter
def allow_unknown(self, value):
if not isinstance(value, (bool, DefinitionSchema)):
if not (self.is_child or isinstance(value, (bool, DefinitionSchema))):
DefinitionSchema(self, {'allow_unknown': value})
self._config['allow_unknown'] = value

Expand Down Expand Up @@ -440,6 +439,18 @@ def purge_unknown(self):
def purge_unknown(self, value):
self._config['purge_unknown'] = value

@property
def root_allow_unknown(self):
return self._config.get('root_allow_unknown', self.allow_unknown)

@property
def root_document(self):
return self._config.get('root_document', self.document)

@property
def root_schema(self):
return self._config.get('root_schema', self.schema)

@property
def schema(self):
return self._schema
Expand All @@ -461,7 +472,7 @@ def transparent_schema_rules(self):
def transparent_schema_rules(self, value):
if isinstance(self._schema, DefinitionSchema):
self._schema.regenerate_validation_schema()
self._schema.update(dict())
self._schema.update({}) # trigger validation
self._config['transparent_schema_rules'] = value

# Document processing
Expand All @@ -476,15 +487,14 @@ def __init_processing(self, document, schema=None):
self.schema = DefinitionSchema(self, schema)
elif self.schema is None:
if isinstance(self.allow_unknown, Mapping):
self.schema = {}
self._schema = {}
else:
raise SchemaError(errors.SCHEMA_ERROR_MISSING)
if document is None:
raise DocumentError(errors.DOCUMENT_MISSING)
if not isinstance(document, Mapping):
raise DocumentError(
errors.DOCUMENT_FORMAT.format(document))
self.root_document = self.root_document or document
self.error_handler.start(self)

# # Normalizing
Expand Down Expand Up @@ -578,7 +588,7 @@ def __normalize_mapping_per_propertyschema(self, field, mapping,
property_rules):
schema = dict(((k, property_rules) for k in mapping[field]))
document = dict(((k, k) for k in mapping[field]))
validator = self.__get_child_validator(
validator = self._get_child_validator(
document_crumb=(field,), schema_crumb=(field, 'propertyschema'),
schema=schema)
result = validator.normalized(document)
Expand All @@ -598,7 +608,7 @@ def __normalize_mapping_per_propertyschema(self, field, mapping,

def __normalize_mapping_per_valueschema(self, field, mapping, value_rules):
schema = dict(((k, value_rules) for k in mapping[field]))
validator = self.__get_child_validator(
validator = self._get_child_validator(
document_crumb=field, schema_crumb=(field, 'valueschema'),
schema=schema)
mapping[field] = validator.normalized(mapping[field])
Expand All @@ -607,9 +617,9 @@ def __normalize_mapping_per_valueschema(self, field, mapping, value_rules):
self._error(validator._errors)

def __normalize_mapping_per_schema(self, field, mapping, schema):
validator = self.__get_child_validator(
validator = self._get_child_validator(
document_crumb=field, schema_crumb=(field, 'schema'),
schema=schema[field].get('schema', dict()),
schema=schema[field].get('schema', {}),
allow_unknown=schema[field].get('allow_unknown', self.allow_unknown), # noqa
purge_unknown=schema[field].get('purge_unknown', self.purge_unknown)) # noqa
mapping[field] = validator.normalized(mapping[field])
Expand All @@ -619,7 +629,7 @@ def __normalize_mapping_per_schema(self, field, mapping, schema):
def __normalize_sequence(self, field, mapping, schema):
child_schema = dict(((k, schema[field]['schema'])
for k in range(len(mapping[field]))))
validator = self.__get_child_validator(
validator = self._get_child_validator(
document_crumb=field, schema_crumb=(field, 'schema'),
schema=child_schema)
result = validator.normalized(dict((k, v) for k, v
Expand Down Expand Up @@ -813,7 +823,7 @@ def __validate_unknown_fields(self, field):
# for unknown_fields
schema_crumb = 'allow_unknown' if self.is_child \
else '__allow_unknown__'
validator = self.__get_child_validator(
validator = self._get_child_validator(
schema_crumb=schema_crumb,
schema={field: self.allow_unknown})
if not validator({field: value}, normalize=False):
Expand Down Expand Up @@ -850,7 +860,8 @@ def validate_rule(rule):
validate_rule(rule)

_validate_allow_unknown = dummy_for_rule_validation(
""" {'type': ['boolean', 'dict'], 'validator': 'allow_unknown'} """)
""" {'oneof': [{'type': 'boolean'},
{'type': 'dict', 'validator': 'bulk_schema'}]} """)

def _validate_allowed(self, allowed_values, field, value):
""" {'type': 'list'} """
Expand Down Expand Up @@ -887,7 +898,7 @@ def __validate_dependencies_mapping(self, dependencies, field):
dep_values = [dep_values]
context = self.document.copy()
parts = dep_name.split('.')
info = dict()
info = {}

for part in parts:
if part in context:
Expand Down Expand Up @@ -966,16 +977,16 @@ def __validate_items_list(self, items, field, values):
self._error(field, errors.ITEMS_LENGTH, len(items), len(values))
else:
schema = dict((i, definition) for i, definition in enumerate(items)) # noqa
validator = self.__get_child_validator(document_crumb=field,
schema_crumb=(field, 'items'), # noqa
validator = self._get_child_validator(document_crumb=field,
schema_crumb=(field, 'items'), # noqa
schema=schema)
if not validator(dict((i, item) for i, item in enumerate(values)),
normalize=False):
self._error(field, errors.BAD_ITEMS, validator._errors)

# TODO remove on next major release
def __validate_items_schema(self, items, field, value):
validator = self.__get_child_validator(schema=items)
validator = self._get_child_validator(schema=items)
for item in value:
if not validator(item, normalize=False):
self._error(validator._errors)
Expand All @@ -995,7 +1006,7 @@ def __validate_logical(self, operator, definitions, field, value):
del s[operator]
s.update(definition)

validator = self.__get_child_validator(
validator = self._get_child_validator(
schema_crumb=(field, operator, i),
schema={field: s})
if validator({field: value}, normalize=False):
Expand Down Expand Up @@ -1070,7 +1081,7 @@ def _validate_propertyschema(self, schema, field, value):
""" {'type': 'dict', 'validator': 'bulk_schema',
'forbidden': ['rename', 'rename_handler']} """
if isinstance(value, Mapping):
validator = self.__get_child_validator(
validator = self._get_child_validator(
document_crumb=(field,),
schema_crumb=(field, 'propertyschema'),
schema=dict(((k, schema) for k in value.keys())))
Expand Down Expand Up @@ -1125,7 +1136,8 @@ def __validate_required_fields(self, document):
self._error(field, errors.REQUIRED_FIELD)

def _validate_schema(self, schema, field, value):
""" {'type': ['dict', 'list'], 'validator': 'schema'} """
""" {'type': 'dict', 'anyof': [{'validator': 'schema'},
{'validator': 'bulk_schema'}]} """
if schema is None:
return

Expand All @@ -1137,16 +1149,16 @@ def _validate_schema(self, schema, field, value):
def __validate_schema_mapping(self, field, schema, value):
allow_unknown = self.schema[field].get('allow_unknown',
self.allow_unknown)
validator = self.__get_child_validator(document_crumb=field,
schema_crumb=(field, 'schema'),
schema=schema,
allow_unknown=allow_unknown)
validator = self._get_child_validator(document_crumb=field,
schema_crumb=(field, 'schema'),
schema=schema,
allow_unknown=allow_unknown)
if not validator(value, update=self.update, normalize=False):
self._error(validator._errors)

def __validate_schema_sequence(self, field, schema, value):
schema = dict(((i, schema) for i in range(len(value))))
validator = self.__get_child_validator(
validator = self._get_child_validator(
document_crumb=field, schema_crumb=(field, 'schema'),
schema=schema, allow_unknown=self.allow_unknown)
validator(dict(((i, v) for i, v in enumerate(value))), normalize=False)
Expand Down Expand Up @@ -1178,7 +1190,7 @@ def call_type_validation(_type, value):
# for x in data_type:
# if call_type_validation(x, value):
# return
validator = self.__get_child_validator(
validator = self._get_child_validator(
schema={'turing': {'anyof': [{'type': x} for x in data_type]}})
if validator({'turing': value}):
return
Expand Down Expand Up @@ -1247,7 +1259,7 @@ def _validate_valueschema(self, schema, field, value):
'forbidden': ['rename', 'rename_handler']} """
schema_crumb = (field, 'valueschema')
if isinstance(value, Mapping):
validator = self.__get_child_validator(
validator = self._get_child_validator(
document_crumb=field, schema_crumb=schema_crumb,
schema=dict((k, schema) for k in value))
validator(value, normalize=False)
Expand Down
8 changes: 4 additions & 4 deletions cerberus/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def __init__(self, path, parent_node):
self.tree_root = self.parent_node.tree_root
self.path = path[:len(self.parent_node.path) + 1]
self.errors = ErrorsList()
self.descendants = dict()
self.descendants = {}

def __add__(self, error):
self.add(error)
Expand Down Expand Up @@ -230,7 +230,7 @@ def __init__(self, errors=[]):
self.tree_root = self
self.path = ()
self.errors = []
self.descendants = dict()
self.descendants = {}
for error in errors:
self += error

Expand Down Expand Up @@ -369,7 +369,7 @@ class BasicErrorHandler(BaseErrorHandler):
}

def __init__(self, tree=None):
self.tree = dict() if tree is None else tree
self.tree = {} if tree is None else tree

def __call__(self, errors=None):
if errors is not None:
Expand All @@ -388,7 +388,7 @@ def add(self, error):
self.format_message(field, error))

def clear(self):
self.tree = dict()
self.tree = {}

def format_message(self, field, error):
return self.messages[error.code]\
Expand Down
8 changes: 4 additions & 4 deletions cerberus/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import sys


if sys.version_info[0] == 3:
_str_type = str
_int_types = (int,)
else:
if sys.version_info[0] == 2:
_str_type = basestring # noqa
_int_types = (int, long) # noqa
else:
_str_type = str
_int_types = (int,)
Loading

0 comments on commit dc2f74a

Please sign in to comment.