Esempio n. 1
0
class VocabularyKeyword(Keyword):
    key = "$vocabulary"

    def __init__(self, parentschema: JSONSchema, value: Mapping[str, bool]):
        super().__init__(parentschema, value)

        if not isinstance(parentschema, Metaschema):
            return

        if (core_vocab_uri := str(parentschema.core_vocabulary.uri)) not in value or \
                value[core_vocab_uri] is not True:
            raise JSONSchemaError(
                f'The "$vocabulary" keyword must list the core vocabulary with a value of true'
            )

        for vocab_uri, vocab_required in value.items():
            try:
                (vocab_uri := URI(vocab_uri)).validate(require_scheme=True,
                                                       require_normalized=True)
            except URIError as e:
                raise JSONSchemaError from e

            try:
                vocabulary = Catalogue.get_vocabulary(vocab_uri)
                parentschema.kwclasses.update(vocabulary.kwclasses)
            except CatalogueError:
                if vocab_required:
                    raise JSONSchemaError(
                        f"The metaschema requires an unrecognized vocabulary '{vocab_uri}'"
                    )
Esempio n. 2
0
    def __init__(self, parentschema: JSONSchema, value: str):
        super().__init__(parentschema, value)
        if value != '#':
            raise JSONSchemaError(
                f'"$recursiveRef" may only take the value "#"')

        self.refschema = None
Esempio n. 3
0
 def resolve(self) -> None:
     uri = URI(self.json.value)
     if not uri.has_absolute_base():
         if (base_uri := self.parentschema.base_uri) is not None:
             uri = uri.resolve(base_uri)
         else:
             raise JSONSchemaError(
                 f'No base URI against which to resolve the "$dynamicRef" value "{uri}"'
             )
Esempio n. 4
0
    def __init__(self, parentschema: JSONSchema, value: str):
        super().__init__(parentschema, value)

        # this is not required by the spec, but it doesn't make sense
        # for a $dynamicRef *not* to end in a plain-name fragment
        if (fragment := URI(value).fragment) is None or '/' in fragment:
            raise JSONSchemaError(
                'The value for "$dynamicRef" must end in a plain-name fragment'
            )
Esempio n. 5
0
    def __init__(self, parentschema: JSONSchema, value: Mapping[str, bool]):
        super().__init__(parentschema, value)

        if not isinstance(parentschema, Metaschema):
            return

        if (core_vocab_uri := str(parentschema.core_vocabulary.uri)) not in value or \
                value[core_vocab_uri] is not True:
            raise JSONSchemaError(
                f'The "$vocabulary" keyword must list the core vocabulary with a value of true'
            )
Esempio n. 6
0
    def __init__(self, parentschema: JSONSchema, value: str):
        super().__init__(parentschema, value)

        (uri := URI(value)).validate(require_normalized=True,
                                     allow_fragment=False)
        if not uri.is_absolute():
            if (base_uri := parentschema.base_uri) is not None:
                uri = uri.resolve(base_uri)
            else:
                raise JSONSchemaError(
                    f'No base URI against which to resolve the "$id" value "{value}"'
                )
Esempio n. 7
0
    key = "$recursiveRef"

    def __init__(self, parentschema: JSONSchema, value: str):
        super().__init__(parentschema, value)
        if value != '#':
            raise JSONSchemaError(
                f'"$recursiveRef" may only take the value "#"')

        self.refschema = None

    def resolve(self) -> None:
        if (base_uri := self.parentschema.base_uri) is not None:
            self.refschema = Catalogue.get_schema(
                base_uri, metaschema_uri=self.parentschema.metaschema_uri)
        else:
            raise JSONSchemaError(
                f'No base URI against which to resolve "$recursiveRef"')

    def evaluate(self, instance: JSON, scope: Scope) -> None:
        refschema = self.refschema
        if (recursive_anchor := refschema.get("$recursiveAnchor")) and \
                recursive_anchor.value is True:
            base_scope = scope.root
            for key in scope.path:
                if (base_schema := base_scope.schema) is refschema:
                    break
                if (base_anchor := base_schema.get("$recursiveAnchor")) and \
                        base_anchor.value is True:
                    refschema = base_schema
                    break
                base_scope = base_scope.children[key]
Esempio n. 8
0
    def metaschema(self) -> Metaschema:
        from jschon.catalogue import Catalogue

        if (uri := self.metaschema_uri) is None:
            raise JSONSchemaError(
                "The schema's metaschema URI has not been set")
Esempio n. 9
0
    def validate(self) -> JSONSchema:
        if not self.metaschema.evaluate(JSON(self.value)).valid:
            raise JSONSchemaError(
                f"The schema is invalid against its metaschema")

        return self
Esempio n. 10
0
        parent = self.parent
        while parent is not None:
            if isinstance(parent, JSONSchema):
                return parent
            parent = parent.parent

    @property
    def metaschema(self) -> Metaschema:
        from jschon.catalogue import Catalogue

        if (uri := self.metaschema_uri) is None:
            raise JSONSchemaError(
                "The schema's metaschema URI has not been set")

        if not isinstance(metaschema := Catalogue.get_schema(uri), Metaschema):
            raise JSONSchemaError(
                f"The schema referenced by {uri} is not a metachema")

        return metaschema

    @property
    def metaschema_uri(self) -> Optional[URI]:
        if self._metaschema_uri is not None:
            return self._metaschema_uri
        if self.parentschema is not None:
            return self.parentschema.metaschema_uri

    @metaschema_uri.setter
    def metaschema_uri(self, value: Optional[URI]) -> None:
        self._metaschema_uri = value

    @property
Esempio n. 11
0
            uri, metaschema_uri=self.parentschema.metaschema_uri)

    def evaluate(self, instance: JSON, scope: Scope) -> None:
        self.refschema.evaluate(instance, scope)


class AnchorKeyword(Keyword):
    key = "$anchor"

    def __init__(self, parentschema: JSONSchema, value: str):
        super().__init__(parentschema, value)

        if (base_uri := parentschema.base_uri) is not None:
            uri = URI(f'{base_uri}#{value}')
        else:
            raise JSONSchemaError(f'No base URI for "$anchor" value "{value}"')

        # just add a schema reference to the catalogue, rather than updating
        # the schema URI itself; this way we keep canonical URIs consistent
        # for subschemas regardless of anchor usage
        # parentschema.uri = uri
        Catalogue.add_schema(uri, parentschema)

    def can_evaluate(self, instance: JSON) -> bool:
        return False


class DynamicRefKeyword(Keyword):
    key = "$dynamicRef"

    def __init__(self, parentschema: JSONSchema, value: str):