Skip to content

Commit 8a9a873

Browse files
cwacekChris Wacek
authored and
Chris Wacek
committed
First commit
0 parents  commit 8a9a873

15 files changed

+900
-0
lines changed

MANIFEST.in

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include requirements.txt
2+
include README.md
3+

development.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
nose==1.3.0
2+
rednose==0.4.1

python_jsonschema_objects/__init__.py

Whitespace-only changes.
+285
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
import util
2+
import validators
3+
4+
5+
class ProtocolBase(object):
6+
__propinfo__ = {}
7+
__required__ = set()
8+
9+
__SCHEMA_TYPES__ = {
10+
'array': list,
11+
'boolean': bool,
12+
'integer': int,
13+
'number': float,
14+
'null': None,
15+
'string': unicode,
16+
'object': dict
17+
}
18+
19+
def __str__(self):
20+
return repr(self)
21+
22+
def __repr__(self):
23+
props = ["%s=%s" % (k, str(v)) for k, v in
24+
self._properties.iteritems()]
25+
return "<%s %s>" % (
26+
self.__class__.__name__,
27+
" ".join(props)
28+
)
29+
30+
def __init__(this, **props):
31+
for prop in props:
32+
try:
33+
propname = this.__prop_names__[prop]
34+
except KeyError:
35+
raise AttributeError(
36+
"{0} is not valid property "
37+
"of '{1}'".format(prop,
38+
this.__class__))
39+
40+
setattr(this, propname, props[prop])
41+
42+
@classmethod
43+
def propinfo(cls, propname):
44+
if propname not in cls.__propinfo__:
45+
return {}
46+
return cls.__propinfo__[propname]
47+
48+
def serialize(self):
49+
enc = util.ProtocolJSONEncoder()
50+
return enc.encode(self)
51+
52+
def validate(this):
53+
missing = [x for x in this.__required__
54+
if this._properties[x] is None]
55+
56+
if len(missing) > 0:
57+
raise validators.ValidationError(
58+
"'{0}' are required attributes for {1}"
59+
.format(missing, this.__class__))
60+
61+
for prop, val in this._properties.iteritems():
62+
info = this.propinfo(prop)
63+
64+
for param, paramval in info.iteritems():
65+
validator = getattr(validators, param, None)
66+
if validator is not None:
67+
validator(paramval, val)
68+
69+
return True
70+
71+
def validate_property(self, propinfo, propval):
72+
"""Validate a property value, and return true or false
73+
74+
:propinfo: A dictionary containing property info
75+
:propval: The property value
76+
:returns: True or False
77+
"""
78+
79+
80+
class ClassBuilder(object):
81+
82+
def __init__(self, resolver):
83+
self.resolver = resolver
84+
self.resolved = {}
85+
86+
def resolve_classes(self, iterable):
87+
pp = []
88+
for elem in iterable:
89+
if '$ref' in elem:
90+
ref = elem['$ref']
91+
uri = util.resolve_ref_uri(self.resolver.resolution_scope, ref)
92+
if uri in self.resolved:
93+
pp.append(self.resolved[uri])
94+
else:
95+
with self.resolver.resolving(ref) as resolved:
96+
self.resolved[uri] = self.construct(
97+
uri,
98+
resolved,
99+
(ProtocolBase,))
100+
pp.append(self.resolved[uri])
101+
else:
102+
pp.append(elem)
103+
104+
return pp
105+
106+
def construct(self, uri, clsdata, parent=(ProtocolBase,)):
107+
if 'oneOf' in clsdata:
108+
potential_parents = self.resolve_classes(clsdata['oneOf'])
109+
110+
for p in potential_parents:
111+
if issubclass(p, ProtocolBase):
112+
self.resolved[uri] = self._build_object(
113+
uri,
114+
clsdata,
115+
(p,))
116+
else:
117+
raise Exception("Don't know how to deal with this")
118+
119+
elif 'anyOf' in clsdata:
120+
raise NotImplementedError(
121+
"anyOf is not supported as bare property")
122+
123+
elif 'allOf' in clsdata:
124+
potential_parents = self.resolve_classes(clsdata['allOf'])
125+
parents = []
126+
for p in potential_parents:
127+
if isinstance(p, dict):
128+
# This is additional constraints
129+
clsdata.update(p)
130+
elif issubclass(p, ProtocolBase):
131+
parents.append(p)
132+
133+
self.resolved[uri] = self._build_object(
134+
uri,
135+
clsdata,
136+
parents)
137+
return self.resolved[uri]
138+
139+
elif '$ref' in clsdata:
140+
141+
if uri in self.resolved:
142+
return self.resolved[uri]
143+
else:
144+
reffed_doc = self.resolver.resolve_remote(uri)
145+
self.resolved[uri] = self._build_object(
146+
uri,
147+
reffed_doc,
148+
parent)
149+
return self.resolved[uri]
150+
151+
elif (clsdata.get('type', None) == 'object' or
152+
clsdata.get('properties', None) is not None):
153+
self.resolved[uri] = self._build_object(
154+
uri,
155+
clsdata,
156+
parent)
157+
return self.resolved[uri]
158+
else:
159+
raise NotImplementedError(
160+
"Unable to parse schema object with "
161+
"no type and no reference")
162+
163+
def _build_object(self, nm, clsdata, parents):
164+
165+
props = {}
166+
167+
properties = {}
168+
for p in parents:
169+
properties = util.propmerge(properties, p.__propinfo__)
170+
171+
if 'properties' in clsdata:
172+
properties = util.propmerge(properties, clsdata['properties'])
173+
174+
name_translation = {}
175+
176+
for prop, detail in properties.items():
177+
properties[prop]['raw_name'] = prop
178+
name_translation[prop] = prop.replace('@', '')
179+
prop = name_translation[prop]
180+
181+
if 'type' not in detail and '$ref' in detail:
182+
ref = detail['$ref']
183+
uri = util.resolve_ref_uri(self.resolver.resolution_scope, ref)
184+
if uri in self.resolved:
185+
props[prop] = make_property(prop,
186+
{'type': self.resolved[uri]},
187+
self.resolved[uri].__doc__)
188+
properties[prop]['$ref'] = uri
189+
properties[prop]['type'] = self.resolved[uri]
190+
else:
191+
with self.resolver.resolving(ref) as resolved:
192+
self.resolved[uri] = self.construct(
193+
uri,
194+
resolved,
195+
(ProtocolBase,))
196+
elif 'oneOf' in detail:
197+
potential = self.resolve_classes(detail['oneOf'])
198+
desc = detail[
199+
'description'] if 'description' in detail else ""
200+
props[prop] = make_property(prop,
201+
{'type': potential}, desc
202+
)
203+
204+
else:
205+
desc = detail[
206+
'description'] if 'description' in detail else ""
207+
208+
props[prop] = make_property(prop, detail, desc)
209+
210+
props['_properties'] = dict(zip(props.keys(),
211+
[None for x in
212+
xrange(len(props.keys()))]))
213+
214+
""" If this object itself has a 'oneOf' designation, then
215+
make the validation 'type' the list of potential objects.
216+
"""
217+
if 'oneOf' in clsdata:
218+
klasses = self.resolve_classes(clsdata['oneOf'])
219+
# Need a validation to check that it meets one of them
220+
props['__validation__'] = {'type': klasses}
221+
222+
props['__prop_names__'] = name_translation
223+
224+
props['__propinfo__'] = properties
225+
required = set.union(*[p.__required__ for p in parents])
226+
227+
if 'required' in clsdata:
228+
for prop in clsdata['required']:
229+
required.add(prop.replace('@', ''))
230+
231+
props['__required__'] = required
232+
233+
cls = type(str(nm.split('/')[-1]), tuple(parents), props)
234+
235+
return cls
236+
237+
238+
def make_property(prop, info, desc=""):
239+
240+
def getprop(this):
241+
try:
242+
return this._properties[prop]
243+
except KeyError:
244+
raise AttributeError("No such attribute")
245+
246+
def setprop(this, val):
247+
if isinstance(info['type'], (list, tuple)):
248+
ok = False
249+
for typ in info['type']:
250+
if isinstance(val, typ):
251+
ok = True
252+
break
253+
elif not isinstance(val, ProtocolBase):
254+
try:
255+
val = typ(**val)
256+
except:
257+
pass
258+
else:
259+
val.validate()
260+
ok = True
261+
break
262+
263+
if not ok:
264+
raise TypeError(
265+
"Value must be one of {0}".format(info['type']))
266+
267+
if info['type'] in this.__SCHEMA_TYPES__.keys() and val
268+
is not None:
269+
val = this.__SCHEMA_TYPES__[info['type']](val)
270+
271+
elif issubclass(info['type'], ProtocolBase):
272+
if not isinstance(val, info['type']):
273+
val = info['type'](**val)
274+
275+
val.validate()
276+
277+
this._properties[prop] = val
278+
279+
def delprop(this):
280+
if prop in this.__required__:
281+
raise AttributeError("'%s' is required" % prop)
282+
else:
283+
del this._properties[prop]
284+
285+
return property(getprop, setprop, delprop, desc)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import markdown
2+
from markdown.extensions import Extension
3+
from markdown.preprocessors import Preprocessor
4+
import re
5+
import json
6+
7+
8+
def extract_code_blocks(filename):
9+
with open(filename) as fin:
10+
doc = fin.read().split("\n")
11+
12+
M = markdown.Markdown(extensions=[SpecialFencedCodeExtension()])
13+
14+
for prep in M.preprocessors.values():
15+
doc = prep.run(doc)
16+
17+
root = M.parser.parseDocument(doc).getroot()
18+
19+
for treeproc in M.treeprocessors.values():
20+
newRoot = treeproc.run(root)
21+
if newRoot is not None:
22+
root = newRoot
23+
24+
return SpecialFencePreprocessor.EXAMPLES
25+
26+
class SpecialFencedCodeExtension(Extension):
27+
28+
def extendMarkdown(self, md, md_globals):
29+
""" Add FencedBlockPreprocessor to the Markdown instance. """
30+
md.registerExtension(self)
31+
32+
md.preprocessors.add('fenced_code_block',
33+
SpecialFencePreprocessor(md),
34+
">normalize_whitespace")
35+
36+
37+
class SpecialFencePreprocessor(Preprocessor):
38+
EXAMPLES = {}
39+
FENCED_BLOCK_RE = re.compile(r'''
40+
(?P<fence>^(?:~{3,}|`{3,}))[ ]* # Opening ``` or ~~~
41+
(\{?\.?(?P<lang>[a-zA-Z0-9_+-]*))?[ ]* # Optional {, and lang
42+
# Optional highlight lines, single- or double-quote-delimited
43+
(hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?[ ]*
44+
}?[ ]*\n # Optional closing }
45+
(?P<code>.*?)(?<=\n)
46+
(?P=fence)[ ]*$''', re.MULTILINE | re.DOTALL | re.VERBOSE)
47+
48+
def __init__(self, md):
49+
super(SpecialFencePreprocessor, self).__init__(md)
50+
51+
self.checked_for_codehilite = False
52+
self.codehilite_conf = {}
53+
54+
def run(self, lines):
55+
56+
text = "\n".join(lines)
57+
58+
while True:
59+
m = self.FENCED_BLOCK_RE.search(text)
60+
if m:
61+
if m.group('lang'):
62+
lang = m.group('lang')
63+
example = json.loads(m.group('code'))
64+
try:
65+
self.EXAMPLES[lang].append(example)
66+
except KeyError:
67+
self.EXAMPLES[lang] = [example]
68+
69+
text = "%s\n%s"% (text[:m.start()], text[m.end():])
70+
else:
71+
break
72+
return text.split("\n")
73+
74+
75+
76+
if __name__ == '__main__':
77+
print(extract_code_blocks())

0 commit comments

Comments
 (0)