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 resolve py-obj and py-pkgdata URLs.

The easiest way to use it is to initialize it using the from_schema method and pass it as a keyword argument to jsonschema.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 of jsonschema.RefResolver, but take note: It is also necessary to override the urljoin_cache argument to use jsonschema_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 to jsonschema.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 and py-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 customized jsonschema_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