Skip to content

Commit

Permalink
#71 handle schema defined in Python object (#75)
Browse files Browse the repository at this point in the history
* added ability to import schema from python module/package

* updated docs and an error. This branch is to support #71

* fixed py dict from import is converted to string due to later processing of the schema, some misc warnings in docs

* clarafied a point about python objects in the docs

Authored-by: Glen.Nicholls <Glen.Nicholls@kratosdefense.com>
  • Loading branch information
GlenNicholls committed Apr 27, 2022
1 parent 5727d04 commit e6177b0
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 8 deletions.
7 changes: 6 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,20 @@ Display a schema located in a file with an absolute path::

.. jsonschema:: /home/leo/src/jsonschema/sample.json

Or a path relative to the referencing document::
A path relative to the referencing document::

.. jsonschema:: jsonschema/sample.json

Or a schema defined in a Python object::

.. jsonschema:: mod.pkg.SCHEMA

With all three of the above you may add JSON Pointer notation to display a subschema::

.. jsonschema:: http://some.domain/with/a/path/spec.json#/path/to/schema
.. jsonschema:: /home/leo/src/jsonschema/sample.json#/path/to/schema
.. jsonschema:: jsonschema/sample.json#/path/to/schema
.. jsonschema:: mod.pkg.SCHEMA#/path/to/schema

Alternatively you can embed the schema::

Expand Down
36 changes: 35 additions & 1 deletion docs/directive.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,38 @@ which renders:
}
}

Lastly, you can use the ``jsonschema`` directive to render a schema from a Python
object:

.. code-block:: python
:caption: ``example.py``
SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "An example",
"id": "http://example.com/schemas/example.json",
"description": "This is just a tiny example of a schema rendered by `sphinx-jsonschema <http://github.com/lnoor/sphinx-jsonschema>`_.\n\nYes that's right you can use *reStructuredText* in a description.",
"type": "string",
"minLength": 10,
"maxLength": 100,
"pattern": "^[A-Z]+$"
}
with the following usage of the directive:

.. code-block:: rst
.. jsonschema:: sphinx-jsonschema.example.SCHEMA
which should render as:

.. jsonschema:: sphinx-jsonschema.example.SCHEMA

.. important::
For rendering Python objects with the ``jsonschema`` directive, the object does not
*need* to be a dict or a string. However, the object must have a ``__str__`` method
defined that will return a valid schema.

Options
-------

Expand Down Expand Up @@ -370,7 +402,9 @@ The ``conf.py`` option **jsonschema_options** lets you do so.
It takes a dict as value the boolean valued keys of which have the same name as the options.

So, in ``conf.py`` you can state:
.. code-block:: py

.. code-block:: python
:caption: ``conf.py``
jsonschema_options = {
'lift_description': True,
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

setup(
name='sphinx-jsonschema',
version='1.18.1', # don't forget: must match __init__.py::setup() return value
version='1.19.0', # don't forget: must match __init__.py::setup() return value

description='Sphinx extension to display JSON Schema',
long_description=long_description,
Expand Down
47 changes: 42 additions & 5 deletions sphinx-jsonschema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
"""

import csv
import os.path
import importlib
import json
import os
import yaml

from jsonpointer import resolve_pointer
from traceback import format_exception, format_exception_only
import yaml
from collections import OrderedDict

from docutils import nodes, utils
Expand Down Expand Up @@ -213,7 +215,7 @@ def run(self):
def get_json_data(self):
"""
Get JSON data from the directive content, from an external
file, or from a URL reference.
file, from a URL reference, or from importing a schema defined in Python.
"""
if self.arguments:
filename, pointer = self._splitpointer(self.arguments[0])
Expand All @@ -224,9 +226,14 @@ def get_json_data(self):
if self.content:
schema, source = self.from_content(filename)
elif filename and filename.startswith('http'):
# Appears to be URL so process it as such
schema, source = self.from_url(filename)
elif filename:
elif os.path.exists(filename):
# File exists so it must be a JSON schema
schema, source = self.from_file(filename)
elif filename:
# Must be a Python reference to a schema
schema, source = self.from_data(filename)
else:
raise self.error('"%s" directive has no content or a reference to an external file.'
% self.name)
Expand Down Expand Up @@ -315,6 +322,36 @@ def from_file(self, filename):

return data, source

def from_data(self, filename):
"""Get schema from Python data/object."""
document_source = os.path.dirname(self.state.document.current_source)

parts = filename.split('.')
if len(parts) > 1:
module_name = '.'.join(parts[:-1])
obj_name = parts[-1]
else:
raise self.error(
f"{self.name} directive requires a Python reference to a schema object"
f" like 'mod.pkg.data'. '{filename}' is not valid."
)

try:
mod = importlib.import_module(module_name)
data = str(getattr(mod, obj_name))
except (ImportError, ModuleNotFoundError) as error:
raise self.error(
f"{self.name} directive encountered an error while importing python"
f" module '{module_name}': \n{error}"
)

# Simplifing source path and to the document a new dependency
source = mod.__file__
source = utils.relative_path(document_source, source)
self.state.document.settings.record_dependencies.add(source)

return data, source

def _splitpointer(self, path):
val = path.rsplit('#', 1)
if len(val) == 1:
Expand Down Expand Up @@ -352,5 +389,5 @@ def setup(app):
app.add_config_value('jsonschema_options', {}, 'env')
return {
'parallel_read_safe': True,
'version': '1.18.1'
'version': '1.19.0'
}
10 changes: 10 additions & 0 deletions sphinx-jsonschema/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "An example",
"id": "http://example.com/schemas/example.json",
"description": "This is just a tiny example of a schema rendered by `sphinx-jsonschema <http://github.com/lnoor/sphinx-jsonschema>`_.\n\nYes that's right you can use *reStructuredText* in a description.",
"type": "string",
"minLength": 10,
"maxLength": 100,
"pattern": "^[A-Z]+$"
}

0 comments on commit e6177b0

Please sign in to comment.