pax_global_header 0000666 0000000 0000000 00000000064 13225652165 0014521 g ustar 00root root 0000000 0000000 52 comment=58d945fd43655bf1b8293093ad7ccfe573678de8 coreschema-0.0.4/ 0000775 0000000 0000000 00000000000 13225652165 0013633 5 ustar 00root root 0000000 0000000 coreschema-0.0.4/PKG-INFO 0000664 0000000 0000000 00000001052 13225652165 0014726 0 ustar 00root root 0000000 0000000 Metadata-Version: 1.1 Name: coreschema Version: 0.0.4 Summary: Core Schema. Home-page: https://github.com/core-api/python-coreschema Author: Tom Christie Author-email: tom@tomchristie.com License: BSD Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Internet :: WWW/HTTP coreschema-0.0.4/coreschema.egg-info/ 0000775 0000000 0000000 00000000000 13225652165 0017436 5 ustar 00root root 0000000 0000000 coreschema-0.0.4/coreschema.egg-info/PKG-INFO 0000664 0000000 0000000 00000001052 13225652165 0020531 0 ustar 00root root 0000000 0000000 Metadata-Version: 1.1 Name: coreschema Version: 0.0.4 Summary: Core Schema. Home-page: https://github.com/core-api/python-coreschema Author: Tom Christie Author-email: tom@tomchristie.com License: BSD Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Internet :: WWW/HTTP coreschema-0.0.4/coreschema.egg-info/SOURCES.txt 0000664 0000000 0000000 00000001422 13225652165 0021321 0 ustar 00root root 0000000 0000000 setup.py coreschema/__init__.py coreschema/compat.py coreschema/formats.py coreschema/schemas.py coreschema/utils.py coreschema.egg-info/PKG-INFO coreschema.egg-info/SOURCES.txt coreschema.egg-info/dependency_links.txt coreschema.egg-info/requires.txt coreschema.egg-info/top_level.txt coreschema/encodings/__init__.py coreschema/encodings/corejson.py coreschema/encodings/html.py coreschema/encodings/jsonschema.py coreschema/templates/base.html coreschema/templates/form.html coreschema/templates/bootstrap3/form.html coreschema/templates/bootstrap3/inputs/checkbox.html coreschema/templates/bootstrap3/inputs/input.html coreschema/templates/bootstrap3/inputs/select.html coreschema/templates/bootstrap3/inputs/select_multiple.html coreschema/templates/bootstrap3/inputs/textarea.html coreschema-0.0.4/coreschema.egg-info/dependency_links.txt 0000664 0000000 0000000 00000000001 13225652165 0023504 0 ustar 00root root 0000000 0000000 coreschema-0.0.4/coreschema.egg-info/requires.txt 0000664 0000000 0000000 00000000007 13225652165 0022033 0 ustar 00root root 0000000 0000000 jinja2 coreschema-0.0.4/coreschema.egg-info/top_level.txt 0000664 0000000 0000000 00000000040 13225652165 0022162 0 ustar 00root root 0000000 0000000 coreschema coreschema/encodings coreschema-0.0.4/coreschema/ 0000775 0000000 0000000 00000000000 13225652165 0015744 5 ustar 00root root 0000000 0000000 coreschema-0.0.4/coreschema/__init__.py 0000664 0000000 0000000 00000000651 13225652165 0020057 0 ustar 00root root 0000000 0000000 from coreschema.schemas import ( Object, Array, Integer, Number, String, Boolean, Null, Enum, Anything, Ref, RefSpace, Union, Intersection, ExclusiveUnion, Not ) from coreschema.encodings.html import render_to_form __version__ = '0.0.4' __all__ = [ Object, Array, Integer, Number, String, Boolean, Null, Enum, Anything, Ref, RefSpace, Union, Intersection, ExclusiveUnion, Not, render_to_form ] coreschema-0.0.4/coreschema/compat.py 0000664 0000000 0000000 00000000262 13225652165 0017601 0 ustar 00root root 0000000 0000000 import sys if sys.version_info.major == 2: text_types = (str, unicode) numeric_types = (float, int, long) else: text_types = (str,) numeric_types = (float, int) coreschema-0.0.4/coreschema/encodings/ 0000775 0000000 0000000 00000000000 13225652165 0017715 5 ustar 00root root 0000000 0000000 coreschema-0.0.4/coreschema/encodings/__init__.py 0000664 0000000 0000000 00000000000 13225652165 0022014 0 ustar 00root root 0000000 0000000 coreschema-0.0.4/coreschema/encodings/corejson.py 0000664 0000000 0000000 00000001025 13225652165 0022107 0 ustar 00root root 0000000 0000000 jsonschema = coreschema.RefSpace({ 'Document': coreschema.Object( properties={ '_type': coreschema.Enum(['document']), '_meta': coreschema.Object( properties={ 'url': coreschema.String(), 'title': coreschema.String(), 'description': coreschema.String(), } ) } ), 'Link': coreschema.Object( properties={ '_type': coreschema.Enum(['link']) } ) }) coreschema-0.0.4/coreschema/encodings/html.py 0000664 0000000 0000000 00000004741 13225652165 0021241 0 ustar 00root root 0000000 0000000 from coreschema import Object, Array, String, Integer, Number, Boolean, Enum import jinja2 env = jinja2.Environment(loader=jinja2.PackageLoader('coreschema', 'templates')) # TODO: required # TODO: initial values, errors, input control def render_to_form(schema): template = env.get_template('form.html') return template.render({ 'parent': schema, 'determine_html_template': determine_html_template, 'get_textarea_value': get_textarea_value, 'get_attrs': get_attrs }) def determine_html_template(schema): if isinstance(schema, Array): if schema.unique_items and isinstance(schema.items, Enum): # Actually only for *unordered* input return 'bootstrap3/inputs/select_multiple.html' # TODO: Comma seperated inputs return 'bootstrap3/inputs/textarea.html' elif isinstance(schema, Object): # TODO: Fieldsets return 'bootstrap3/inputs/textarea.html' elif isinstance(schema, Number): return 'bootstrap3/inputs/input.html' elif isinstance(schema, Boolean): # TODO: nullable boolean return 'bootstrap3/inputs/checkbox.html' elif isinstance(schema, Enum): # TODO: display values return 'bootstrap3/inputs/select.html' # String: if schema.format == 'textarea': return 'bootstrap3/inputs/textarea.html' return 'bootstrap3/inputs/input.html' def get_textarea_value(schema): if isinstance(schema, Array): return "[ ]" elif isinstance(schema, Object): return "{ }" return "" def get_attrs(schema): if isinstance(schema, Array): # TODO: Add data-child-type and use with selects return "data-empty=[] data-type='array'" elif isinstance(schema, Object): return "data-empty={} data-type='object'" elif isinstance(schema, Integer): return "data-empty=null data-type='integer' type='number' step=1" elif isinstance(schema, Number): return "data-empty=null data-type='number' type='number' step=any" elif isinstance(schema, Boolean): return "data-empty=false data-type='boolean'" elif isinstance(schema, Enum): # TODO: Non-string Enum return "data-empty='' data-type='string'" # String: if schema.format: # TODO: Only include valid HTML5 formats. # Coerce datetime to datetime-local. return "data-empty='' data-type='string' type='%s'" % schema.format return "data-empty='' data-type='string'" coreschema-0.0.4/coreschema/encodings/jsonschema.py 0000664 0000000 0000000 00000016351 13225652165 0022427 0 ustar 00root root 0000000 0000000 from coreschema.compat import text_types import coreschema import re jsonschema = coreschema.RefSpace({ 'Schema': coreschema.Object( properties={ # Meta 'id': coreschema.String(format='uri'), '$schema': coreschema.String(format='uri'), 'title': coreschema.String(), 'description': coreschema.String(), 'default': coreschema.Anything(), 'definitions': coreschema.Ref('SchemaMap'), # Type 'type': coreschema.Ref('SimpleTypes') | coreschema.Array(items=coreschema.Ref('SimpleTypes'), min_items=1, unique_items=True), # Number validators 'minimum': coreschema.Number(), 'maximum': coreschema.Number(), 'exclusiveMinimum': coreschema.Boolean(default=False), 'exclusiveMaximum': coreschema.Boolean(default=False), 'multipleOf': coreschema.Number(minimum=0, exclusive_minimum=True), # String validators 'minLength': coreschema.Integer(minimum=0, default=0), 'maxLength': coreschema.Integer(minimum=0), 'pattern': coreschema.String(format='regex'), 'format': coreschema.String(), # Array validators 'items': coreschema.Ref('Schema') | coreschema.Ref('SchemaArray'), # TODO: default={} 'additionalItems': coreschema.Boolean() | coreschema.Ref('Schema'), # TODO: default={} 'minItems': coreschema.Integer(minimum=0, default=0), 'maxItems': coreschema.Integer(minimum=0), 'uniqueItems': coreschema.Boolean(default=False), # Object validators 'properties': coreschema.Ref('SchemaMap'), 'patternProperties': coreschema.Ref('SchemaMap'), 'additionalProperties': coreschema.Boolean() | coreschema.Ref('Schema'), 'minProperties': coreschema.Integer(minimum=0, default=0), 'maxProperties': coreschema.Integer(minimum=0), 'required': coreschema.Ref('StringArray'), 'dependancies': coreschema.Object(additional_properties=coreschema.Ref('Schema') | coreschema.Ref('StringArray')), # Enum validators 'enum': coreschema.Array(min_items=1, unique_items=True), # Composites 'allOf': coreschema.Ref('SchemaArray'), 'anyOf': coreschema.Ref('SchemaArray'), 'oneOf': coreschema.Ref('SchemaArray'), 'not': coreschema.Ref('Schema') }, # dependancies=..., TODO default={}, ), 'SchemaArray': coreschema.Array( items=coreschema.Ref('Schema'), min_items=1, ), 'SchemaMap': coreschema.Object( additional_properties=coreschema.Ref('Schema'), default={}, ), 'SimpleTypes': coreschema.Enum( enum=['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'] ), 'StringArray': coreschema.Array( items=coreschema.String(), min_items=1, unique_items=True, ) }, root='Schema') KEYWORD_TO_TYPE = { 'minimum': 'number', 'maximum': 'number', 'exclusiveMinimum': 'number', 'exclusiveMaximum': 'number', 'multipleOf': 'number', # 'minLength': 'string', 'maxLength': 'string', 'pattern': 'string', 'format': 'string', # 'items': 'array', 'maxItems': 'array', 'minItems': 'array', 'uniqueItems': 'array', 'additionalItems': 'array', # 'properties': 'object', 'maxProperties': 'object', 'minProperties': 'object', 'additionalProperties': 'object', 'patternProperties': 'object', 'required': 'object', } TYPE_NAMES = [ 'array', 'boolean', 'integer', 'null', 'number', 'object', 'string' ] CLS_MAP = { 'array': coreschema.Array, 'boolean': coreschema.Boolean, 'integer': coreschema.Integer, 'null': coreschema.Null, 'number': coreschema.Number, 'object': coreschema.Object, 'string': coreschema.String, } def camelcase_to_snakecase(name): s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() def get_typed_schemas(data): """ Return a list of schemas for any primitive type restrictions. """ has_type = False type_kwargs = {type_name: {} for type_name in TYPE_NAMES} for keyword, value in data.items(): if keyword not in KEYWORD_TO_TYPE: continue # Load any nested schemas if keyword == 'items' and isinstance(value, dict): value = load_jsonschema(value) elif keyword == 'items' and isinstance(value, list): value = [load_jsonschema(item) for item in value] elif keyword == 'additionalItems' and isinstance(value, dict): value = load_jsonschema(value) elif keyword == 'properties' and isinstance(value, dict): value = {key: load_jsonschema(item) for key, item in value.items()} elif keyword == 'additionalProperties' and isinstance(value, dict): value = load_jsonschema(value) elif keyword == 'patternProperties' and isinstance(value, dict): value = {key: load_jsonschema(item) for key, item in value.items()} type_name = KEYWORD_TO_TYPE[keyword] has_type = True argument_name = camelcase_to_snakecase(keyword) type_kwargs[type_name][argument_name] = value type_kwargs['integer'] = type_kwargs['number'] if 'type' in data: has_type = True types = data.get('type') types = types if isinstance(types, list) else [types] for type_name in list(type_kwargs.keys()): if type_name not in types: type_kwargs.pop(type_name) schemas = [] if has_type: for type_name, kwargs in type_kwargs.items(): cls = CLS_MAP[type_name] schemas.append(cls(**kwargs)) return schemas def get_composite_schemas(data): schemas = [] if 'anyOf' in data: value = data['anyOf'] schema = coreschema.Union([ load_jsonschema(item) for item in value ]) schemas.append(schema) if 'allOf' in data: value = data['allOf'] schema = coreschema.Intersection([ load_jsonschema(item) for item in value ]) schemas.append(schema) if 'oneOf' in data: value = data['oneOf'] schema = coreschema.ExclusiveUnion([ load_jsonschema(item) for item in value ]) schemas.append(schema) if 'not' in data: value = data['not'] schema = coreschema.Not(load_jsonschema(value)) schemas.append(schema) return schemas def load_jsonschema(data): schemas = get_typed_schemas(data) if len(schemas) > 1: schemas = [coreschema.Union(schemas)] schemas += get_composite_schemas(data) if not schemas: schema = coreschema.Anything() elif len(schemas) == 1: schema = schemas[0] else: schema = coreschema.Intersection(schemas) if 'enum' in data: # Restrict enum values by any existing type constraints, # and then use an Enum type. enum_values = [ value for value in data['enum'] if schema.validate(value) == [] ] return coreschema.Enum(enum_values) return schema coreschema-0.0.4/coreschema/formats.py 0000664 0000000 0000000 00000000741 13225652165 0017773 0 ustar 00root root 0000000 0000000 import re email_pattern = re.compile('^[^@]+@[^@]') uri_pattern = re.compile('^[A-Za-z][A-Za-z0-9+.-]+:') def validate_format(value, format): function = { 'email': validate_email, 'uri': validate_uri }.get(format, unknown_format) return function(value) def unknown_format(value): return value def validate_email(value): return bool(re.match(email_pattern, value)) def validate_uri(value): return bool(re.match(uri_pattern, value)) coreschema-0.0.4/coreschema/schemas.py 0000664 0000000 0000000 00000040560 13225652165 0017746 0 ustar 00root root 0000000 0000000 from collections import namedtuple from coreschema.compat import text_types, numeric_types from coreschema.formats import validate_format from coreschema.utils import uniq import re Error = namedtuple('Error', ['text', 'index']) def push_index(errors, key): return [ Error(error.text, [key] + error.index) for error in errors ] # TODO: Properties as OrderedDict if from list of tuples. # TODO: null keyword / Nullable # TODO: dependancies # TODO: remote ref # TODO: remaining formats # LATER: Enum display values # LATER: File # LATER: strict, coerce float etc... # LATER: decimals # LATER: override errors class Schema(object): errors = {} def __init__(self, title='', description='', default=None): self.title = title self.description = description self.default = default def make_error(self, code): error_string = self.errors[code] params = self.__dict__ return Error(error_string.format(**params), []) def __or__(self, other): if isinstance(self, Union): self_children = self.children else: self_children = [self] if isinstance(other, Union): other_children = other.children else: other_children = [other] return Union(self_children + other_children) def __and__(self, other): if isinstance(self, Intersection): self_children = self.children else: self_children = [self] if isinstance(other, Intersection): other_children = other.children else: other_children = [other] return Intersection(self_children + other_children) def __xor__(self, other): return ExclusiveUnion([self, other]) def __invert__(self): return Not(self) def __eq__(self, other): return ( self.__class__ == other.__class__ and self.__dict__ == other.__dict__ ) class Object(Schema): errors = { 'type': 'Must be an object.', 'invalid_key': 'Object keys must be strings.', 'empty': 'Must not be empty.', 'required': 'This field is required.', 'max_properties': 'Must have no more than {max_properties} properties.', 'min_properties': 'Must have at least {min_properties} properties.', 'invalid_property': 'Invalid property.' } def __init__(self, properties=None, required=None, max_properties=None, min_properties=None, pattern_properties=None, additional_properties=True, **kwargs): super(Object, self).__init__(**kwargs) if isinstance(additional_properties, bool): # Handle `additional_properties` set to a boolean. self.additional_properties_schema = Anything() else: # Handle `additional_properties` set to a schema. self.additional_properties_schema = additional_properties additional_properties = True self.properties = properties self.required = required or [] self.max_properties = max_properties self.min_properties = min_properties self.pattern_properties = pattern_properties self.additional_properties = additional_properties # Compile pattern regexes. self.pattern_properties_regex = None if pattern_properties is not None: self.pattern_properties_regex = { re.compile(key): value for key, value in pattern_properties.items() } def validate(self, value, context=None): if not isinstance(value, dict): return [self.make_error('type')] errors = [] if any(not isinstance(key, text_types) for key in value.keys()): errors += [self.make_error('invalid_key')] if self.required is not None: for key in self.required: if key not in value: error_items = [self.make_error('required')] errors += push_index(error_items, key) if self.min_properties is not None: if len(value) < self.min_properties: if self.min_properties == 1: errors += [self.make_error('empty')] else: errors += [self.make_error('min_properties')] if self.max_properties is not None: if len(value) > self.max_properties: errors += [self.make_error('max_properties')] # Properties remaining_keys = set(value.keys()) if self.properties is not None: remaining_keys -= set(self.properties.keys()) for key, property_item in self.properties.items(): if key not in value: continue error_items = property_item.validate(value[key], context) errors += push_index(error_items, key) # Pattern properties if self.pattern_properties is not None: for key in list(remaining_keys): for pattern, schema in self.pattern_properties_regex.items(): if re.search(pattern, key): error_items = schema.validate(value[key], context) errors += push_index(error_items, key) remaining_keys.discard(key) # Additional properties if self.additional_properties: for key in remaining_keys: error_items = self.additional_properties_schema.validate(value[key], context) errors += push_index(error_items, key) else: for key in remaining_keys: error_items = [self.make_error('invalid_property')] errors += push_index(error_items, key) return errors class Array(Schema): errors = { 'type': 'Must be an array.', 'empty': 'Must not be empty.', 'max_items': 'Must have no more than {max_items} items.', 'min_items': 'Must have at least {min_items} items.', 'unique': 'Must not contain duplicate items.' } def __init__(self, items=None, max_items=None, min_items=None, unique_items=False, additional_items=True, **kwargs): super(Array, self).__init__(**kwargs) if items is None: items = Anything() if isinstance(items, list) and additional_items is False: # Setting additional_items==False implies a value for max_items. if max_items is None or max_items > len(items): max_items = len(items) self.items = items self.max_items = max_items self.min_items = min_items self.unique_items = unique_items self.additional_items = additional_items def validate(self, value, context=None): if not isinstance(value, list): return [self.make_error('type')] errors = [] if self.items is not None: child_schema = self.items is_list = isinstance(self.items, list) for idx, item in enumerate(value): if is_list: # Case where `items` is a list of schemas. if idx < len(self.items): # Handle each item in the list. child_schema = self.items[idx] else: # Handle any additional items. if isinstance(self.additional_items, bool): break else: child_schema = self.additional_items error_items = child_schema.validate(item, context) errors += push_index(error_items, idx) if self.min_items is not None: if len(value) < self.min_items: if self.min_items == 1: errors += [self.make_error('empty')] else: errors += [self.make_error('min_items')] if self.max_items is not None: if len(value) > self.max_items: errors += [self.make_error('max_items')] if self.unique_items: if not(uniq(value)): errors += [self.make_error('unique')] return errors class Number(Schema): integer_only = False errors = { 'type': 'Must be a number.', 'minimum': 'Must be greater than or equal to {minimum}.', 'exclusive_minimum': 'Must be greater than {minimum}.', 'maximum': 'Must be less than or equal to {maximum}.', 'exclusive_maximum': 'Must be less than {maximum}.', 'multiple_of': 'Must be a multiple of {multiple_of}.', } def __init__(self, minimum=None, maximum=None, exclusive_minimum=False, exclusive_maximum=False, multiple_of=None, **kwargs): super(Number, self).__init__(**kwargs) self.minimum = minimum self.maximum = maximum self.exclusive_minimum = exclusive_minimum self.exclusive_maximum = exclusive_maximum self.multiple_of = multiple_of def validate(self, value, context=None): if isinstance(value, bool): # In Python `bool` subclasses `int`, so handle that case explicitly. return [self.make_error('type')] if not isinstance(value, numeric_types): return [self.make_error('type')] if self.integer_only and isinstance(value, float) and not value.is_integer(): return [self.make_error('type')] errors = [] if self.minimum is not None: if self.exclusive_minimum: if value <= self.minimum: errors += [self.make_error('exclusive_minimum')] else: if value < self.minimum: errors += [self.make_error('minimum')] if self.maximum is not None: if self.exclusive_maximum: if value >= self.maximum: errors += [self.make_error('exclusive_maximum')] else: if value > self.maximum: errors += [self.make_error('maximum')] if self.multiple_of is not None: if isinstance(self.multiple_of, float): failed = not (float(value) / self.multiple_of).is_integer() else: failed = value % self.multiple_of if failed: errors += [self.make_error('multiple_of')] return errors class Integer(Number): errors = { 'type': 'Must be an integer.', 'minimum': 'Must be greater than or equal to {minimum}.', 'exclusive_minimum': 'Must be greater than {minimum}.', 'maximum': 'Must be less than or equal to {maximum}.', 'exclusive_maximum': 'Must be less than {maximum}.', 'multiple_of': 'Must be a multiple of {multiple_of}.', } integer_only = True class String(Schema): errors = { 'type': 'Must be a string.', 'blank': 'Must not be blank.', 'max_length': 'Must have no more than {max_length} characters.', 'min_length': 'Must have at least {min_length} characters.', 'pattern': 'Must match the pattern /{pattern}/.', 'format': 'Must be a valid {format}.', } def __init__(self, max_length=None, min_length=None, pattern=None, format=None, **kwargs): super(String, self).__init__(**kwargs) self.max_length = max_length self.min_length = min_length self.pattern = pattern self.format = format self.pattern_regex = None if self.pattern is not None: self.pattern_regex = re.compile(pattern) def validate(self, value, context=None): if not isinstance(value, text_types): return [self.make_error('type')] errors = [] if self.min_length is not None: if len(value) < self.min_length: if self.min_length == 1: errors += [self.make_error('blank')] else: errors += [self.make_error('min_length')] if self.max_length is not None: if len(value) > self.max_length: errors += [self.make_error('max_length')] if self.pattern is not None: if not re.search(self.pattern_regex, value): errors += [self.make_error('pattern')] if self.format is not None: if not validate_format(value, self.format): errors += [self.make_error('format')] return errors class Boolean(Schema): errors = { 'type': 'Must be a boolean.' } def validate(self, value, context=None): if not isinstance(value, bool): return [self.make_error('type')] return [] class Null(Schema): errors = { 'type': 'Must be null.' } def validate(self, value, context=None): if value is not None: return [self.make_error('type')] return [] class Enum(Schema): errors = { 'enum': 'Must be one of {enum}.', 'exact': 'Must be {exact}.', } def __init__(self, enum, **kwargs): super(Enum, self).__init__(**kwargs) self.enum = enum if len(enum) == 1: self.exact = repr(enum[0]) def validate(self, value, context=None): if value not in self.enum: if len(self.enum) == 1: return [self.make_error('exact')] return [self.make_error('enum')] return [] class Anything(Schema): errors = { 'type': 'Must be a valid primitive type.' } types = text_types + (dict, list, int, float, bool, type(None)) def validate(self, value, context=None): if not isinstance(value, self.types): return [self.make_error('type')] errors = [] if isinstance(value, list): schema = Array() errors += schema.validate(value, context) elif isinstance(value, dict): schema = Object() errors += schema.validate(value, context) return errors # Composites class Union(Schema): errors = { 'match': 'Must match one of the options.' } def __init__(self, children, **kwargs): super(Union, self).__init__(**kwargs) self.children = children def validate(self, value, context=None): for child in self.children: if child.validate(value, context) == []: return [] return [self.make_error('match')] class Intersection(Schema): def __init__(self, children, **kwargs): super(Intersection, self).__init__(**kwargs) self.children = children def validate(self, value, context=None): errors = [] for child in self.children: errors.extend(child.validate(value, context)) return errors class ExclusiveUnion(Schema): errors = { 'match': 'Must match one of the options.', 'match_only_one': 'Must match only one of the options.' } def __init__(self, children, **kwargs): super(ExclusiveUnion, self).__init__(**kwargs) self.children = children def validate(self, value, context=None): matches = 0 for child in self.children: if child.validate(value, context) == []: matches += 1 if not matches: return [self.make_error('match')] elif matches > 1: return [self.make_error('match_only_one')] return [] class Not(Schema): errors = { 'must_not_match': 'Must not match the option.' } def __init__(self, child, **kwargs): super(Not, self).__init__(**kwargs) self.child = child def validate(self, value, context=None): errors = [] if self.child.validate(value, context): return [] return [self.make_error('must_not_match')] # References class Ref(Schema): def __init__(self, ref_name): self.ref_name = ref_name def dereference(self, context): assert isinstance(context, dict) assert 'refs' in context assert self.ref_name in context['refs'] return context['refs'][self.ref_name] def validate(self, value, context=None): schema = self.dereference(context) return schema.validate(value, context) class RefSpace(Schema): def __init__(self, refs, root): assert root in refs self.refs = refs self.root = root self.root_validator = refs[root] def validate(self, value): context = {'refs': self.refs} return self.root_validator.validate(value, context) coreschema-0.0.4/coreschema/templates/ 0000775 0000000 0000000 00000000000 13225652165 0017742 5 ustar 00root root 0000000 0000000 coreschema-0.0.4/coreschema/templates/base.html 0000664 0000000 0000000 00000014706 13225652165 0021552 0 ustar 00root root 0000000 0000000
{{ schema.description }}
{% endif %}{{ schema.description }}
{% endif %}