Skip to content

Commit 8c7425a

Browse files
committed
Add a JSON Schema documentation role.
0 parents  commit 8c7425a

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed

sphinx_json_schema_spec/__init__.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
from datetime import datetime
2+
from docutils import nodes
3+
import errno
4+
import os
5+
import urllib2
6+
7+
from lxml import html
8+
9+
10+
VALIDATION_SPEC = "http://json-schema.org/latest/json-schema-validation.html"
11+
12+
13+
def setup(app):
14+
"""
15+
Install the plugin.
16+
17+
:argument sphinx.application.Sphinx app: the Sphinx application context
18+
19+
"""
20+
21+
app.add_config_value("cache_path", "_cache", "")
22+
23+
try:
24+
os.makedirs(app.config.cache_path)
25+
except OSError as error:
26+
if error.errno != errno.EEXIST:
27+
raise
28+
29+
path = os.path.join(app.config.cache_path, "spec.html")
30+
spec = fetch_or_load(path)
31+
app.add_role("validator", docutils_sucks(spec))
32+
33+
34+
def fetch_or_load(spec_path):
35+
"""
36+
Fetch a new specification or use the cache if it's current.
37+
38+
:argument cache_path: the path to a cached specification
39+
40+
"""
41+
42+
headers = {}
43+
44+
try:
45+
modified = datetime.utcfromtimestamp(os.path.getmtime(spec_path))
46+
date = modified.strftime("%a, %d %b %Y %I:%M:%S UTC")
47+
headers["If-Modified-Since"] = date
48+
except OSError as error:
49+
if error.errno != errno.ENOENT:
50+
raise
51+
52+
request = urllib2.Request(VALIDATION_SPEC, headers=headers)
53+
response = urllib2.urlopen(request)
54+
55+
if response.code == 200:
56+
with open(spec_path, "w+") as spec:
57+
spec.writelines(response)
58+
spec.seek(0)
59+
return html.parse(spec)
60+
61+
with open(spec_path) as spec:
62+
return html.parse(spec)
63+
64+
65+
def docutils_sucks(spec):
66+
"""
67+
Yeah.
68+
69+
It doesn't allow using a class because it does stupid stuff like try to set
70+
attributes on the callable object rather than just keeping a dict.
71+
72+
"""
73+
74+
base_url = VALIDATION_SPEC
75+
76+
def validator(name, raw_text, text, lineno, inliner):
77+
"""
78+
Link to the JSON Schema documentation for a validator.
79+
80+
:argument str name: the name of the role in the document
81+
:argument str raw_source: the raw text (role with argument)
82+
:argument str text: the argument given to the role
83+
:argument int lineno: the line number
84+
:argument docutils.parsers.rst.states.Inliner inliner: the inliner
85+
86+
:returns: 2-tuple of nodes to insert into the document and an iterable
87+
of system messages, both possibly empty
88+
89+
"""
90+
91+
header = spec.xpath(
92+
"//h3[re:match(text(), '(^|\W){0}($|\W,)', 'i')]".format(text),
93+
namespaces={"re": "http://exslt.org/regular-expressions"},
94+
)
95+
96+
if len(header) == 0:
97+
inliner.reporter.warning(
98+
"Didn't find a target for {0}".format(text),
99+
)
100+
uri = base_url
101+
else:
102+
if len(header) > 1:
103+
inliner.reporter.warning(
104+
"Found multiple targets for {0}".format(text),
105+
)
106+
uri = base_url + "#" + header[0].getprevious().attrib["name"]
107+
108+
reference = nodes.reference(raw_text, text, refuri=uri)
109+
return [reference], []
110+
111+
return validator

0 commit comments

Comments
 (0)