API Documentation
- class jsonschema_pyref.RefResolver(base_uri: str, referrer: Optional[Union[dict, list, str, int, float, bool]], store: Union[dict, tuple] = (), cache_remote: bool = True, handlers: Union[dict, tuple] = (), urljoin_cache: Optional[Callable[[str, str], str]] = None, remote_cache: Optional[Callable[[str], dict]] = None)[source]
Bases:
jsonschema.validators.RefResolver
Drop-in replacement for
jsonschema.RefResolver
that can resolvepy-obj
andpy-pkgdata
URLs.The easiest way to use it is to initialize it using the
from_schema
method and pass it as a keyword argument tojsonschema.validate
:>>> import jsonschema >>> from jsonschema_pyref import RefResolver >>> schema = { ... 'properties': { ... 'a': {'$ref': 'py-obj:jsonschema_pyref.examples.schema1'} ... } ... } ... >>> resolver = RefResolver.from_schema(schema) >>> jsonschema.validate({'a': 'hello'}, schema, resolver=resolver) is None True
- jsonschema_pyref.URL_SCHEME_RESOLVERS = {'py-obj': <function _resolve_url_py_obj>, 'py-pkgdata': <function _resolve_url_py_pkgdata>}
Additional URL handlers for the custom URL schemes supported by this package.
This can be passed to the
handlers
argument ofjsonschema.RefResolver
, but take note: It is also necessary to override theurljoin_cache
argument to usejsonschema_pyref.urljoin
as follows:>>> import jonschema >>> from jsonschema_pyref import URL_SCHEME_RESOLVERS, urljoin >>> from functools import lru_cache >>> resolver = RefResolver({}, {}, handlers=URL_SCHEME_RESOLVERS, ... urljoin_cache=lru_cache(1024)(urljoin))
This boilerplate can be avoided entirely by using the supplied
jsonschema_pyref.RefResolver
which has these enhancements by default (and is otherwise identical tojsonschema.RefResolver
).
- exception jsonschema_pyref.ValidationError(message, validator=<unset>, path=(), cause=None, context=(), validator_value=<unset>, instance=<unset>, schema=<unset>, schema_path=(), parent=None)[source]
Bases:
jsonschema.exceptions._Error
An instance was invalid under a provided schema.
- jsonschema_pyref.urljoin(base: str, url: str, allow_fragments: bool = True) str [source]
Extends the stdlib’s
urllib.parse.urljoin
to support the custom URL schemes implemented by this package.The join semantics for
py-obj
andpy-pkgdata
URLs differ from those of typical http-like URLs, as well as from each other.The rules for
py-obj
are as follows:If joined with a “relative” URL, if the URL starts with a Python identifier, it is simply appended to the current object path:
>>> from jsonschema_pyref import urljoin >>> urljoin('py-obj:a.b.c', 'd.e') 'py-obj:a.b.c.d.e'
However, if the “relative” URL starts with one or more
.
, it is processed relative to the base path using similar semantics to Python relative imports. That is, one dot means a different attribute of the same object, and so on:>>> urljoin('py-obj:a.b.c', '.d') 'py-obj:a.b.d' >>> urljoin('py-obj:a.b.c', '..d') 'py-obj:a.d' >>> urljoin('py-obj:a.b.c', '..d.e') 'py-obj:a.d.e'
When joining an absolute URL with the
py-obj
scheme, the new URL replaces the base:>>> urljoin('py-obj:a.b.c', 'py-obj:d.e.f') 'py-obj:d.e.f'
And likewise when joining an absolute URL with an entirely different scheme:
>>> urljoin('py-obj:a.b.c', 'https://example.com') 'https://example.com'
Fragments are dropped from the base URL, and retained from the joined URL:
>>> urljoin('py-obj:a.b.c#replaced', '.d#fragment') 'py-obj:a.b.d#fragment' >>> urljoin('py-obj:a.b.c#replaced', '#fragment') 'py-obj:a.b.c#fragment'
The rules for
py-pkgdata
are as follows:If joined to a relative URL, it is treated as a different file relative to the same package/directory as the base URL, very similarly to how
urllib.parse.urljoin
works for relative paths joined to http(s) URLs:>>> urljoin('py-pkgdata:a.b.c/schema1.json', 'schema2.json') 'py-pkgdata:a.b.c/schema2.json' >>> urljoin('py-pkgdata:a.b.c/schemas/schema1.json', 'schema2.json') 'py-pkgdata:a.b.c/schemas/schema2.json' >>> urljoin('py-pkgdata:a.b.c/schemas/sub/schema1.json', 'schema2.json') 'py-pkgdata:a.b.c/schemas/sub/schema2.json' >>> urljoin('py-pkgdata:a.b.c/schemas/schema1.json', '../more_schemas/schema2.json') 'py-pkgdata:a.b.c/more_schemas/schema2.json'
Note
This does not support relative URLs with a package-relative component, only different paths within the same package. Maybe support for this could be added in the future, but under the current format it’s too ambiguous.
When joining an absolute URL, if the joined URL is in the same package it works the same as the relative case (effectively the joined URL replaces the base):
>>> urljoin('py-pkgdata:a.b.c/schema1.json', 'py-pkgdata:a.b.c/schema2.json') 'py-pkgdata:a.b.c/schema2.json'
Likewise when they are different packages:
>>> urljoin('py-pkgdata:a.b.c/schema1.json', 'py-pkgdata:d.e.f/schema2.json') 'py-pkgdata:d.e.f/schema2.json'
Or if they are the exact same URL, that URL is returned:
>>> urljoin('py-pkgdata:a.b.c/sub/schema1.json', 'py-pkgdata:a.b.c/sub/schema1.json') 'py-pkgdata:a.b.c/sub/schema1.json'
And likewise if they are not the same scheme at all:
>>> urljoin('py-pkgdata:a.b.c/schema1.json', 'http://example.com/schema2.json') 'http://example.com/schema2.json'
The same rules apply for fragments as with
py-obj
:>>> urljoin('py-pkgdata:a.b.c/schema1.json#replaced', 'schema2.json#fragment') 'py-pkgdata:a.b.c/schema2.json#fragment' >>> urljoin('py-pkgdata:a.b.c/schema1.json#replaced', '#fragment') 'py-pkgdata:a.b.c/schema1.json#fragment'
- jsonschema_pyref.urlparse(url: str) urllib.parse.ParseResult [source]
Extends the stdlib’s
urllib.parse.urlparse
to support the custom URL schemes implemented by this package.
- jsonschema_pyref.validate(document: Optional[Union[dict, list, str, int, float, bool]], schema: Union[dict, bool], cls: Optional[Type] = None, *args: Any, **kwargs: Any)[source]
Drop-in replacement for
jsonschema.validate
which uses the customizedjsonschema_pyref.RefResolver
by default.>>> from jsonschema_pyref import validate >>> schema = { ... 'properties': { ... 'a': {'$ref': 'py-obj:jsonschema_pyref.examples.schema1'} ... } ... } ... >>> validate({'a': 'hello'}, schema) is None True