Esempio n. 1
0
    def resolve_fragment(self, document, fragment):
        """
        Resolve a ``fragment`` within the referenced ``document``.

        Arguments:

            document:

                The referrant document

            fragment (str):

                a URI fragment to resolve within it

        """

        fragment = fragment.lstrip(u"/")
        parts = unquote(fragment).split(u"/") if fragment else []

        for part in parts:
            part = part.replace(u"~1", u"/").replace(u"~0", u"~")

            if isinstance(document, Sequence):
                # Array indexes should be turned into integers
                try:
                    part = int(part)
                except ValueError:
                    pass
            try:
                document = document[part]
            except (TypeError, LookupError):
                raise RefResolutionError("Unresolvable JSON pointer: %r" %
                                         fragment)

        return document
Esempio n. 2
0
def _resolve_refs(schema_root: str, json_spec: dict, context: str) -> dict:
    """
    Resolve JSON Schema references in `json_spec` relative to `base_uri`,
    return `json_spec` with all references inlined. `context` is used to
    format error to provide (wait for it) context.
    """
    resolver = _build_ref_resolver(schema_root, json_spec)

    def _resolve_ref(ref: str) -> dict:
        # Don't resolve local refs, since this would make loading recursive schemas impossible.
        if ref.startswith("#"):
            return {"$ref": ref}

        with resolver.resolving(ref) as resolved_spec:
            # resolved_spec might have unresolved refs in it, so we pass
            # it back to _resolve_refs to resolve them. This way,
            # we can fully resolve schemas with nested refs.

            try:
                res = _resolve_refs(schema_root, resolved_spec, ref)
            except RefResolutionError as e:
                raise RefResolutionError(
                    f"Error resolving '$ref':{ref!r}: {e}") from e

            # as reslover uses cache we don't want to return mutable
            # objects, so we make a copy
            return copy.deepcopy(res)

    try:
        return _map_refs(json_spec, _resolve_ref)
    except RefResolutionError as e:
        raise RefResolutionError(
            f"Error resolving refs in {context!r}: {e}") from e
Esempio n. 3
0
    def resolving(self, ref):
        """
        Context manager which resolves a JSON ``ref`` and enters the
        resolution scope of this ref.

        :argument str ref: reference to resolve

        """

        full_uri = urljoin(self.resolution_scope, ref)
        uri, fragment = urldefrag(full_uri)
        if not uri:
            uri = self.base_uri

        if uri in self.store:
            document = self.store[uri]
        else:
            try:
                document = self.resolve_remote(uri)
            except Exception as exc:
                raise RefResolutionError(exc)

        old_base_uri, self.base_uri = self.base_uri, uri
        try:
            with self.in_scope(uri):
                yield self.resolve_fragment(document, fragment)
        finally:
            self.base_uri = old_base_uri
Esempio n. 4
0
 def pop_scope(self):
     try:
         self._scopes_stack.pop()
     except IndexError:
         raise RefResolutionError(
             "Failed to pop the scope from an empty stack. "
             "`pop_scope()` should only be called once for every "
             "`push_scope()`")
Esempio n. 5
0
    def resolve_from_url(self, url):
        url, fragment = urldefrag(url)
        try:
            document = self.store[url]
        except KeyError:
            try:
                document = self.resolve_remote(url)
            except Exception as exc:
                raise RefResolutionError(exc)

        return self.resolve_fragment(document, fragment)
Esempio n. 6
0
def iter_jsonpointer_parts(jsonpath):
    """
    Generates the ``jsonpath`` parts according to jsonpointer spec.

    :param str jsonpath:  a jsonpath to resolve within document
    :return:              The parts of the path as generator), without
                          converting any step to int, and None if None.

    :author: Julian Berman, ankostis

    Examples::

        >>> list(iter_jsonpointer_parts('/a/b'))
        ['a', 'b']

        >>> list(iter_jsonpointer_parts('/a//b'))
        ['a', '', 'b']

        >>> list(iter_jsonpointer_parts('/'))
        ['']

        >>> list(iter_jsonpointer_parts(''))
        []


    But paths are strings begining (NOT_MPL: but not ending) with slash('/')::

        >>> list(iter_jsonpointer_parts(None))
        Traceback (most recent call last):
        AttributeError: 'NoneType' object has no attribute 'split'

        >>> list(iter_jsonpointer_parts('a'))
        Traceback (most recent call last):
        jsonschema.exceptions.RefResolutionError: Jsonpointer-path(a) must start with '/'!

        #>>> list(iter_jsonpointer_parts('/a/'))
        #Traceback (most recent call last):
        #jsonschema.exceptions.RefResolutionError: Jsonpointer-path(a) must NOT ends with '/'!

    """

    #     if jsonpath.endswith('/'):
    #         msg = "Jsonpointer-path({}) must NOT finish with '/'!"
    #         raise RefResolutionError(msg.format(jsonpath))
    parts = jsonpath.split("/")
    if parts.pop(0) != "":
        msg = "Jsonpointer-path({}) must start with '/'!"
        raise RefResolutionError(msg.format(jsonpath))

    for part in parts:
        part = unescape_jsonpointer_part(part)

        yield part
Esempio n. 7
0
def resolve_jsonpointer(doc, jsonpointer, default=_scream):
    """
    Resolve a ``jsonpointer`` within the referenced ``doc``.

    :param doc:      the referrant document
    :param str path: a jsonpointer to resolve within document
    :param default:  A value to return if path does not resolve.
    :return:         the resolved doc-item or raises :class:`RefResolutionError`
    :raises:     RefResolutionError (if cannot resolve path and no `default`)

    Examples:

        >>> dt = {
        ...     'pi':3.14,
        ...     'foo':'bar',
        ...     'df': pd.DataFrame(np.ones((3,2)), columns=list('VN')),
        ...     'sub': {
        ...         'sr': pd.Series({'abc':'def'}),
        ...     }
        ... }
        >>> resolve_jsonpointer(dt, '/pi', default=_scream)
        3.14

        >>> resolve_jsonpointer(dt, '/pi/BAD')
        Traceback (most recent call last):
        jsonschema.exceptions.RefResolutionError: Unresolvable JSON pointer('/pi/BAD')@(BAD)

        >>> resolve_jsonpointer(dt, '/pi/BAD', 'Hi!')
        'Hi!'

    :author: Julian Berman, ankostis
    """
    for part in iter_jsonpointer_parts(jsonpointer):
        if isinstance(doc, cabc.Sequence):
            # Array indexes should be turned into integers
            try:
                part = int(part)
            except ValueError:
                pass
        try:
            doc = doc[part]
        except (TypeError, LookupError):
            if default is _scream:
                raise RefResolutionError(
                    "Unresolvable JSON pointer(%r)@(%s)" % (jsonpointer, part)
                )
            else:
                return default

    return doc
Esempio n. 8
0
    def _resolve_ref(ref: str) -> dict:
        # Don't resolve local refs, since this would make loading recursive schemas impossible.
        if ref.startswith("#"):
            return {"$ref": ref}

        with resolver.resolving(ref) as resolved_spec:
            # resolved_spec might have unresolved refs in it, so we pass
            # it back to _resolve_refs to resolve them. This way,
            # we can fully resolve schemas with nested refs.

            try:
                res = _resolve_refs(schema_root, resolved_spec, ref)
            except RefResolutionError as e:
                raise RefResolutionError(
                    f"Error resolving '$ref':{ref!r}: {e}") from e

            # as reslover uses cache we don't want to return mutable
            # objects, so we make a copy
            return copy.deepcopy(res)
Esempio n. 9
0
def set_jsonpointer(doc, jsonpointer, value, object_factory=dict):
    """
    Resolve a ``jsonpointer`` within the referenced ``doc``.

    :param doc: the referrant document
    :param str jsonpointer: a jsonpointer to the node to modify
    :raises: RefResolutionError (if jsonpointer empty, missing, invalid-contet)
    """

    parts = list(iter_jsonpointer_parts(jsonpointer))

    # Will scream if used on 1st iteration.
    #
    pdoc = None
    ppart = None
    for i, part in enumerate(parts):
        if isinstance(doc, cabc.Sequence) and not isinstance(doc, str):
            # Array indexes should be turned into integers
            #
            doclen = len(doc)
            if part == "-":
                part = doclen
            else:
                try:
                    part = int(part)
                except ValueError:
                    raise RefResolutionError(
                        "Expected numeric index(%s) for sequence at (%r)[%i]"
                        % (part, jsonpointer, i)
                    )
                else:
                    if part > doclen:
                        raise RefResolutionError(
                            "Index(%s) out of bounds(%i) of (%r)[%i]"
                            % (part, doclen, jsonpointer, i)
                        )
        try:
            ndoc = doc[part]
        except (LookupError):
            break  # Branch-extension needed.
        except (TypeError):  # Maybe indexing a string...
            ndoc = object_factory()
            pdoc[ppart] = ndoc
            doc = ndoc
            break  # Branch-extension needed.

        doc, pdoc, ppart = ndoc, doc, part
    else:
        doc = pdoc  # If loop exhausted, cancel last assignment.

    # Build branch with value-leaf.
    #
    nbranch = value
    for part2 in reversed(parts[i + 1 :]):
        ndoc = object_factory()
        ndoc[part2] = nbranch
        nbranch = ndoc

    # Attach new-branch.
    try:
        doc[part] = nbranch
    # Inserting last sequence-element raises IndexError("list assignment index
    # out of range")
    except IndexError:
        doc.append(nbranch)
Esempio n. 10
0
def resolve_path(doc, path, default=_scream, root=None):
    """
    Like :func:`resolve_jsonpointer` also for relative-paths & attribute-branches.

    :param doc:      the referrant document
    :param str path: An abdolute or relative path to resolve within document.
    :param default:  A value to return if path does not resolve.
    :param root:     Document for absolute paths, assumed `doc` if missing.
    :return:         the resolved doc-item or raises :class:`RefResolutionError`
    :raises:     RefResolutionError (if cannot resolve path and no `default`)

    Examples:

        >>> dt = {
        ...     'pi':3.14,
        ...     'foo':'bar',
        ...     'df': pd.DataFrame(np.ones((3,2)), columns=list('VN')),
        ...     'sub': {
        ...         'sr': pd.Series({'abc':'def'}),
        ...     }
        ... }
        >>> resolve_path(dt, '/pi', default=_scream)
        3.14

        >>> resolve_path(dt, 'df/V')
        0    1.0
        1    1.0
        2    1.0
        Name: V, dtype: float64

        >>> resolve_path(dt, '/pi/BAD', 'Hi!')
        'Hi!'

    :author: Julian Berman, ankostis
    """

    def resolve_root(d, p):
        if not p:
            return root or doc
        raise ValueError()

    part_resolvers = [
        lambda d, p: d[int(p)],
        lambda d, p: d[p],
        lambda d, p: getattr(d, p),
        resolve_root,
    ]
    for part in iter_jsonpointer_parts_relaxed(path):
        start_i = 0 if isinstance(doc, cabc.Sequence) else 1
        for resolver in part_resolvers[start_i:]:
            try:
                doc = resolver(doc, part)
                break
            except (ValueError, TypeError, LookupError, AttributeError):
                pass
        else:
            if default is _scream:
                raise RefResolutionError("Unresolvable path(%r)@(%s)" % (path, part))
            return default

    return doc
Esempio n. 11
0
 def resolve_remote(self, uri):
     if uri in self.store[""]:
         return self.store[""][uri]
     else:
         raise RefResolutionError("Unable to find type %s" % uri)
Esempio n. 12
0
    def validate_against(self, instance, schemauris=[], strict=False):
        """
        validate the instance against each of the schemas identified by the 
        list of schemauris.  For the instance to be considered valid, it 
        must validate against each of the named schemas.  $extensionSchema
        properties within the instance are ignored.  

        :argument instance:  a parsed JSON document to be validated.
        :argument list schemauris:  a list of URIs of the schemas to validate
                                    against.  

        :return list: a list of encountered errors in the form of exceptions; 
                      otherwise, an empty list if the instance is valid against
                      all schemas.
        """
        if isinstance(schemauris, (str, unicode)):
            schemauris = [schemauris]
        schema = None
        out = []
        for uri in schemauris:
            val = self._validators.get(uri)
            if not val:
                (urib, frag) = self._spliturifrag(uri)
                schema = self._schemaStore.get(urib)
                if not schema:
                    try:
                        schema = self._loader(urib)
                    except KeyError as e:
                        ex = MissingSchemaDocument(
                            "Unable to find schema document for " + urib)
                        if strict:
                            out.append(ex)
                        continue

                resolver = jsch.RefResolver(uri,
                                            schema,
                                            self._schemaStore,
                                            handlers=self._handler)

                if frag:
                    try:
                        schema = resolver.resolve_fragment(schema, frag)
                    except RefResolutionError as ex:
                        exc = RefResolutionError(
                            "Unable to resolve fragment, {0} from schema, {1} ({2})"
                            .format(frag, urib, str(ex)))
                        out.append(exc)
                        continue

                cls = jsch.validator_for(schema)

                # check the schema for errors
                scherrs = [ SchemaError.create_from(err) \
                            for err in cls(cls.META_SCHEMA).iter_errors(schema) ]
                if len(scherrs) > 0:
                    out.extend(scherrs)
                    continue

                val = cls(schema, resolver=resolver)

            out.extend([err for err in val.iter_errors(instance)])

            self._validators[uri] = val
            self._schemaStore.update(val.resolver.store)

        return out