Skip to content

Commit

Permalink
Expands schemas and rules sets when added to a registry.
Browse files Browse the repository at this point in the history
Solves pyeve#284.
  • Loading branch information
funkyfuture committed Nov 27, 2016
1 parent d687e65 commit 1523df3
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 22 deletions.
53 changes: 34 additions & 19 deletions cerberus/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,17 @@ def __setitem__(self, key, value):
def __str__(self):
return str(self.schema)

def expand(self, schema):
@classmethod
def expand(cls, schema):
try:
schema = self._expand_logical_shortcuts(schema)
schema = self._expand_subschemas(schema)
schema = cls._expand_logical_shortcuts(schema)
schema = cls._expand_subschemas(schema)
except Exception:
pass
return schema

def _expand_logical_shortcuts(self, schema):
@classmethod
def _expand_logical_shortcuts(cls, schema):
""" Expand agglutinated rules in a definition-schema.
:param schema: The schema-definition to expand.
Expand All @@ -125,7 +127,8 @@ def is_of_rule(x):
del schema[field][of_rule]
return schema

def _expand_subschemas(self, schema):
@classmethod
def _expand_subschemas(cls, schema):
def has_schema_rule():
return isinstance(schema[field], Mapping) and \
'schema' in schema[field]
Expand All @@ -143,21 +146,21 @@ def has_mapping_schema():
if not has_schema_rule():
pass
elif has_mapping_schema():
schema[field]['schema'] = self.expand(schema[field]['schema'])
schema[field]['schema'] = cls.expand(schema[field]['schema'])
else: # assumes schema-constraints for a sequence
schema[field]['schema'] = \
self.expand({0: schema[field]['schema']})[0]
cls.expand({0: schema[field]['schema']})[0]

for rule in ('keyschema', 'valueschema'):
if rule in schema[field]:
schema[field][rule] = \
self.expand({0: schema[field][rule]})[0]
cls.expand({0: schema[field][rule]})[0]

for rule in ('allof', 'anyof', 'items', 'noneof', 'oneof'):
if rule in schema[field]:
new_rules_definition = []
for item in schema[field][rule]:
new_rules_definition.append(self.expand({0: item})[0])
new_rules_definition.append(cls.expand({0: item})[0])
schema[field][rule] = new_rules_definition
return schema

Expand Down Expand Up @@ -363,58 +366,70 @@ class Registry(object):
""" A registry to store and retrieve schemas and parts of it by a name
that can be used in validation schemas.
:param definitions: Optional, initial defintions.
:param definitions: Optional, initial definitions.
:type definitions: any :term:`mapping` """

def __init__(self, definitions={}):
self._storage = {}
self.extend(definitions)

def add(self, name, definition):
""" Register a definition to the registry. Existing defintions are
""" Register a definition to the registry. Existing definitions are
replaced silently.
:param name: The name which can be used as reference in a validation
schema.
:type name: :class:`str`
:param definition: The definition.
:type definition: any :term:`mapping` """
self._storage[name] = definition
self._storage[name] = self._expand_definition(definition)

def all(self):
""" Returns a :class:`dict` with all registered definitions mapped to
their name. """
return self._storage

def extend(self, definitions):
""" Add several defintions at once. Existing defintions are
""" Add several definitions at once. Existing definitions are
replaced silently.
:param definitions: The names and defintions.
:param definitions: The names and definitions.
:type definitions: a :term:`mapping` or an :term:`iterable` with
two-value :class:`tuple` s """
for name, definition in dict(definitions).items():
self.add(name, definition)

def clear(self):
""" Purge all defintions in the registry. """
""" Purge all definitions in the registry. """
self._storage.clear()

def get(self, name, default=None):
""" Retrieve a defintion from the registry.
""" Retrieve a definition from the registry.
:param name: The reference that points to the defintion.
:param name: The reference that points to the definition.
:type name: :class:`str`
:param default: Return value if the reference isn't registered. """
return self._storage.get(name, default)

def remove(self, *names):
""" Unregister definitions from the registry.
:param names: The names of the defintions that are to be
:param names: The names of the definitions that are to be
unregistered. """
for name in names:
self._storage.pop(name, None)


schema_registry, rules_set_registry = Registry(), Registry()
class SchemaRegistry(Registry):
@classmethod
def _expand_definition(cls, definition):
return DefinitionSchema.expand(definition)


class RulesSetRegistry(Registry):
@classmethod
def _expand_definition(cls, definition):
return DefinitionSchema.expand({0: definition})[0]


schema_registry, rules_set_registry = SchemaRegistry(), RulesSetRegistry()
11 changes: 9 additions & 2 deletions cerberus/tests/test_registries.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ def test_references_remain_unresolved(validator):
assert 'boolean' == s


def test_rules_registry_with_anyof_type(validator):
rules_set_registry.add('string_or_integer', {'anyof_type': ['string', 'integer']})
def test_rules_registry_with_anyof_type():
rules_set_registry.add('string_or_integer',
{'anyof_type': ['string', 'integer']})
schema = {'soi': 'string_or_integer'}
assert_success({'soi': 'hello'}, schema)


def test_schema_registry_with_anyof_type():
schema_registry.add('soi_id', {'id': {'anyof_type': ['string', 'integer']}})
schema = {'soi': {'schema': 'soi_id'}}
assert_success({'soi': {'id': 'hello'}}, schema)
2 changes: 1 addition & 1 deletion cerberus/tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def test_validated_schema_cache():
v = Validator({'foozifix': {'coerce': int}})
assert len(v._valid_schemas) == cache_size

max_cache_size = 80
max_cache_size = 81
assert cache_size < max_cache_size, \
"There's an unexpected high amount of cached valid " \
"definition schemas. Unless you added further tests, " \
Expand Down

0 comments on commit 1523df3

Please sign in to comment.