def __init__(self, **props): self._extended_properties = dict() self._properties = dict( zip(self.__prop_names__.values(), [None for x in six.moves.xrange(len(self.__prop_names__))])) # To support defaults, we have to actually execute the constructors # but only for the ones that have defaults set. for name in self.__has_default__: if name not in props: logger.debug(util.lazy_format("Initializing '{0}' ", name)) setattr(self, name, None) for prop in props: try: logger.debug( util.lazy_format("Setting value for '{0}' to {1}", prop, props[prop])) if props[prop] is not None: setattr(self, prop, props[prop]) except validators.ValidationError as e: import sys raise six.reraise( type(e), type(e)(str(e) + " \nwhile setting '{0}' in {1}".format( prop, self.__class__.__name__)), sys.exc_info()[2]) if getattr(self, '__strict__', None): self.validate()
def __init__(self, **props): self._extended_properties = dict() self._properties = dict(zip(self.__prop_names__.values(), [None for x in six.moves.xrange(len(self.__prop_names__))])) # To support defaults, we have to actually execute the constructors # but only for the ones that have defaults set. for name in self.__has_default__: if name not in props: default_value = copy.deepcopy( self.__propinfo__[name]['default'] ) logger.debug(util.lazy_format("Initializing '{0}' to '{1}'", name, default_value)) setattr(self, name, default_value) for prop in props: try: logger.debug(util.lazy_format( "Setting value for '{0}' to {1}", prop, props[prop])) if props[prop] is not None: setattr(self, prop, props[prop]) except validators.ValidationError as e: import sys raise six.reraise( type(e), type(e)(str(e) + " \nwhile setting '{0}' in {1}".format( prop, self.__class__.__name__)), sys.exc_info()[2]) if getattr(self, '__strict__', None): self.validate()
def construct(self, uri, *args, **kw): """ Wrapper to debug things """ logger.debug(util.lazy_format("Constructing {0}", uri)) ret = self._construct(uri, *args, **kw) logger.debug(util.lazy_format("Constructed {0}", ret)) # processing pending items for pending_item in self.pending: logger.debug( util.lazy_format( "Atttempting to resolve property " "{0.property_name} for {0.uri}", pending_item)) if pending_item.refuri not in self.resolved: continue if pending_item.uri not in self.resolved: raise ValueError( "{0} refers to {1}, but {0} has not been resolved".format( pending_item.uri, pending_item.refuri)) target_class = self.resolved[pending_item.uri] pending_item.apply(target_class, self.resolved) return ret
def construct(self, uri, *args, **kw): """ Wrapper to debug things """ logger.debug(util.lazy_format("Constructing {0}", uri)) if ("override" not in kw or kw["override"] is False) and uri in self.resolved: logger.debug(util.lazy_format("Using existing {0}", uri)) return self.resolved[uri] else: ret = self._construct(uri, *args, **kw) logger.debug(util.lazy_format("Constructed {0}", ret)) return ret
def construct(self, uri, *args, **kw): """ Wrapper to debug things """ logger.debug(util.lazy_format("Constructing {0}", uri)) if ('override' not in kw or kw['override'] is False) \ and uri in self.resolved: logger.debug(util.lazy_format("Using existing {0}", uri)) return self.resolved[uri] else: ret = self._construct(uri, *args, **kw) logger.debug(util.lazy_format("Constructed {0}", ret)) return ret
def resolve_type(self, ref, source): """ Return a resolved type for a URI, potentially constructing one if necessary""" uri = util.resolve_ref_uri(self.resolver.resolution_scope, ref) if uri in self.resolved: return self.resolved[uri] elif uri in self.under_construction: logger.debug( util.lazy_format( "Using a TypeRef to avoid a cyclic reference for {0} -> {1} ", uri, source, )) return TypeRef(uri, self.resolved) else: logger.debug( util.lazy_format( "Resolving direct reference object {0} -> {1}", source, uri)) with self.resolver.resolving(ref) as resolved: self.resolved[uri] = self.construct(uri, resolved, (ProtocolBase, )) return self.resolved[uri]
def __init__(self, **props): self._extended_properties = dict() self._properties = dict(zip(self.__prop_names__.values(), [None for x in six.moves.xrange(len(self.__prop_names__))])) for prop in props: try: logger.debug(util.lazy_format("Setting value for '{0}' to {1}", prop, props[prop])) setattr(self, prop, props[prop]) except validators.ValidationError as e: import sys raise six.reraise(type(e), type(e)(str(e) + " \nwhile setting '{0}' in {1}".format( prop, self.__class__.__name__)), sys.exc_info()[2])
def __init__(self, **props): self._extended_properties = dict() self._properties = dict(zip(self.__prop_names__.values(), [None for x in six.moves.xrange(len(self.__prop_names__))])) for prop in props: try: logger.debug(util.lazy_format("Setting value for '{0}' to {1}", prop, props[prop])) setattr(self, prop, props[prop]) except validators.ValidationError as e: import sys raise six.reraise(type(e), type(e)(str(e) + " \nwhile setting '{0}' in {1}".format( prop, self.__class__.__name__)), sys.exc_info()[2]) if getattr(self, '__strict__', None): self.validate()
def __call__(self, *a, **kw): validation_errors = [] valid_types = self._types for klass in valid_types: logger.debug(util.lazy_format( "Attempting to instantiate {0} as {1}", self.__class__, klass)) try: obj = klass(*a, **kw) except TypeError as e: validation_errors.append((klass, e)) except validators.ValidationError as e: validation_errors.append((klass, e)) else: return obj else: # We got nothing raise validators.ValidationError( "Unable to instantiate any valid types: \n" "".join("{0}: {1}\n".format(k, e) for k, e in validation_errors) )
def __call__(self, *a, **kw): validation_errors = [] valid_types = self._types for klass in valid_types: logger.debug( util.lazy_format("Attempting to instantiate {0} as {1}", self.__class__, klass)) try: obj = klass(*a, **kw) except TypeError as e: validation_errors.append((klass, e)) except validators.ValidationError as e: validation_errors.append((klass, e)) else: return obj else: # We got nothing raise validators.ValidationError( "Unable to instantiate any valid types: \n" "".join("{0}: {1}\n".format(k, e) for k, e in validation_errors))
def __new__(cls, **props): """ Overridden to support oneOf, where we need to instantiate a different class depending on what value we've seen """ if getattr(cls, "__validation__", None) is None: new = super(ProtocolBase, cls).__new__ if new is object.__new__: return new(cls) return new(cls, **props) valid_types = cls.__validation__.get("type", None) if valid_types is None or not isinstance(valid_types, list): new = super(ProtocolBase, cls).__new__ if new is object.__new__: return new(cls) return new(cls, **props) obj = None validation_errors = [] for klass in valid_types: logger.debug( util.lazy_format("Attempting to instantiate {0} as {1}", cls, klass) ) try: obj = klass(**props) obj.validate() except validators.ValidationError as e: validation_errors.append((klass, e)) else: break else: # We got nothing raise validators.ValidationError( "Unable to instantiate any valid types: \n" "".join("{0}: {1}\n".format(k, e) for k, e in validation_errors) ) return obj
def __new__(cls, **props): """ Overridden to support oneOf, where we need to instantiate a different class depending on what value we've seen """ if getattr(cls, '__validation__', None) is None: new = super(ProtocolBase, cls).__new__ if new is object.__new__: return new(cls) return new(cls, **props) valid_types = cls.__validation__.get('type', None) if valid_types is None or not isinstance(valid_types, list): new = super(ProtocolBase, cls).__new__ if new is object.__new__: return new(cls) return new(cls, **props) obj = None validation_errors = [] for klass in valid_types: logger.debug(util.lazy_format( "Attempting to instantiate {0} as {1}", cls, klass)) try: obj = klass(**props) except validators.ValidationError as e: validation_errors.append((klass, e)) else: break else: # We got nothing raise validators.ValidationError( "Unable to instantiate any valid types: \n" "".join("{0}: {1}\n".format(k, e) for k, e in validation_errors) ) return obj
def _build_object(self, nm, clsdata, parents, **kw): logger.debug(util.lazy_format("Building object {0}", nm)) # To support circular references, we tag objects that we're # currently building as "under construction" self.under_construction.add(nm) props = {} defaults = set() properties = {} for p in parents: properties = util.propmerge(properties, p.__propinfo__) if "properties" in clsdata: properties = util.propmerge(properties, clsdata["properties"]) name_translation = {} for prop, detail in properties.items(): logger.debug(util.lazy_format("Handling property {0}.{1}", nm, prop)) properties[prop]["raw_name"] = prop name_translation[prop] = prop.replace("@", "") prop = name_translation[prop] if detail.get("default", None) is not None: defaults.add(prop) if detail.get("type", None) == "object": uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous>") self.resolved[uri] = self.construct(uri, detail, (ProtocolBase,)) props[prop] = make_property( prop, {"type": self.resolved[uri]}, self.resolved[uri].__doc__ ) properties[prop]["type"] = self.resolved[uri] elif "type" not in detail and "$ref" in detail: ref = detail["$ref"] typ = self.resolve_type(ref, ".".join([nm, prop])) props[prop] = make_property(prop, {"type": typ}, typ.__doc__) properties[prop]["$ref"] = ref properties[prop]["type"] = typ elif "oneOf" in detail: potential = self.expand_references(nm, detail["oneOf"]) logger.debug( util.lazy_format("Designating {0} as oneOf {1}", prop, potential) ) desc = detail["description"] if "description" in detail else "" props[prop] = make_property(prop, {"type": potential}, desc) elif "type" in detail and detail["type"] == "array": if "items" in detail and isinstance(detail["items"], dict): if "$ref" in detail["items"]: typ = self.resolve_type(detail["items"]["$ref"], nm) constraints = copy.copy(detail) constraints["strict"] = kw.get("strict") propdata = { "type": "array", "validator": python_jsonschema_objects.wrapper_types.ArrayWrapper.create( nm, item_constraint=typ, **constraints ), } else: uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous_field>") try: if "oneOf" in detail["items"]: typ = TypeProxy( self.construct_objects( detail["items"]["oneOf"], uri ) ) else: typ = self.construct(uri, detail["items"]) constraints = copy.copy(detail) constraints["strict"] = kw.get("strict") propdata = { "type": "array", "validator": python_jsonschema_objects.wrapper_types.ArrayWrapper.create( uri, item_constraint=typ, **constraints ), } except NotImplementedError: typ = detail["items"] constraints = copy.copy(detail) constraints["strict"] = kw.get("strict") propdata = { "type": "array", "validator": python_jsonschema_objects.wrapper_types.ArrayWrapper.create( uri, item_constraint=typ, **constraints ), } props[prop] = make_property(prop, propdata, typ.__doc__) elif "items" in detail: typs = [] for i, elem in enumerate(detail["items"]): uri = "{0}/{1}/<anonymous_{2}>".format(nm, prop, i) typ = self.construct(uri, elem) typs.append(typ) props[prop] = make_property(prop, {"type": typs}) else: desc = detail["description"] if "description" in detail else "" uri = "{0}/{1}".format(nm, prop) typ = self.construct(uri, detail) props[prop] = make_property(prop, {"type": typ}, desc) props["__extensible__"] = pattern_properties.ExtensibleValidator( nm, clsdata, self ) props["__prop_names__"] = name_translation props["__propinfo__"] = properties required = set.union(*[p.__required__ for p in parents]) if "required" in clsdata: for prop in clsdata["required"]: required.add(prop) invalid_requires = [req for req in required if req not in props["__propinfo__"]] if len(invalid_requires) > 0: raise validators.ValidationError( "Schema Definition Error: {0} schema requires " "'{1}', but properties are not defined".format(nm, invalid_requires) ) props["__required__"] = required props["__has_default__"] = defaults if required and kw.get("strict"): props["__strict__"] = True props["__title__"] = clsdata.get("title") cls = type(str(nm.split("/")[-1]), tuple(parents), props) self.under_construction.remove(nm) return cls
def construct(self, uri, *args, **kw): """ Wrapper to debug things """ logger.debug(util.lazy_format("Constructing {0}", uri)) ret = self._construct(uri, *args, **kw) logger.debug(util.lazy_format("Constructed {0}", ret)) return ret
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 _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 _build_object(self, nm, clsdata, parents): logger.debug(util.lazy_format("Building object {0}", nm)) props = {} properties = {} for p in parents: properties = util.propmerge(properties, p.__propinfo__) if 'properties' in clsdata: properties = util.propmerge(properties, clsdata['properties']) name_translation = {} for prop, detail in properties.items(): properties[prop]['raw_name'] = prop name_translation[prop] = prop.replace('@', '') prop = name_translation[prop] if detail.get('type', None) == 'object': uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous>") self.resolved[uri] = self.construct(uri, detail, (ProtocolBase, )) props[prop] = make_property(prop, {'type': self.resolved[uri]}, self.resolved[uri].__doc__) properties[prop]['type'] = self.resolved[uri] elif 'type' not in detail and '$ref' in detail: ref = detail['$ref'] uri = util.resolve_ref_uri(self.resolver.resolution_scope, ref) if uri not in self.resolved: with self.resolver.resolving(ref) as resolved: self.resolved[uri] = self.construct( uri, resolved, (ProtocolBase, )) props[prop] = make_property(prop, {'type': self.resolved[uri]}, self.resolved[uri].__doc__) properties[prop]['$ref'] = uri properties[prop]['type'] = self.resolved[uri] elif 'oneOf' in detail: potential = self.resolve_classes(detail['oneOf']) logger.debug( util.lazy_format("Designating {0} as oneOf {1}", prop, potential)) desc = detail['description'] if 'description' in detail else "" props[prop] = make_property(prop, {'type': potential}, desc) elif 'type' in detail and detail['type'] == 'array': if 'items' in detail and isinstance(detail['items'], dict): if '$ref' in detail['items']: uri = util.resolve_ref_uri( self.resolver.resolution_scope, detail['items']['$ref']) typ = self.construct(uri, detail['items']) propdata = { 'type': 'array', 'validator': validators.ArrayValidator.create( uri, item_constraint=typ) } else: uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous_field>") try: if 'oneOf' in detail['items']: typ = TypeProxy([ self.construct(uri + "_%s" % i, item_detail) if '$ref' not in item_detail else self.construct( util.resolve_ref_uri( self.resolver.resolution_scope, item_detail['$ref']), item_detail) for i, item_detail in enumerate( detail['items']['oneOf']) ]) else: typ = self.construct(uri, detail['items']) propdata = { 'type': 'array', 'validator': validators.ArrayValidator.create( uri, item_constraint=typ, addl_constraints=detail) } except NotImplementedError: typ = detail['items'] propdata = { 'type': 'array', 'validator': validators.ArrayValidator.create( uri, item_constraint=typ, addl_constraints=detail) } props[prop] = make_property(prop, propdata, typ.__doc__) elif 'items' in detail: typs = [] for i, elem in enumerate(detail['items']): uri = "{0}/{1}/<anonymous_{2}>".format(nm, prop, i) typ = self.construct(uri, detail['items']) typs.append(typ) props[prop] = make_property(prop, { 'type': 'tuple', 'items': typ }, typ.__doc__) else: desc = detail['description'] if 'description' in detail else "" uri = "{0}/{1}".format(nm, prop) typ = self.construct(uri, detail) props[prop] = make_property(prop, {'type': typ}, desc) """ If this object itself has a 'oneOf' designation, then make the validation 'type' the list of potential objects. """ if 'oneOf' in clsdata: klasses = self.resolve_classes(clsdata['oneOf']) # Need a validation to check that it meets one of them props['__validation__'] = {'type': klasses} props['__extensible__'] = True if 'additionalProperties' in clsdata: addlProp = clsdata['additionalProperties'] if addlProp is False: props['__extensible__'] = False elif addlProp is True: props['__extensible__'] = True else: if '$ref' in addlProp: refs = self.resolve_classes([addlProp]) else: uri = "{0}/{1}_{2}".format(nm, "<additionalProperties>", "<anonymous>") self.resolved[uri] = self.construct( uri, addlProp, (ProtocolBase, )) refs = [self.resolved[uri]] props['__extensible__'] = refs[0] props['__prop_names__'] = name_translation props['__propinfo__'] = properties required = set.union(*[p.__required__ for p in parents]) if 'required' in clsdata: for prop in clsdata['required']: required.add(prop) invalid_requires = [ req for req in required if req not in props['__propinfo__'] ] if len(invalid_requires) > 0: raise validators.ValidationError( "Schema Definition Error: {0} schema requires " "'{1}', but properties are not defined".format( nm, invalid_requires)) props['__required__'] = required cls = type(str(nm.split('/')[-1]), tuple(parents), props) return cls
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 _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 _build_object(self, nm, clsdata, parents): logger.debug(util.lazy_format("Building object {0}", nm)) props = {} properties = {} for p in parents: properties = util.propmerge(properties, p.__propinfo__) if 'properties' in clsdata: properties = util.propmerge(properties, clsdata['properties']) name_translation = {} for prop, detail in properties.items(): properties[prop]['raw_name'] = prop name_translation[prop] = prop.replace('@', '') prop = name_translation[prop] if detail.get('type', None) == 'object': uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous>") self.resolved[uri] = self.construct( uri, detail, (ProtocolBase,)) props[prop] = make_property(prop, {'type': self.resolved[uri]}, self.resolved[uri].__doc__) properties[prop]['type'] = self.resolved[uri] elif 'type' not in detail and '$ref' in detail: ref = detail['$ref'] uri = util.resolve_ref_uri(self.resolver.resolution_scope, ref) if uri not in self.resolved: with self.resolver.resolving(ref) as resolved: self.resolved[uri] = self.construct( uri, resolved, (ProtocolBase,)) props[prop] = make_property(prop, {'type': self.resolved[uri]}, self.resolved[uri].__doc__) properties[prop]['$ref'] = uri properties[prop]['type'] = self.resolved[uri] elif 'oneOf' in detail: potential = self.resolve_classes(detail['oneOf']) logger.debug(util.lazy_format("Designating {0} as oneOf {1}", prop, potential)) desc = detail[ 'description'] if 'description' in detail else "" props[prop] = make_property(prop, {'type': potential}, desc ) elif 'type' in detail and detail['type'] == 'array': if 'items' in detail and isinstance(detail['items'], dict): if '$ref' in detail['items']: uri = util.resolve_ref_uri( self.resolver.resolution_scope, detail['items']['$ref']) typ = self.construct(uri, detail['items']) propdata = { 'type': 'array', 'validator': validators.ArrayValidator.create( uri, item_constraint=typ)} else: uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous_field>") try: if 'oneOf' in detail['items']: typ = TypeProxy([ self.construct(uri + "_%s" % i, item_detail) if '$ref' not in item_detail else self.construct(util.resolve_ref_uri( self.resolver.resolution_scope, item_detail['$ref']), item_detail) for i, item_detail in enumerate(detail['items']['oneOf'])] ) else: typ = self.construct(uri, detail['items']) propdata = {'type': 'array', 'validator': validators.ArrayValidator.create(uri, item_constraint=typ, addl_constraints=detail)} except NotImplementedError: typ = detail['items'] propdata = {'type': 'array', 'validator': validators.ArrayValidator.create(uri, item_constraint=typ, addl_constraints=detail)} props[prop] = make_property(prop, propdata, typ.__doc__) elif 'items' in detail: typs = [] for i, elem in enumerate(detail['items']): uri = "{0}/{1}/<anonymous_{2}>".format(nm, prop, i) typ = self.construct(uri, detail['items']) typs.append(typ) props[prop] = make_property(prop, {'type': 'tuple', 'items': typ}, typ.__doc__) else: desc = detail[ 'description'] if 'description' in detail else "" uri = "{0}/{1}".format(nm, prop) typ = self.construct(uri, detail) props[prop] = make_property(prop, {'type': typ}, desc) """ If this object itself has a 'oneOf' designation, then make the validation 'type' the list of potential objects. """ if 'oneOf' in clsdata: klasses = self.resolve_classes(clsdata['oneOf']) # Need a validation to check that it meets one of them props['__validation__'] = {'type': klasses} props['__extensible__'] = True if 'additionalProperties' in clsdata: addlProp = clsdata['additionalProperties'] if addlProp is False: props['__extensible__'] = False elif addlProp is True: props['__extensible__'] = True else: if '$ref' in addlProp: refs = self.resolve_classes([addlProp]) else: uri = "{0}/{1}_{2}".format(nm, "<additionalProperties>", "<anonymous>") self.resolved[uri] = self.construct( uri, addlProp, (ProtocolBase,)) refs = [self.resolved[uri]] props['__extensible__'] = refs[0] props['__prop_names__'] = name_translation props['__propinfo__'] = properties required = set.union(*[p.__required__ for p in parents]) if 'required' in clsdata: for prop in clsdata['required']: required.add(prop) invalid_requires = [req for req in required if req not in props['__propinfo__']] if len(invalid_requires) > 0: raise validators.ValidationError("Schema Definition Error: {0} schema requires " "'{1}', but properties are not defined" .format(nm, invalid_requires)) props['__required__'] = required cls = type(str(nm.split('/')[-1]), tuple(parents), props) return cls
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 _build_object(self, nm, clsdata, parents, **kw): logger.debug(util.lazy_format("Building object {0}", nm)) clsdata_hash = hash_dict(clsdata) props = {} properties = {} for p in parents: properties = util.propmerge(properties, p.__propinfo__) if 'properties' in clsdata: properties = util.propmerge(properties, clsdata['properties']) name_translation = {} for prop, detail in properties.items(): logger.debug( util.lazy_format("Handling property {0}.{1}", nm, prop)) properties[prop]['raw_name'] = prop name_translation[prop] = prop.replace('@', '').replace(' ', '_') raw_prop = prop prop = name_translation[prop] if detail.get('type', None) == 'object': #~ uri = "{0}/{1}_{2}".format(nm, #~ prop, "<anonymous>") # Re-use existing submodules if possible uri = detail['title'] # Scrub raw_name since that's a function of this property but not of the substructure. detail_clean = dict(detail) del detail_clean['raw_name'] self.resolved[uri] = self.construct(uri, detail_clean, (ProtocolBase, )) props[prop] = make_property(prop, {'type': self.resolved[uri]}, self.resolved[uri].__doc__) properties[raw_prop]['type'] = self.resolved[uri] elif 'type' not in detail and '$ref' in detail: ref = detail['$ref'] uri = util.resolve_ref_uri(self.resolver.resolution_scope, ref) logger.debug( util.lazy_format("Resolving reference {0} for {1}.{2}", ref, nm, prop)) if uri not in self.resolved: with self.resolver.resolving(ref) as resolved: self.resolved[uri] = self.construct( uri, resolved, (ProtocolBase, )) props[prop] = make_property(prop, {'type': self.resolved[uri]}, self.resolved[uri].__doc__) properties[prop]['$ref'] = uri properties[prop]['type'] = self.resolved[uri] elif 'oneOf' in detail: potential = self.resolve_classes(detail['oneOf']) logger.debug( util.lazy_format("Designating {0} as oneOf {1}", prop, potential)) desc = detail['description'] if 'description' in detail else "" props[prop] = make_property(prop, {'type': potential}, desc) elif 'type' in detail and detail['type'] == 'array': if 'items' in detail and isinstance(detail['items'], dict): if '$ref' in detail['items']: uri = util.resolve_ref_uri( self.resolver.resolution_scope, detail['items']['$ref']) typ = self.construct(uri, detail['items']) propdata = { 'type': 'array', 'validator': python_jsonschema_objects.wrapper_types. ArrayWrapper.create(uri, item_constraint=typ) } else: uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous_field>") try: if 'oneOf' in detail['items']: typ = TypeProxy([ self.construct(uri + "_%s" % i, item_detail) if '$ref' not in item_detail else self.construct( util.resolve_ref_uri( self.resolver.resolution_scope, item_detail['$ref']), item_detail) for i, item_detail in enumerate( detail['items']['oneOf']) ]) else: # Re-use existing submodules if possible uri = detail['items']['title'] typ = self.construct(uri, detail['items']) propdata = { 'type': 'array', 'validator': python_jsonschema_objects.wrapper_types. ArrayWrapper.create(uri, item_constraint=typ, addl_constraints=detail) } except NotImplementedError: typ = detail['items'] propdata = { 'type': 'array', 'validator': python_jsonschema_objects.wrapper_types. ArrayWrapper.create(uri, item_constraint=typ, addl_constraints=detail) } props[prop] = make_property(prop, propdata, typ.__doc__) elif 'items' in detail: typs = [] for i, elem in enumerate(detail['items']): uri = "{0}/{1}/<anonymous_{2}>".format(nm, prop, i) typ = self.construct(uri, detail['items']) typs.append(typ) props[prop] = make_property(prop, { 'type': 'tuple', 'items': typ }, typ.__doc__) else: desc = detail['description'] if 'description' in detail else "" uri = "{0}/{1}".format(nm, prop) typ = self.construct(uri, detail) props[prop] = make_property(prop, {'type': typ}, desc) """ If this object itself has a 'oneOf' designation, then make the validation 'type' the list of potential objects. """ if 'oneOf' in clsdata: klasses = self.resolve_classes(clsdata['oneOf']) # Need a validation to check that it meets one of them props['__validation__'] = {'type': klasses} props['__extensible__'] = pattern_properties.ExtensibleValidator( nm, clsdata, self) props['__prop_names__'] = name_translation props['__propinfo__'] = properties required = set.union(*[p.__required__ for p in parents]) if 'required' in clsdata: for prop in clsdata['required']: required.add(prop) invalid_requires = [ req for req in required if req not in props['__propinfo__'] ] if len(invalid_requires) > 0: raise validators.ValidationError( "Schema Definition Error: {0} schema requires " "'{1}', but properties are not defined".format( nm, invalid_requires)) props['__required__'] = required props['__clsdata_hash__'] = clsdata_hash if required and kw.get("strict"): props['__strict__'] = True cls = type(str(nm.split('/')[-1]), tuple(parents), props) return cls
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 _build_object(self, nm, clsdata, parents, **kw): logger.debug(util.lazy_format("Building object {0}", nm)) # To support circular references, we tag objects that we're # currently building as "under construction" self.under_construction.append(nm) props = {} defaults = set() properties = {} for p in parents: properties = util.propmerge(properties, p.__propinfo__) if 'properties' in clsdata: properties = util.propmerge(properties, clsdata['properties']) name_translation = {} for prop, detail in properties.items(): logger.debug( util.lazy_format("Handling property {0}.{1}", nm, prop)) properties[prop]['raw_name'] = prop name_translation[prop] = prop.replace('@', '') prop = name_translation[prop] if detail.get('default', None) is not None: defaults.add(prop) if detail.get('type', None) == 'object': uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous>") self.resolved[uri] = self.construct(uri, detail, (ProtocolBase, )) props[prop] = make_property(prop, {'type': self.resolved[uri]}, self.resolved[uri].__doc__) properties[prop]['type'] = self.resolved[uri] elif 'type' not in detail and '$ref' in detail: ref = detail['$ref'] uri = util.resolve_ref_uri(self.resolver.resolution_scope, ref) logger.debug( util.lazy_format("Resolving reference {0} for {1}.{2}", ref, nm, prop)) if uri not in self.resolved: """ if $ref is under construction, then we're staring at a circular reference. We save the information required to construct the property for later. """ if uri in self.under_construction: self.pending.add( UnresolvedProperty(uri=nm, property_name=prop, refuri=uri, is_array=False)) continue with self.resolver.resolving(ref) as resolved: self.resolved[uri] = self.construct( uri, resolved, (ProtocolBase, )) props[prop] = make_property(prop, {'type': self.resolved[uri]}, self.resolved[uri].__doc__) properties[prop]['$ref'] = uri properties[prop]['type'] = self.resolved[uri] elif 'oneOf' in detail: potential = self.resolve_classes(detail['oneOf']) logger.debug( util.lazy_format("Designating {0} as oneOf {1}", prop, potential)) desc = detail['description'] if 'description' in detail else "" props[prop] = make_property(prop, {'type': potential}, desc) elif 'type' in detail and detail['type'] == 'array': if 'items' in detail and isinstance(detail['items'], dict): if '$ref' in detail['items']: ref = detail['items']['$ref'] uri = util.resolve_ref_uri( self.resolver.resolution_scope, ref) if uri not in self.resolved and uri in self.under_construction: """ if $ref is under construction, then we're staring at a circular reference. We save the information required to construct the property for later. """ self.pending.add( UnresolvedProperty(uri=nm, property_name=prop, refuri=uri, is_array=True)) continue else: typ = self.construct(uri, detail['items']) propdata = { 'type': 'array', 'validator': python_jsonschema_objects.wrapper_types. ArrayWrapper.create(uri, item_constraint=typ) } else: uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous_field>") try: if 'oneOf' in detail['items']: typ = TypeProxy([ self.construct(uri + "_%s" % i, item_detail) if '$ref' not in item_detail else self.construct( util.resolve_ref_uri( self.resolver.resolution_scope, item_detail['$ref']), item_detail) for i, item_detail in enumerate( detail['items']['oneOf']) ]) else: typ = self.construct(uri, detail['items']) propdata = { 'type': 'array', 'validator': python_jsonschema_objects.wrapper_types. ArrayWrapper.create(uri, item_constraint=typ, addl_constraints=detail) } except NotImplementedError: typ = detail['items'] propdata = { 'type': 'array', 'validator': python_jsonschema_objects.wrapper_types. ArrayWrapper.create(uri, item_constraint=typ, addl_constraints=detail) } props[prop] = make_property(prop, propdata, typ.__doc__) elif 'items' in detail: typs = [] for i, elem in enumerate(detail['items']): uri = "{0}/{1}/<anonymous_{2}>".format(nm, prop, i) typ = self.construct(uri, detail['items']) typs.append(typ) props[prop] = make_property(prop, { 'type': 'tuple', 'items': typ }, typ.__doc__) else: desc = detail['description'] if 'description' in detail else "" uri = "{0}/{1}".format(nm, prop) typ = self.construct(uri, detail) props[prop] = make_property(prop, {'type': typ}, desc) """ If this object itself has a 'oneOf' designation, then make the validation 'type' the list of potential objects. """ if 'oneOf' in clsdata: klasses = self.resolve_classes(clsdata['oneOf']) # Need a validation to check that it meets one of them props['__validation__'] = {'type': klasses} props['__extensible__'] = pattern_properties.ExtensibleValidator( nm, clsdata, self) props['__prop_names__'] = name_translation props['__propinfo__'] = properties required = set.union(*[p.__required__ for p in parents]) if 'required' in clsdata: for prop in clsdata['required']: required.add(prop) invalid_requires = [ req for req in required if req not in props['__propinfo__'] ] if len(invalid_requires) > 0: raise validators.ValidationError( "Schema Definition Error: {0} schema requires " "'{1}', but properties are not defined".format( nm, invalid_requires)) props['__required__'] = required props['__has_default__'] = defaults if required and kw.get("strict"): props['__strict__'] = True cls = type(str(nm.split('/')[-1]), tuple(parents), props) self.under_construction.remove(nm) return cls
def _build_object(self, nm, clsdata, parents,**kw): logger.debug(util.lazy_format("Building object {0}", nm)) # To support circular references, we tag objects that we're # currently building as "under construction" self.under_construction.add(nm) props = {} defaults = set() properties = {} for p in parents: properties = util.propmerge(properties, p.__propinfo__) if 'properties' in clsdata: properties = util.propmerge(properties, clsdata['properties']) name_translation = {} for prop, detail in properties.items(): logger.debug(util.lazy_format("Handling property {0}.{1}",nm, prop)) properties[prop]['raw_name'] = prop name_translation[prop] = prop.replace('@', '') prop = name_translation[prop] if detail.get('default', None) is not None: defaults.add(prop) if detail.get('type', None) == 'object': uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous>") self.resolved[uri] = self.construct( uri, detail, (ProtocolBase,)) props[prop] = make_property(prop, {'type': self.resolved[uri]}, self.resolved[uri].__doc__) properties[prop]['type'] = self.resolved[uri] elif 'type' not in detail and '$ref' in detail: ref = detail['$ref'] uri = util.resolve_ref_uri(self.resolver.resolution_scope, ref) logger.debug(util.lazy_format( "Resolving reference {0} for {1}.{2}", ref, nm, prop )) if uri in self.resolved: typ = self.resolved[uri] else: typ = self.construct(uri, detail, (ProtocolBase,)) props[prop] = make_property(prop, {'type': typ}, typ.__doc__) properties[prop]['$ref'] = uri properties[prop]['type'] = typ elif 'oneOf' in detail: potential = self.resolve_classes(detail['oneOf']) logger.debug(util.lazy_format("Designating {0} as oneOf {1}", prop, potential)) desc = detail[ 'description'] if 'description' in detail else "" props[prop] = make_property(prop, {'type': potential}, desc ) elif 'type' in detail and detail['type'] == 'array': if 'items' in detail and isinstance(detail['items'], dict): if '$ref' in detail['items']: uri = util.resolve_ref_uri( self.resolver.resolution_scope, detail['items']['$ref']) typ = self.construct(uri, detail['items']) constraints = copy.copy(detail) constraints['strict'] = kw.get('strict') propdata = { 'type': 'array', 'validator': python_jsonschema_objects.wrapper_types.ArrayWrapper.create( uri, item_constraint=typ, **constraints)} else: uri = "{0}/{1}_{2}".format(nm, prop, "<anonymous_field>") try: if 'oneOf' in detail['items']: typ = TypeProxy([ self.construct(uri + "_%s" % i, item_detail) if '$ref' not in item_detail else self.construct(util.resolve_ref_uri( self.resolver.resolution_scope, item_detail['$ref']), item_detail) for i, item_detail in enumerate(detail['items']['oneOf'])] ) else: typ = self.construct(uri, detail['items']) constraints = copy.copy(detail) constraints['strict'] = kw.get('strict') propdata = {'type': 'array', 'validator': python_jsonschema_objects.wrapper_types.ArrayWrapper.create( uri, item_constraint=typ, **constraints)} except NotImplementedError: typ = detail['items'] constraints = copy.copy(detail) constraints['strict'] = kw.get('strict') propdata = {'type': 'array', 'validator': python_jsonschema_objects.wrapper_types.ArrayWrapper.create( uri, item_constraint=typ, **constraints)} props[prop] = make_property(prop, propdata, typ.__doc__) elif 'items' in detail: typs = [] for i, elem in enumerate(detail['items']): uri = "{0}/{1}/<anonymous_{2}>".format(nm, prop, i) typ = self.construct(uri, elem) typs.append(typ) props[prop] = make_property(prop, {'type': typs}, ) else: desc = detail[ 'description'] if 'description' in detail else "" uri = "{0}/{1}".format(nm, prop) typ = self.construct(uri, detail) props[prop] = make_property(prop, {'type': typ}, desc) """ If this object itself has a 'oneOf' designation, then make the validation 'type' the list of potential objects. """ if 'oneOf' in clsdata: klasses = self.resolve_classes(clsdata['oneOf']) # Need a validation to check that it meets one of them props['__validation__'] = {'type': klasses} props['__extensible__'] = pattern_properties.ExtensibleValidator( nm, clsdata, self) props['__prop_names__'] = name_translation props['__propinfo__'] = properties required = set.union(*[p.__required__ for p in parents]) if 'required' in clsdata: for prop in clsdata['required']: required.add(prop) invalid_requires = [req for req in required if req not in props['__propinfo__']] if len(invalid_requires) > 0: raise validators.ValidationError("Schema Definition Error: {0} schema requires " "'{1}', but properties are not defined" .format(nm, invalid_requires)) props['__required__'] = required props['__has_default__'] = defaults if required and kw.get("strict"): props['__strict__'] = True props['__title__'] = clsdata.get('title') cls = type(str(nm.split('/')[-1]), tuple(parents), props) self.under_construction.remove(nm) return cls
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 (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))