1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
from datetime import datetime
from docutils import nodes
import errno
import os
try:
import urllib2 as urllib
except ImportError:
import urllib.request as urllib
from lxml import html
VALIDATION_SPEC = "http://json-schema.org/draft-04/json-schema-validation.html"
def setup(app):
"""
Install the plugin.
Arguments:
app (sphinx.application.Sphinx):
the Sphinx application context
"""
app.add_config_value("cache_path", "_cache", "")
try:
os.makedirs(app.config.cache_path)
except OSError as error:
if error.errno != errno.EEXIST:
raise
path = os.path.join(app.config.cache_path, "spec.html")
spec = fetch_or_load(path)
app.add_role("validator", docutils_sucks(spec))
def fetch_or_load(spec_path):
"""
Fetch a new specification or use the cache if it's current.
Arguments:
cache_path:
the path to a cached specification
"""
headers = {}
try:
modified = datetime.utcfromtimestamp(os.path.getmtime(spec_path))
date = modified.strftime("%a, %d %b %Y %I:%M:%S UTC")
headers["If-Modified-Since"] = date
except OSError as error:
if error.errno != errno.ENOENT:
raise
request = urllib.Request(VALIDATION_SPEC, headers=headers)
response = urllib.urlopen(request)
if response.code == 200:
with open(spec_path, "w+b") as spec:
spec.writelines(response)
spec.seek(0)
return html.parse(spec)
with open(spec_path) as spec:
return html.parse(spec)
def docutils_sucks(spec):
"""
Yeah.
It doesn't allow using a class because it does stupid stuff like try to set
attributes on the callable object rather than just keeping a dict.
"""
base_url = VALIDATION_SPEC
ref_url = "http://json-schema.org/draft-04/json-schema-core.html#rfc.section.4.1"
schema_url = (
"http://json-schema.org/draft-04/json-schema-core.html#rfc.section.6"
)
def validator(name, raw_text, text, lineno, inliner):
"""
Link to the JSON Schema documentation for a validator.
Arguments:
name (str):
the name of the role in the document
raw_source (str):
the raw text (role with argument)
text (str):
the argument given to the role
lineno (int):
the line number
inliner (docutils.parsers.rst.states.Inliner):
the inliner
Returns:
tuple:
a 2-tuple of nodes to insert into the document and an
iterable of system messages, both possibly empty
"""
if text == "$ref":
return [nodes.reference(raw_text, text, refuri=ref_url)], []
elif text == "$schema":
return [nodes.reference(raw_text, text, refuri=schema_url)], []
# find the header in the validation spec containing matching text
header = spec.xpath("//h1[contains(text(), '{0}')]".format(text))
if len(header) == 0:
inliner.reporter.warning(
"Didn't find a target for {0}".format(text),
)
uri = base_url
else:
if len(header) > 1:
inliner.reporter.info(
"Found multiple targets for {0}".format(text),
)
# get the href from link in the header
uri = base_url + header[0].find('a').attrib["href"]
reference = nodes.reference(raw_text, text, refuri=uri)
return [reference], []
return validator
|