Skip to main content

Supports referencing schemas from Python packages

Project description

jsonschema-pyref

This package adds support to jsonschema's RefResolver for resolving schema $refs with two new custom URL schemes:

  • py-obj: URLs with this scheme allow loading a schema from a dict in a Python module/class/etc. This uses dotted attribute notation for the path to the schema. For example py-obj:package.submodule.Class.schema resolves to a schema class attribute in the class Class in the module package.submodule.

  • py-pkgdata: URLs with this scheme allow loading a schema from a file (currently only JSON and YAML are supported) in the package data installed with a Python package. For example py-pkgdata:package.subpackage/schema.json loads the schema from the file schema.json under package/subpackage from wherever the package is installed.

Many JSON Schemas have an $id property declaring the unique identity of the schema. Typically this is an http(s) URL, but there is nothing requiring it to be. Often this is just used for namespacing, and the supplied URL does technically require to exist.

In practice, if your Python package ships with a number of schemas used by the package, and you would like to reference those schemas either from another package, or relative to other schemas in the same package, the use of the http scheme for schema $ids doesn't help with this. It is necessary to provide some kind of mapping between the URLs in the schema $ids, and their actual location in the Python package.

The use of these custom URL schemes makes it implicit that this schema's home is in a Python package or module (in the case of schemas declared directly in Python code) and that it is not located on a network resource.

This gives an alternative way to declare the $ids of and $ref schemas installed alongside Python code.

Examples

>>> import jsonschema
>>> from jsonschema_pyref import RefResolver
>>> schema = {
...     'properties': {
...         'a': {'$ref': 'py-obj:jsonschema_pyref.examples.schema1'},
...         'b': {'$ref': 'py-pkgdata:jsonschema_pyref.examples/schema1.json'},
...         'c': {'$ref': 'py-pkgdata:jsonschema_pyref.examples/schema2.yml'}
...      }
... }
>>> resolver = RefResolver.from_schema(schema)
>>> doc = {'a': 'hello', 'b': 'world', 'c': 123}
>>> jsonschema.validate(doc, schema, resolver=resolver) is None
True

As a convenience, jsonschema_pyref.validate is also provided as a drop-in replacement for jsonschema.validate which uses the custom RefResolver by default:

>>> from jsonschema_pyref import validate
>>> validate(doc, schema) is None
True

Let's have a look at what's in the referenced schemas to see exactly what we're validating the document against:

>>> from pkgutil import get_data
>>> import jsonschema_pyref.examples
>>> jsonschema_pyref.examples.schema1
{'type': 'string'}
>>> def show_file(filename):
...     data = get_data('jsonschema_pyref.examples', filename)
...     print(data.decode('ascii').strip())
...
>>> show_file('schema1.json')
{"type": "string"}
>>> show_file('schema2.yml')
type: integer

These example schemas may be trivial, but you can easily confirm that this would work all the same with a more complex schema.

Just to prove that the correct schemas are actually being loaded (and the document is not just being trivially validated) we can also try some counter-examples:

>>> doc1 = dict(doc, a=123)
>>> jsonschema.validate(doc1, schema, resolver=resolver)
Traceback (most recent call last):
...
jsonschema.exceptions.ValidationError: 123 is not of type 'string'
...
Failed validating 'type' in schema['properties']['a']:
    {'type': 'string'}
...
On instance['a']:
    123
>>> doc2 = dict(doc, b=123)
>>> jsonschema.validate(doc2, schema, resolver=resolver)
Traceback (most recent call last):
...
jsonschema.exceptions.ValidationError: 123 is not of type 'string'
...
Failed validating 'type' in schema['properties']['b']:
    {'type': 'string'}
...
On instance['b']:
    123
>>> doc3 = dict(doc, c='hello')
>>> jsonschema.validate(doc3, schema, resolver=resolver)
Traceback (most recent call last):
...
jsonschema.exceptions.ValidationError: 'hello' is not of type 'integer'
...
Failed validating 'type' in schema['properties']['c']:
    {'type': 'integer'}
...
On instance['c']:
    'hello'

Here is a slightly more complex example demonstrating how relative refs work with py-pkgdata URLs, as well as that fragments are correctly resolved relative to each schema.

First we have schema4.yml which also contains an $id property, allowing jsonschema.RefResolver to correctly determine the base URL against which to resolve relative refs (in the case of {"$ref": "schema3.json"}):

>>> show_file('schema4.yml')
$id: "py-pkgdata:jsonschema_pyref.examples/schema4.yml"
type: "object"
properties:
    foo: {"$ref": "schema3.json"}
    bar: {"$ref": "#/$defs/bar"}
additionalProperties: false
$defs:
    bar: {"type": "integer"}

And schema3.json:

>>> show_file('schema3.json')
{
    "$id": "py-pkgdata:jsonschema_pyref.examples/schema3.json",
    "type": "object",
    "properties": {
        "foo": {"$ref": "#/$defs/foo"}
    },
    "additionalProperties": false,
    "$defs": {
        "foo": {"type": "string"}
    }
}

Here is a document that should validate against schema4 which we'll load also by $ref:

>>> doc = {
...     'foo': {'foo': 'hello'},
...     'bar': 123
... }
...
>>> schema = {'$ref': 'py-pkgdata:jsonschema_pyref.examples/schema4.yml'}
>>> resolver = RefResolver.from_schema(schema)
>>> jsonschema.validate(doc, schema, resolver=resolver) is None
True

And again a couple counter-examples:

>>> doc1 = dict(doc, foo={'foo': 123})
>>> jsonschema.validate(doc1, schema, resolver=resolver)
Traceback (most recent call last):
...
jsonschema.exceptions.ValidationError: 123 is not of type 'string'
...
Failed validating 'type' in schema['properties']['foo']['properties']['foo']:
    {'type': 'string'}
...
On instance['foo']['foo']:
    123
>>> doc2 = dict(doc, bar='bar')
>>> jsonschema.validate(doc2, schema, resolver=resolver)
Traceback (most recent call last):
...
jsonschema.exceptions.ValidationError: 'bar' is not of type 'integer'
...
Failed validating 'type' in schema['properties']['bar']:
    {'type': 'integer'}
...
On instance['bar']:
    'bar'

API Documentation

https://jsonschema-pyref.readthedocs.io/en/latest/api.html

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jsonschema-pyref-0.1.0.tar.gz (15.4 kB view details)

Uploaded Source

Built Distribution

jsonschema_pyref-0.1.0-py3-none-any.whl (10.7 kB view details)

Uploaded Python 3

File details

Details for the file jsonschema-pyref-0.1.0.tar.gz.

File metadata

  • Download URL: jsonschema-pyref-0.1.0.tar.gz
  • Upload date:
  • Size: 15.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.3.0 pkginfo/1.7.0 requests/2.25.1 setuptools/54.1.2 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.7.5

File hashes

Hashes for jsonschema-pyref-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5b0364623ff602adae3734596e4ea2364ed741ae82ad54123898acca9386a956
MD5 b779deebcc8385f3c4055ed13e782897
BLAKE2b-256 dc39a6b1f81ede7a2496479c3fb09c3063ea825631e64deb67da472995635657

See more details on using hashes here.

File details

Details for the file jsonschema_pyref-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: jsonschema_pyref-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.3.0 pkginfo/1.7.0 requests/2.25.1 setuptools/54.1.2 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.7.5

File hashes

Hashes for jsonschema_pyref-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1793e3f2ffc0d3acead078cc03715771f2f5b67142f2a2af726898ee349a2109
MD5 1c20169b38ff0bdd53e41881202be76d
BLAKE2b-256 01eff2a7dd0504df81a621374ce4a0bd2610d3a96b9b64034b15978c6fb4b046

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page