def validate_items(self): from python_jsonschema_objects import classbuilder if self.__itemtype__ is None: return type_checks = self.__itemtype__ if not isinstance(type_checks, (tuple, list)): # we were given items = {'type': 'blah'} ; thus ensure the type for all data. type_checks = [type_checks] * len(self.data) elif len(type_checks) > len(self.data): raise ValidationError( "{1} does not have sufficient elements to validate against {0}" .format(self.__itemtype__, self.data)) typed_elems = [] for elem, typ in zip(self.data, type_checks): if isinstance(typ, dict): for param, paramval in six.iteritems(typ): validator = registry(param) if validator is not None: validator(paramval, elem, typ) elif util.safe_issubclass(typ, classbuilder.LiteralValue): val = typ(elem) val.validate() typed_elems.append(val) elif util.safe_issubclass(typ, classbuilder.ProtocolBase): if not isinstance(elem, typ): try: if isinstance(elem, (six.string_types, six.integer_types, float)): val = typ(elem) else: val = typ(**util.coerce_for_expansion(elem)) except TypeError as e: raise ValidationError("'{0}' is not a valid value for '{1}': {2}" .format(elem, typ, e)) else: val = elem val.validate() typed_elems.append(val) elif util.safe_issubclass(typ, ArrayValidator): val = typ(elem) val.validate() typed_elems.append(val) elif isinstance(typ, classbuilder.TypeProxy): try: if isinstance(elem, (six.string_types, six.integer_types, float)): val = typ(elem) else: val = typ(**util.coerce_for_expansion(elem)) except TypeError as e: raise ValidationError("'{0}' is not a valid value for '{1}': {2}" .format(elem, typ, e)) val.validate() typed_elems.append(val) return typed_elems
def create(name, item_constraint=None, **addl_constraints): """ Create an array validator based on the passed in constraints. If item_constraint is a tuple, it is assumed that tuple validation is being performed. If it is a class or dictionary, list validation will be performed. Classes are assumed to be subclasses of ProtocolBase, while dictionaries are expected to be basic types ('string', 'number', ...). addl_constraints is expected to be key-value pairs of any of the other constraints permitted by JSON Schema v4. """ from python_jsonschema_objects import classbuilder props = {} if item_constraint is not None: if isinstance(item_constraint, (tuple, list)): for i, elem in enumerate(item_constraint): isdict = isinstance(elem, (dict, )) isklass = isinstance(elem, type) and util.safe_issubclass( elem, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError( "Item constraint (position {0}) was not a schema". format(i)) else: isdict = isinstance(item_constraint, (dict, )) isklass = isinstance( item_constraint, type) and util.safe_issubclass( item_constraint, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError("Item constraint was not a schema") if isdict and item_constraint['type'] == 'array': item_constraint = ArrayValidator.create( name + "#sub", item_constraint=item_constraint['items'], addl_constraints=item_constraint) props['__itemtype__'] = item_constraint props.update(addl_constraints) validator = type(str(name), (ArrayValidator, ), props) return validator
def _make_type(self, typ, val): import python_jsonschema_objects.classbuilder as cb if getattr(typ, 'isLiteralClass', None) is True: return typ(val) if util.safe_issubclass(typ, cb.ProtocolBase): return typ(**util.coerce_for_expansion(val)) if util.safe_issubclass(typ, validators.ArrayValidator): return typ(val) raise validators.ValidationError( "additionalProperty type {0} was neither a literal " "nor a schema wrapper: {1}".format(typ, val))
def __setattr__(self, name, val): if name.startswith("_"): object.__setattr__(self, name, val) elif name in self.__propinfo__: # If its in __propinfo__, then it actually has a property defined. # The property does special validation, so we actually need to # run its setter. We get it from the class definition and call # it directly. XXX Heinous. prop = self.__class__.__dict__[self.__prop_names__[name]] prop.fset(self, val) else: # This is an additional property of some kind typ = getattr(self, '__extensible__', None) if typ is False: raise validators.ValidationError( "Attempted to set unknown property '{0}', " "but 'additionalProperties' is false.".format(name)) if typ is True: # There is no type defined, so just make it a basic literal # Pick the type based on the type of the values valtype = [k for k, t in six.iteritems(self.__SCHEMA_TYPES__) if t is not None and isinstance(val, t)] valtype = valtype[0] val = MakeLiteral(name, valtype, val) elif isinstance(typ, type) and getattr(typ, 'isLiteralClass', None) is True: val = typ(val) elif isinstance(typ, type) and util.safe_issubclass(typ, ProtocolBase): val = typ(**util.coerce_for_expansion(val)) self._extended_properties[name] = val
def apply(self, klass, resolve_map={}): """ Attach this property to the provided class Args: klass: (ProtocolBase) The class wrapper to which this property should be attached. resolve_map: (dict) A map of URIs to resolved ProtocolBase objects. """ assert util.safe_issubclass(klass, ProtocolBase) if self.is_array: typ = resolve_map[self.refuri] propdata = { 'type': 'array', 'validator': python_jsonschema_objects.wrapper_types.ArrayWrapper.create( self.refuri, item_constraint=typ) } else: propdata = {'type': resolve_map[self.refuri]} resolved_property = make_property(self.property_name, propdata, resolve_map[self.refuri].__doc__) setattr(klass, self.property_name, resolved_property) klass.__propinfo__[self.property_name] = { "$ref": self.refuri, "type": resolve_map[self.refuri] }
def __setattr__(self, name, val): if name.startswith("_"): object.__setattr__(self, name, val) elif name in self.__propinfo__: # If its in __propinfo__, then it actually has a property defined. # The property does special validation, so we actually need to # run its setter. We get it from the class definition and call # it directly. XXX Heinous. prop = self.__class__.__dict__[self.__prop_names__[name]] prop.fset(self, val) else: # This is an additional property of some kind typ = getattr(self, '__extensible__', None) if typ is False: raise validators.ValidationError( "Attempted to set unknown property '{0}', " "but 'additionalProperties' is false.".format(name)) if typ is True: # There is no type defined, so just make it a basic literal # Pick the type based on the type of the values valtype = [ k for k, t in six.iteritems(self.__SCHEMA_TYPES__) if t is not None and isinstance(val, t) ] valtype = valtype[0] val = MakeLiteral(name, valtype, val) elif isinstance( typ, type) and getattr(typ, 'isLiteralClass', None) is True: val = typ(val) elif isinstance(typ, type) and util.safe_issubclass( typ, ProtocolBase): val = typ(**util.coerce_for_expansion(val)) self._extended_properties[name] = val
def _make_type(self, typ, val): import python_jsonschema_objects.classbuilder as cb if getattr( typ, 'isLiteralClass', None) is True: return typ(val) if util.safe_issubclass(typ, cb.ProtocolBase): return typ(**util.coerce_for_expansion(val)) if util.safe_issubclass(typ, validators.ArrayValidator): return typ(val) raise validators.ValidationError( "additionalProperty type {0} was neither a literal " "nor a schema wrapper: {1}".format(typ, val))
def create(name, item_constraint=None, **addl_constraints): """ Create an array validator based on the passed in constraints. If item_constraint is a tuple, it is assumed that tuple validation is being performed. If it is a class or dictionary, list validation will be performed. Classes are assumed to be subclasses of ProtocolBase, while dictionaries are expected to be basic types ('string', 'number', ...). addl_constraints is expected to be key-value pairs of any of the other constraints permitted by JSON Schema v4. """ from python_jsonschema_objects import classbuilder props = {} if item_constraint is not None: if isinstance(item_constraint, (tuple, list)): for i, elem in enumerate(item_constraint): isdict = isinstance(elem, (dict,)) isklass = isinstance( elem, type) and util.safe_issubclass( elem, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError( "Item constraint (position {0}) was not a schema".format(i)) else: isdict = isinstance(item_constraint, (dict,)) isklass = isinstance( item_constraint, type) and util.safe_issubclass( item_constraint, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError("Item constraint was not a schema") if isdict and item_constraint['type'] == 'array': item_constraint = ArrayValidator.create(name + "#sub", item_constraint=item_constraint[ 'items'], addl_constraints=item_constraint) props['__itemtype__'] = item_constraint props.update(addl_constraints) validator = type(str(name), (ArrayValidator,), props) return validator
def apply(self, klass, resolve_map={}): """ Attach this property to the provided class Args: klass: (ProtocolBase) The class wrapper to which this property should be attached. resolve_map: (dict) A map of URIs to resolved ProtocolBase objects. """ assert util.safe_issubclass(klass, ProtocolBase) resolved_property = make_property(self.property_name, {'type': resolve_map[self.refuri]}, resolve_map[self.refuri].__doc__) setattr(klass, self.property_name, resolved_property) klass.__propinfo__[self.property_name] = { "$ref": self.refuri, "type": resolve_map[self.refuri] }
def _construct(self, uri, clsdata, parent=(ProtocolBase,)): if 'oneOf' in clsdata: potential_parents = self.resolve_classes(clsdata['oneOf']) for p in potential_parents: if util.safe_issubclass(p, ProtocolBase): self.resolved[uri] = self._build_object( uri, clsdata, (p,)) else: raise Exception("Don't know how to deal with this") return self.resolved[uri] elif 'anyOf' in clsdata: raise NotImplementedError( "anyOf is not supported as bare property") elif 'allOf' in clsdata: potential_parents = self.resolve_classes(clsdata['allOf']) parents = [] for p in potential_parents: if isinstance(p, dict): # This is additional constraints clsdata.update(p) elif util.safe_issubclass(p, ProtocolBase): parents.append(p) self.resolved[uri] = self._build_object( uri, clsdata, parents) return self.resolved[uri] elif '$ref' in clsdata: if 'type' in clsdata and util.safe_issubclass( clsdata['type'], (ProtocolBase, LiteralValue)): # It's possible that this reference was already resolved, in which # case it will have its type parameter set logging.debug("Using previously resolved type " "(with different URI) for %s", uri) self.resolved[uri] = clsdata['type'] elif uri in self.resolved: logging.debug("Using previously resolved object for %s", uri) else: logging.debug("Resolving object for %s", uri) with self.resolver.resolving(uri) as resolved: self.resolved[uri] = None # Set incase there is a circular reference in schema definition self.resolved[uri] = self._build_object( uri, resolved, parent) return self.resolved[uri] elif clsdata.get('type') == 'array' and 'items' in clsdata: self.resolved[uri] = self._build_object( uri, clsdata, parent) elif (clsdata.get('type', None) == 'object' or clsdata.get('properties', None) is not None or clsdata.get('additionalProperties', False)): self.resolved[uri] = self._build_object( uri, clsdata, parent) return self.resolved[uri] elif clsdata.get('type') in ('integer', 'number', 'string', 'boolean', 'null'): self.resolved[uri] = self._build_literal( uri, clsdata) return self.resolved[uri] elif 'enum' in clsdata: obj = self._build_literal(uri, clsdata) self.resolved[uri] = obj return obj elif 'type' in clsdata and util.safe_issubclass(clsdata['type'], ProtocolBase): self.resolved[uri] = clsdata.get('type') return self.resolved[uri] else: raise NotImplementedError( "Unable to parse schema object '{0}' with " "no type and no reference".format(clsdata))
def _construct(self, uri, clsdata, parent=(ProtocolBase,),**kw): if 'anyOf' in clsdata: raise NotImplementedError( "anyOf is not supported as bare property") elif 'oneOf' in clsdata: self.resolved[uri] = self._build_object( uri, clsdata, parent, **kw) return self.resolved[uri] elif 'allOf' in clsdata: potential_parents = self.resolve_classes(clsdata['allOf']) parents = [] for p in potential_parents: if isinstance(p, dict): # This is additional constraints clsdata.update(p) elif util.safe_issubclass(p, ProtocolBase): parents.append(p) self.resolved[uri] = self._build_object( uri, clsdata, parents,**kw) return self.resolved[uri] elif '$ref' in clsdata: if 'type' in clsdata and util.safe_issubclass( clsdata['type'], (ProtocolBase, LiteralValue)): # It's possible that this reference was already resolved, in which # case it will have its type parameter set logger.debug(util.lazy_format("Using previously resolved type " "(with different URI) for {0}", uri)) self.resolved[uri] = clsdata['type'] elif uri in self.resolved: logger.debug(util.lazy_format("Using previously resolved object for {0}", uri)) else: ref = clsdata['$ref'] refuri = util.resolve_ref_uri( self.resolver.resolution_scope, ref) if refuri in self.under_construction: logger.debug( util.lazy_format( "Resolving cyclic reference from {0} to {1}.", uri, refuri)) return TypeRef(refuri, self.resolved) else: logger.debug( util.lazy_format( "Resolving direct reference object for {0}: {1}", uri, refuri)) with self.resolver.resolving(refuri) as resolved: self.resolved[uri] = self.construct( refuri, resolved, parent) return self.resolved[uri] elif clsdata.get('type') == 'array' and 'items' in clsdata: clsdata_copy = {} clsdata_copy.update(clsdata) self.resolved[uri] = python_jsonschema_objects.wrapper_types.ArrayWrapper.create( uri, item_constraint=clsdata_copy.pop('items'), classbuilder=self, **clsdata_copy) return self.resolved[uri] elif isinstance(clsdata.get('type'), list): types = [] for i, item_detail in enumerate(clsdata['type']): subdata = {k: v for k, v in six.iteritems(clsdata) if k != 'type'} subdata['type'] = item_detail types.append(self._build_literal( uri + "_%s" % i, subdata)) self.resolved[uri] = TypeProxy(types) return self.resolved[uri] elif (clsdata.get('type', None) == 'object' or clsdata.get('properties', None) is not None or clsdata.get('additionalProperties', False)): self.resolved[uri] = self._build_object( uri, clsdata, parent,**kw) return self.resolved[uri] elif clsdata.get('type') in ('integer', 'number', 'string', 'boolean', 'null'): self.resolved[uri] = self._build_literal( uri, clsdata) return self.resolved[uri] elif 'enum' in clsdata: obj = self._build_literal(uri, clsdata) self.resolved[uri] = obj return obj elif 'type' in clsdata and util.safe_issubclass(clsdata['type'], ProtocolBase): self.resolved[uri] = clsdata.get('type') return self.resolved[uri] else: raise NotImplementedError( "Unable to parse schema object '{0}' with " "no type and no reference".format(clsdata))
def _construct(self, uri, clsdata, parent=(ProtocolBase,), **kw): if "anyOf" in clsdata: raise NotImplementedError("anyOf is not supported as bare property") elif "oneOf" in clsdata: """ If this object itself has a 'oneOf' designation, then construct a TypeProxy. """ klasses = self.construct_objects(clsdata["oneOf"], uri) logger.debug( util.lazy_format("Designating {0} as TypeProxy for {1}", uri, klasses) ) self.resolved[uri] = TypeProxy(klasses, title=clsdata.get("title")) return self.resolved[uri] elif "allOf" in clsdata: potential_parents = self.expand_references(uri, clsdata["allOf"]) parents = [] for p in potential_parents: if isinstance(p, dict): # This is additional constraints clsdata.update(p) elif util.safe_issubclass(p, ProtocolBase): parents.append(p) self.resolved[uri] = self._build_object(uri, clsdata, parents, **kw) return self.resolved[uri] elif "$ref" in clsdata: if "type" in clsdata and util.safe_issubclass( clsdata["type"], (ProtocolBase, LiteralValue) ): # It's possible that this reference was already resolved, in which # case it will have its type parameter set logger.debug( util.lazy_format( "Using previously resolved type " "(with different URI) for {0}", uri, ) ) self.resolved[uri] = clsdata["type"] elif uri in self.resolved: logger.debug( util.lazy_format("Using previously resolved object for {0}", uri) ) else: ref = clsdata["$ref"] typ = self.resolve_type(ref, uri) self.resolved[uri] = typ return self.resolved[uri] elif clsdata.get("type") == "array" and "items" in clsdata: clsdata_copy = {} clsdata_copy.update(clsdata) self.resolved[ uri ] = python_jsonschema_objects.wrapper_types.ArrayWrapper.create( uri, item_constraint=clsdata_copy.pop("items"), classbuilder=self, **clsdata_copy ) return self.resolved[uri] elif isinstance(clsdata.get("type"), list): types = [] for i, item_detail in enumerate(clsdata["type"]): subdata = {k: v for k, v in six.iteritems(clsdata) if k != "type"} subdata["type"] = item_detail types.append(self._build_literal(uri + "_%s" % i, subdata)) self.resolved[uri] = TypeProxy(types) return self.resolved[uri] elif ( clsdata.get("type", None) == "object" or clsdata.get("properties", None) is not None or clsdata.get("additionalProperties", False) ): self.resolved[uri] = self._build_object(uri, clsdata, parent, **kw) return self.resolved[uri] elif clsdata.get("type") in ("integer", "number", "string", "boolean", "null"): self.resolved[uri] = self._build_literal(uri, clsdata) return self.resolved[uri] elif "enum" in clsdata: obj = self._build_literal(uri, clsdata) self.resolved[uri] = obj return obj elif "type" in clsdata and util.safe_issubclass(clsdata["type"], ProtocolBase): self.resolved[uri] = clsdata.get("type") return self.resolved[uri] else: raise NotImplementedError( "Unable to parse schema object '{0}' with " "no type and no reference".format(clsdata) )
def setprop(self, val): if isinstance(info['type'], (list, tuple)): ok = False errors = [] type_checks = [] for typ in info['type']: if not isinstance(typ, dict): type_checks.append(typ) continue typ = ProtocolBase.__SCHEMA_TYPES__[typ['type']] if typ is None: typ = type(None) if isinstance(typ, (list, tuple)): type_checks.extend(typ) else: type_checks.append(typ) for typ in type_checks: if isinstance(val, typ): ok = True break elif hasattr(typ, 'isLiteralClass'): try: validator = typ(val) except Exception as e: errors.append("Failed to coerce to '{0}': {1}".format( typ, e)) pass else: validator.validate() ok = True break elif util.safe_issubclass(typ, ProtocolBase): # force conversion- thus the val rather than validator assignment try: val = typ(**util.coerce_for_expansion(val)) except Exception as e: errors.append("Failed to coerce to '{0}': {1}".format( typ, e)) pass else: val.validate() ok = True break elif util.safe_issubclass(typ, validators.ArrayValidator): try: val = typ(val) except Exception as e: errors.append("Failed to coerce to '{0}': {1}".format( typ, e)) pass else: val.validate() ok = True break if not ok: errstr = "\n".join(errors) raise validators.ValidationError( "Object must be one of {0}: \n{1}".format( info['type'], errstr)) elif info['type'] == 'array': instance = info['validator'](val) val = instance.validate() elif getattr(info['type'], 'isLiteralClass', False) is True: if not isinstance(val, info['type']): validator = info['type'](val) validator.validate() elif util.safe_issubclass(info['type'], ProtocolBase): if not isinstance(val, info['type']): val = info['type'](**util.coerce_for_expansion(val)) val.validate() elif isinstance(info['type'], TypeProxy): val = info['type'](val) elif info['type'] is None: # This is the null value if val is not None: raise validators.ValidationError( "None is only valid value for null") else: raise TypeError("Unknown object type: '{0}'".format(info['type'])) self._properties[prop] = val
def _construct(self, uri, clsdata, parent=(ProtocolBase, )): if 'anyOf' in clsdata: raise NotImplementedError( "anyOf is not supported as bare property") elif 'allOf' in clsdata: potential_parents = self.resolve_classes(clsdata['allOf']) parents = [] for p in potential_parents: if isinstance(p, dict): # This is additional constraints clsdata.update(p) elif util.safe_issubclass(p, ProtocolBase): parents.append(p) self.resolved[uri] = self._build_object(uri, clsdata, parents) return self.resolved[uri] elif '$ref' in clsdata: if 'type' in clsdata and util.safe_issubclass( clsdata['type'], (ProtocolBase, LiteralValue)): # It's possible that this reference was already resolved, in which # case it will have its type parameter set logger.debug( util.lazy_format( "Using previously resolved type " "(with different URI) for {0}", uri)) self.resolved[uri] = clsdata['type'] elif uri in self.resolved: logger.debug( util.lazy_format( "Using previously resolved object for {0}", uri)) else: logger.debug(util.lazy_format("Resolving object for {0}", uri)) with self.resolver.resolving(uri) as resolved: self.resolved[ uri] = None # Set incase there is a circular reference in schema definition self.resolved[uri] = self.construct(uri, resolved, parent) return self.resolved[uri] elif clsdata.get('type') == 'array' and 'items' in clsdata: clsdata_copy = {} clsdata_copy.update(clsdata) self.resolved[uri] = validators.ArrayValidator.create( uri, item_constraint=clsdata_copy.pop('items'), classbuilder=self, **clsdata_copy) return self.resolved[uri] elif isinstance(clsdata.get('type'), list): types = [] for i, item_detail in enumerate(clsdata['type']): subdata = { k: v for k, v in six.iteritems(clsdata) if k != 'type' } subdata['type'] = item_detail types.append(self._build_literal(uri + "_%s" % i, subdata)) self.resolved[uri] = TypeProxy(types) return self.resolved[uri] elif (clsdata.get('type', None) == 'object' or clsdata.get('properties', None) is not None or clsdata.get('additionalProperties', False)): self.resolved[uri] = self._build_object(uri, clsdata, parent) return self.resolved[uri] elif clsdata.get('type') in ('integer', 'number', 'string', 'boolean', 'null'): self.resolved[uri] = self._build_literal(uri, clsdata) return self.resolved[uri] elif 'enum' in clsdata: obj = self._build_literal(uri, clsdata) self.resolved[uri] = obj return obj elif 'type' in clsdata and util.safe_issubclass( clsdata['type'], ProtocolBase): self.resolved[uri] = clsdata.get('type') return self.resolved[uri] else: raise NotImplementedError( "Unable to parse schema object '{0}' with " "no type and no reference".format(clsdata))
def validate_items(self): """ Validates the items in the backing array, including performing type validation. Sets the _typed property and clears the dirty flag as a side effect Returns: The typed array """ logger.debug(fmt("Validating {}", self)) from python_jsonschema_objects import classbuilder if self.__itemtype__ is None: return type_checks = self.__itemtype__ if not isinstance(type_checks, (tuple, list)): # we were given items = {'type': 'blah'} ; thus ensure the type for all data. type_checks = [type_checks] * len(self.data) elif len(type_checks) > len(self.data): raise ValidationError( "{1} does not have sufficient elements to validate against {0}" .format(self.__itemtype__, self.data)) typed_elems = [] for elem, typ in zip(self.data, type_checks): if isinstance(typ, dict): for param, paramval in six.iteritems(typ): validator = registry(param) if validator is not None: validator(paramval, elem, typ) typed_elems.append(elem) elif util.safe_issubclass(typ, classbuilder.LiteralValue): val = typ(elem) val.validate() typed_elems.append(val) elif util.safe_issubclass(typ, classbuilder.ProtocolBase): if not isinstance(elem, typ): try: if isinstance(elem, (six.string_types, six.integer_types, float)): val = typ(elem) else: val = typ(**util.coerce_for_expansion(elem)) except TypeError as e: raise ValidationError("'{0}' is not a valid value for '{1}': {2}" .format(elem, typ, e)) else: val = elem val.validate() typed_elems.append(val) elif util.safe_issubclass(typ, ArrayWrapper): val = typ(elem) val.validate() typed_elems.append(val) elif isinstance(typ, (classbuilder.TypeProxy, classbuilder.TypeRef)): try: if isinstance(elem, (six.string_types, six.integer_types, float)): val = typ(elem) else: val = typ(**util.coerce_for_expansion(elem)) except TypeError as e: raise ValidationError("'{0}' is not a valid value for '{1}': {2}" .format(elem, typ, e)) else: val.validate() typed_elems.append(val) self._dirty = False self._typed = typed_elems return typed_elems
def setprop(self, val): if isinstance(info['type'], (list, tuple)): ok = False errors = [] type_checks = [] for typ in info['type']: if not isinstance(typ, dict): type_checks.append(typ) continue typ = ProtocolBase.__SCHEMA_TYPES__[typ['type']] if typ == None: typ = type(None) if isinstance(typ, (list, tuple)): type_checks.extend(typ) else: type_checks.append(typ) for typ in type_checks: if isinstance(val, typ): ok = True break elif hasattr(typ, 'isLiteralClass'): try: validator = typ(val) except Exception as e: errors.append( "Failed to coerce to '{0}': {1}".format(typ, e)) pass else: validator.validate() ok = True break elif util.safe_issubclass(typ, ProtocolBase): # force conversion- thus the val rather than validator assignment try: val = typ(**util.coerce_for_expansion(val)) except Exception as e: errors.append( "Failed to coerce to '{0}': {1}".format(typ, e)) pass else: val.validate() ok = True break if not ok: errstr = "\n".join(errors) raise validators.ValidationError( "Object must be one of {0}: \n{1}".format(info['type'], errstr)) elif info['type'] == 'array': instance = info['validator'](val) val = instance.validate() elif getattr(info['type'], 'isLiteralClass', False) is True: if not isinstance(val, info['type']): validator = info['type'](val) validator.validate() elif util.safe_issubclass(info['type'], ProtocolBase): if not isinstance(val, info['type']): val = info['type'](**util.coerce_for_expansion(val)) val.validate() else: raise TypeError("Unknown object type: '{0}'".format(info['type'])) self._properties[prop] = val
def create(name, item_constraint=None, **addl_constraints): """ Create an array validator based on the passed in constraints. If item_constraint is a tuple, it is assumed that tuple validation is being performed. If it is a class or dictionary, list validation will be performed. Classes are assumed to be subclasses of ProtocolBase, while dictionaries are expected to be basic types ('string', 'number', ...). addl_constraints is expected to be key-value pairs of any of the other constraints permitted by JSON Schema v4. """ from python_jsonschema_objects import classbuilder klassbuilder = addl_constraints.pop("classbuilder", None) props = {} if item_constraint is not None: if isinstance(item_constraint, (tuple, list)): for i, elem in enumerate(item_constraint): isdict = isinstance(elem, (dict,)) isklass = isinstance( elem, type) and util.safe_issubclass( elem, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError( "Item constraint (position {0}) is not a schema".format(i)) elif isinstance(item_constraint, classbuilder.TypeProxy): pass else: isdict = isinstance(item_constraint, (dict,)) isklass = isinstance( item_constraint, type) and util.safe_issubclass( item_constraint, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError("Item constraint is not a schema") if isdict and '$ref' in item_constraint: if klassbuilder is None: raise TypeError("Cannot resolve {0} without classbuilder" .format(item_constraint['$ref'])) uri = item_constraint['$ref'] if uri in klassbuilder.resolved: logger.debug(util.lazy_format( "Using previously resolved object for {0}", uri)) else: logger.debug(util.lazy_format("Resolving object for {0}", uri)) with klassbuilder.resolver.resolving(uri) as resolved: # Set incase there is a circular reference in schema definition klassbuilder.resolved[uri] = None klassbuilder.resolved[uri] = klassbuilder.construct( uri, resolved, (classbuilder.ProtocolBase,)) item_constraint = klassbuilder.resolved[uri] elif isdict and item_constraint['type'] == 'array': item_constraint = ArrayValidator.create(name + "#sub", item_constraint=item_constraint[ 'items'], addl_constraints=item_constraint) props['__itemtype__'] = item_constraint props.update(addl_constraints) validator = type(str(name), (ArrayValidator,), props) return validator
def setprop(self, val): if isinstance(info['type'], (list, tuple)): ok = False errors = [] type_checks = [] for typ in info['type']: if not isinstance(typ, dict): type_checks.append(typ) continue typ = next(t for n, t in validators.SCHEMA_TYPE_MAPPING if typ['type'] == n) if typ is None: typ = type(None) if isinstance(typ, (list, tuple)): type_checks.extend(typ) else: type_checks.append(typ) for typ in type_checks: if isinstance(val, typ): ok = True break elif hasattr(typ, 'isLiteralClass'): try: validator = typ(val) except Exception as e: errors.append("Failed to coerce to '{0}': {1}".format( typ, e)) pass else: validator.validate() ok = True break elif util.safe_issubclass(typ, ProtocolBase): # force conversion- thus the val rather than validator assignment try: val = typ(**util.coerce_for_expansion(val)) except Exception as e: errors.append("Failed to coerce to '{0}': {1}".format( typ, e)) pass else: val.validate() ok = True break elif util.safe_issubclass( typ, python_jsonschema_objects.wrapper_types.ArrayWrapper): try: val = typ(val) except Exception as e: errors.append("Failed to coerce to '{0}': {1}".format( typ, e)) pass else: val.validate() ok = True break if not ok: errstr = "\n".join(errors) raise validators.ValidationError( "Object must be one of {0}: \n{1}".format( info['type'], errstr)) elif info['type'] == 'array': val = info['validator'](val) val.validate() elif util.safe_issubclass( info['type'], python_jsonschema_objects.wrapper_types.ArrayWrapper): # An array type may have already been converted into an ArrayValidator val = info['type'](val) val.validate() elif getattr(info['type'], 'isLiteralClass', False) is True: if not isinstance(val, info['type']): validator = info['type'](val) validator.validate() if validator._value is not None: # This allows setting of default Literal values val = validator elif util.safe_issubclass(info['type'], ProtocolBase): if not isinstance(val, info['type']): val = info['type'](**util.coerce_for_expansion(val)) val.validate() elif isinstance(info['type'], TypeProxy): val = info['type'](val) elif isinstance(info['type'], TypeRef): if not isinstance(val, info['type'].ref_class): val = info['type'](**val) val.validate() elif info['type'] is None: # This is the null value if val is not None: raise validators.ValidationError( "None is only valid value for null") else: raise TypeError("Unknown object type: '{0}'".format(info['type'])) self._properties[prop] = val
def create(name, item_constraint=None, **addl_constraints): """ Create an array validator based on the passed in constraints. If item_constraint is a tuple, it is assumed that tuple validation is being performed. If it is a class or dictionary, list validation will be performed. Classes are assumed to be subclasses of ProtocolBase, while dictionaries are expected to be basic types ('string', 'number', ...). addl_constraints is expected to be key-value pairs of any of the other constraints permitted by JSON Schema v4. """ logger.debug( fmt("Constructing ArrayValidator with {} and {}", item_constraint, addl_constraints)) from python_jsonschema_objects import classbuilder klassbuilder = addl_constraints.pop("classbuilder", None) props = {} if item_constraint is not None: if isinstance(item_constraint, (tuple, list)): for i, elem in enumerate(item_constraint): isdict = isinstance(elem, (dict, )) isklass = isinstance(elem, type) and util.safe_issubclass( elem, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError( "Item constraint (position {0}) is not a schema". format(i)) elif isinstance(item_constraint, (classbuilder.TypeProxy, classbuilder.TypeRef)): pass elif util.safe_issubclass(item_constraint, ArrayWrapper): pass else: isdict = isinstance(item_constraint, (dict, )) isklass = isinstance( item_constraint, type) and util.safe_issubclass( item_constraint, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError("Item constraint is not a schema") if isdict and '$ref' in item_constraint: if klassbuilder is None: raise TypeError( "Cannot resolve {0} without classbuilder".format( item_constraint['$ref'])) uri = item_constraint['$ref'] if uri in klassbuilder.resolved: logger.debug( util.lazy_format( "Using previously resolved object for {0}", uri)) else: logger.debug( util.lazy_format("Resolving object for {0}", uri)) with klassbuilder.resolver.resolving(uri) as resolved: # Set incase there is a circular reference in schema definition klassbuilder.resolved[uri] = None klassbuilder.resolved[ uri] = klassbuilder.construct( uri, resolved, (classbuilder.ProtocolBase, )) item_constraint = klassbuilder.resolved[uri] elif isdict and item_constraint.get('type') == 'array': # We need to create a sub-array validator. item_constraint = ArrayWrapper.create( name + "#sub", item_constraint=item_constraint['items'], addl_constraints=item_constraint) elif isdict and 'oneOf' in item_constraint: # We need to create a TypeProxy validator uri = "{0}_{1}".format(name, "<anonymous_list_type>") type_array = [] for i, item_detail in enumerate(item_constraint['oneOf']): if '$ref' in item_detail: subtype = klassbuilder.construct( util.resolve_ref_uri( klassbuilder.resolver.resolution_scope, item_detail['$ref']), item_detail) else: subtype = klassbuilder.construct( uri + "_%s" % i, item_detail) type_array.append(subtype) item_constraint = classbuilder.TypeProxy(type_array) elif isdict and item_constraint.get('type') == 'object': """ We need to create a ProtocolBase object for this anonymous definition""" uri = "{0}_{1}".format(name, "<anonymous_list_type>") item_constraint = klassbuilder.construct( uri, item_constraint) props['__itemtype__'] = item_constraint strict = addl_constraints.pop('strict', False) props['_strict_'] = strict props.update(addl_constraints) validator = type(str(name), (ArrayWrapper, ), props) return validator
def validate_items(self): """ Validates the items in the backing array, including performing type validation. Sets the _typed property and clears the dirty flag as a side effect Returns: The typed array """ logger.debug(fmt("Validating {}", self)) from python_jsonschema_objects import classbuilder if self.__itemtype__ is None: return type_checks = self.__itemtype__ if not isinstance(type_checks, (tuple, list)): # we were given items = {'type': 'blah'} ; thus ensure the type for all data. type_checks = [type_checks] * len(self.data) elif len(type_checks) > len(self.data): raise ValidationError( "{1} does not have sufficient elements to validate against {0}" .format(self.__itemtype__, self.data)) typed_elems = [] for elem, typ in zip(self.data, type_checks): if isinstance(typ, dict): for param, paramval in six.iteritems(typ): validator = registry(param) if validator is not None: validator(paramval, elem, typ) typed_elems.append(elem) elif util.safe_issubclass(typ, classbuilder.LiteralValue): val = typ(elem) val.validate() typed_elems.append(val) elif util.safe_issubclass(typ, classbuilder.ProtocolBase): if not isinstance(elem, typ): try: if isinstance( elem, (six.string_types, six.integer_types, float)): val = typ(elem) else: val = typ(**util.coerce_for_expansion(elem)) except TypeError as e: raise ValidationError( "'{0}' is not a valid value for '{1}': {2}".format( elem, typ, e)) else: val = elem val.validate() typed_elems.append(val) elif util.safe_issubclass(typ, ArrayWrapper): val = typ(elem) val.validate() typed_elems.append(val) elif isinstance(typ, (classbuilder.TypeProxy, classbuilder.TypeRef)): try: if isinstance( elem, (six.string_types, six.integer_types, float)): val = typ(elem) else: val = typ(**util.coerce_for_expansion(elem)) except TypeError as e: raise ValidationError( "'{0}' is not a valid value for '{1}': {2}".format( elem, typ, e)) else: val.validate() typed_elems.append(val) self._dirty = False self._typed = typed_elems return typed_elems
def create(name, item_constraint=None, **addl_constraints): """ Create an array validator based on the passed in constraints. If item_constraint is a tuple, it is assumed that tuple validation is being performed. If it is a class or dictionary, list validation will be performed. Classes are assumed to be subclasses of ProtocolBase, while dictionaries are expected to be basic types ('string', 'number', ...). addl_constraints is expected to be key-value pairs of any of the other constraints permitted by JSON Schema v4. """ logger.debug( fmt( "Constructing ArrayValidator with {} and {}", item_constraint, addl_constraints, ) ) from python_jsonschema_objects import classbuilder klassbuilder = addl_constraints.pop( "classbuilder", None ) # type: python_jsonschema_objects.classbuilder.ClassBuilder props = {} if item_constraint is not None: if isinstance(item_constraint, (tuple, list)): for i, elem in enumerate(item_constraint): isdict = isinstance(elem, (dict,)) isklass = isinstance(elem, type) and util.safe_issubclass( elem, (classbuilder.ProtocolBase, classbuilder.LiteralValue) ) if not any([isdict, isklass]): raise TypeError( "Item constraint (position {0}) is not a schema".format(i) ) elif isinstance( item_constraint, (classbuilder.TypeProxy, classbuilder.TypeRef) ): pass elif util.safe_issubclass(item_constraint, ArrayWrapper): pass else: isdict = isinstance(item_constraint, (dict,)) isklass = isinstance(item_constraint, type) and util.safe_issubclass( item_constraint, (classbuilder.ProtocolBase, classbuilder.LiteralValue), ) if not any([isdict, isklass]): raise TypeError("Item constraint is not a schema") if isdict and "$ref" in item_constraint: if klassbuilder is None: raise TypeError( "Cannot resolve {0} without classbuilder".format( item_constraint["$ref"] ) ) item_constraint = klassbuilder.resolve_type( item_constraint["$ref"], name ) elif isdict and item_constraint.get("type") == "array": # We need to create a sub-array validator. item_constraint = ArrayWrapper.create( name + "#sub", item_constraint=item_constraint["items"], addl_constraints=item_constraint, ) elif isdict and "oneOf" in item_constraint: # We need to create a TypeProxy validator uri = "{0}_{1}".format(name, "<anonymous_list_type>") type_array = klassbuilder.construct_objects( item_constraint["oneOf"], uri ) item_constraint = classbuilder.TypeProxy(type_array) elif isdict and item_constraint.get("type") == "object": """ We need to create a ProtocolBase object for this anonymous definition""" uri = "{0}_{1}".format(name, "<anonymous_list_type>") item_constraint = klassbuilder.construct(uri, item_constraint) props["__itemtype__"] = item_constraint strict = addl_constraints.pop("strict", False) props["_strict_"] = strict props.update(addl_constraints) validator = type(str(name), (ArrayWrapper,), props) return validator
def create(name, item_constraint=None, **addl_constraints): """ Create an array validator based on the passed in constraints. If item_constraint is a tuple, it is assumed that tuple validation is being performed. If it is a class or dictionary, list validation will be performed. Classes are assumed to be subclasses of ProtocolBase, while dictionaries are expected to be basic types ('string', 'number', ...). addl_constraints is expected to be key-value pairs of any of the other constraints permitted by JSON Schema v4. """ from python_jsonschema_objects import classbuilder klassbuilder = addl_constraints.pop("classbuilder", None) props = {} if item_constraint is not None: if isinstance(item_constraint, (tuple, list)): for i, elem in enumerate(item_constraint): isdict = isinstance(elem, (dict,)) isklass = isinstance( elem, type) and util.safe_issubclass( elem, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError( "Item constraint (position {0}) is not a schema".format(i)) elif isinstance(item_constraint, classbuilder.TypeProxy): pass elif util.safe_issubclass(item_constraint, ArrayValidator): pass else: isdict = isinstance(item_constraint, (dict,)) isklass = isinstance( item_constraint, type) and util.safe_issubclass( item_constraint, (classbuilder.ProtocolBase, classbuilder.LiteralValue)) if not any([isdict, isklass]): raise TypeError("Item constraint is not a schema") if isdict and '$ref' in item_constraint: if klassbuilder is None: raise TypeError("Cannot resolve {0} without classbuilder" .format(item_constraint['$ref'])) uri = item_constraint['$ref'] if uri in klassbuilder.resolved: logger.debug(util.lazy_format( "Using previously resolved object for {0}", uri)) else: logger.debug(util.lazy_format("Resolving object for {0}", uri)) with klassbuilder.resolver.resolving(uri) as resolved: # Set incase there is a circular reference in schema definition klassbuilder.resolved[uri] = None klassbuilder.resolved[uri] = klassbuilder.construct( uri, resolved, (classbuilder.ProtocolBase,)) item_constraint = klassbuilder.resolved[uri] elif isdict and item_constraint.get('type') == 'array': # We need to create a sub-array validator. item_constraint = ArrayValidator.create(name + "#sub", item_constraint=item_constraint[ 'items'], addl_constraints=item_constraint) elif isdict and 'oneOf' in item_constraint: # We need to create a TypeProxy validator uri = "{0}_{1}".format(name, "<anonymous_list_type>") type_array = [] for i, item_detail in enumerate(item_constraint['oneOf']): if '$ref' in item_detail: subtype = klassbuilder.construct( util.resolve_ref_uri( klassbuilder.resolver.resolution_scope, item_detail['$ref']), item_detail) else: subtype = klassbuilder.construct( uri + "_%s" % i, item_detail) type_array.append(subtype) item_constraint = classbuilder.TypeProxy(type_array) props['__itemtype__'] = item_constraint props.update(addl_constraints) validator = type(str(name), (ArrayValidator,), props) return validator
def setprop(self, val): if isinstance(info['type'], (list, tuple)): ok = False errors = [] type_checks = [] for typ in info['type']: if not isinstance(typ, dict): type_checks.append(typ) continue typ = next(t for n, t in validators.SCHEMA_TYPE_MAPPING if typ['type'] == t) if typ is None: typ = type(None) if isinstance(typ, (list, tuple)): type_checks.extend(typ) else: type_checks.append(typ) for typ in type_checks: if isinstance(val, typ): ok = True break elif hasattr(typ, 'isLiteralClass'): try: validator = typ(val) except Exception as e: errors.append( "Failed to coerce to '{0}': {1}".format(typ, e)) pass else: validator.validate() ok = True break elif util.safe_issubclass(typ, ProtocolBase): # force conversion- thus the val rather than validator assignment try: val = typ(**util.coerce_for_expansion(val)) except Exception as e: errors.append( "Failed to coerce to '{0}': {1}".format(typ, e)) pass else: val.validate() ok = True break elif util.safe_issubclass(typ, validators.ArrayValidator): try: val = typ(val) except Exception as e: errors.append( "Failed to coerce to '{0}': {1}".format(typ, e)) pass else: val.validate() ok = True break if not ok: errstr = "\n".join(errors) raise validators.ValidationError( "Object must be one of {0}: \n{1}".format(info['type'], errstr)) elif info['type'] == 'array': instance = info['validator'](val) val = instance.validate() elif util.safe_issubclass(info['type'], validators.ArrayValidator): # An array type may have already been converted into an ArrayValidator instance = info['type'](val) val = instance.validate() elif getattr(info['type'], 'isLiteralClass', False) is True: if not isinstance(val, info['type']): validator = info['type'](val) validator.validate() elif util.safe_issubclass(info['type'], ProtocolBase): if not isinstance(val, info['type']): val = info['type'](**util.coerce_for_expansion(val)) val.validate() elif isinstance(info['type'], TypeProxy): val = info['type'](val) elif info['type'] is None: # This is the null value if val is not None: raise validators.ValidationError( "None is only valid value for null") else: raise TypeError("Unknown object type: '{0}'".format(info['type'])) self._properties[prop] = val
def _construct(self, uri, clsdata, parent=(ProtocolBase, )): if 'oneOf' in clsdata: potential_parents = self.resolve_classes(clsdata['oneOf']) for p in potential_parents: if util.safe_issubclass(p, ProtocolBase): self.resolved[uri] = self._build_object( uri, clsdata, (p, )) else: raise Exception("Don't know how to deal with this") return self.resolved[uri] elif 'anyOf' in clsdata: raise NotImplementedError( "anyOf is not supported as bare property") elif 'allOf' in clsdata: potential_parents = self.resolve_classes(clsdata['allOf']) parents = [] for p in potential_parents: if isinstance(p, dict): # This is additional constraints clsdata.update(p) elif util.safe_issubclass(p, ProtocolBase): parents.append(p) self.resolved[uri] = self._build_object(uri, clsdata, parents) return self.resolved[uri] elif '$ref' in clsdata: if 'type' in clsdata and util.safe_issubclass( clsdata['type'], (ProtocolBase, LiteralValue)): # It's possible that this reference was already resolved, in which # case it will have its type parameter set logging.debug( "Using previously resolved type " "(with different URI) for %s", uri) self.resolved[uri] = clsdata['type'] elif uri in self.resolved: logging.debug("Using previously resolved object for %s", uri) else: logging.debug("Resolving object for %s", uri) with self.resolver.resolving(uri) as resolved: self.resolved[ uri] = None # Set incase there is a circular reference in schema definition self.resolved[uri] = self._build_object( uri, resolved, parent) return self.resolved[uri] elif clsdata.get('type') == 'array' and 'items' in clsdata: self.resolved[uri] = self._build_object(uri, clsdata, parent) elif (clsdata.get('type', None) == 'object' or clsdata.get('properties', None) is not None or clsdata.get('additionalProperties', False)): self.resolved[uri] = self._build_object(uri, clsdata, parent) return self.resolved[uri] elif clsdata.get('type') in ('integer', 'number', 'string', 'boolean', 'null'): self.resolved[uri] = self._build_literal(uri, clsdata) return self.resolved[uri] elif 'enum' in clsdata: obj = self._build_literal(uri, clsdata) self.resolved[uri] = obj return obj elif 'type' in clsdata and util.safe_issubclass( clsdata['type'], ProtocolBase): self.resolved[uri] = clsdata.get('type') return self.resolved[uri] else: raise NotImplementedError( "Unable to parse schema object '{0}' with " "no type and no reference".format(clsdata))