1 # This file is licensed under the GNU Lesser General Public License v2.1.
2 # originally written for Mojo Nation by Bram Cohen, based on an earlier
3 # version by Bryce Wilcox
4 # The authors disclaim all liability for any damages resulting from
5 # any use of this software.
9 def string_template(thing, verbose):
10 if type(thing) != types.StringType:
11 raise ValueError, "not a string"
16 def func(s, verbose, l = l):
17 if type(s) != types.StringType:
18 raise ValueError, 'should have been string'
20 raise ValueError, 'wrong length, should have been ' + str(l) + ' was ' + str(len(s))
24 def __init__(self, max_depth, template = None):
26 self.max_depth = max_depth
27 self.template = template
29 def get_real_template(self):
30 assert self.template is not None, 'You forgot to set the template!'
31 if self.max_depth == 0:
35 return compile_inner(self.template)
40 if hasattr(self, 'p'):
44 return 'MaxDepth(' + str(self.max_depth) + ', ' + `self.template` + ')'
48 def fail_too_deep(thing, verbose):
49 raise ValueError, 'recursed too deep'
52 def __init__(self, template):
53 self.template = template
55 def get_real_template(self):
56 return compile_list_template(self.template)
59 return 'ListMarker(' + `self.template` + ')'
61 def compile_list_template(template):
62 def func(thing, verbose, template = compile_inner(template)):
63 if type(thing) not in (types.ListType, types.TupleType):
64 raise ValueError, 'not a list'
67 for i in xrange(0, len(thing)):
70 reason = 'mismatch at index ' + str(i) + ': ' + str(e)
71 raise ValueError, reason
78 def __init__(self, template, t2 = string_template):
79 self.template = template
82 def get_real_template(self):
83 return compile_values_template(self.template, self.t2)
86 return 'ValuesMarker(' + `self.template` + ')'
88 def compile_values_template(template, t2):
89 def func(thing, verbose, template = compile_inner(template),
90 t2 = compile_inner(t2)):
91 if type(thing) != types.DictType:
92 raise ValueError, 'not a dict'
95 for key, val in thing.items():
99 raise ValueError, 'mismatch in key ' + `key` + ': ' + str(e)
101 for key, val in thing.items():
108 def compile_string_template(template):
109 assert type(template) is types.StringType
110 def func(thing, verbose, template = template):
111 if thing != template:
112 raise ValueError, "didn't match string"
115 compilers[types.StringType] = compile_string_template
117 def int_template(thing, verbose):
118 if type(thing) not in (types.IntType, types.LongType):
119 raise ValueError, 'thing not of integer type'
121 def nonnegative_int_template(thing, verbose):
122 if type(thing) not in (types.IntType, types.LongType):
123 raise ValueError, 'thing not of integer type'
125 raise ValueError, 'thing less than zero'
127 def positive_int_template(thing, verbose):
128 if type(thing) not in (types.IntType, types.LongType):
129 raise ValueError, 'thing not of integer type'
131 raise ValueError, 'thing less than or equal to zero'
133 def compile_int_template(s):
134 assert s in (-1, 0, 1)
138 return nonnegative_int_template
140 return positive_int_template
142 compilers[types.IntType] = compile_int_template
143 compilers[types.LongType] = compile_int_template
145 def compile_slice(template):
146 assert type(template) is types.SliceType
147 assert template.step is None
148 assert template.stop is not None
149 start = template.start
152 def func(thing, verbose, start = start, stop = template.stop):
153 if type(thing) not in (types.IntType, types.LongType):
154 raise ValueError, 'not an int'
156 raise ValueError, 'thing too small'
158 raise ValueError, 'thing too large'
161 compilers[types.SliceType] = compile_slice
164 def __init__(self, template):
165 self.option_template = template
168 return 'OptionMarker(' + `self.option_template` + ')'
170 def compile_dict_template(template):
171 assert type(template) is types.DictType
176 for key, value in template.items():
177 if hasattr(value, 'option_template'):
178 optiongroup.append((key, compile_inner(value.option_template)))
179 elif type(value) is types.StringType:
180 agroup.append((key, compile_inner(value)))
181 elif type(value) in (types.IntType, types.LongType, types.SliceType):
182 bgroup.append((key, compile_inner(value)))
184 cgroup.append((key, compile_inner(value)))
185 def func(thing, verbose, required = agroup + bgroup + cgroup, optional = optiongroup):
186 if type(thing) is not types.DictType:
187 raise ValueError, 'not a dict'
189 for key, template in required:
190 if not thing.has_key(key):
191 raise ValueError, 'key not present'
192 template(thing[key], verbose)
193 for key, template in optional:
194 if thing.has_key(key):
195 template(thing[key], verbose)
196 except ValueError, e:
198 reason = 'mismatch in key ' + `key` + ': ' + str(e)
199 raise ValueError, reason
204 compilers[types.DictType] = compile_dict_template
206 def none_template(thing, verbose):
207 if thing is not None:
208 raise ValueError, 'thing was not None'
210 compilers[types.NoneType] = lambda template: none_template
212 def compile_or_template(template):
213 assert type(template) in (types.ListType, types.TupleType)
214 def func(thing, verbose, templ = [compile_inner(x) for x in template]):
216 failure_reason = ('did not match any of the ' +
217 str(len(templ)) + ' possible templates;')
218 for i in xrange(len(templ)):
222 except ValueError, reason:
223 failure_reason += (' failed template at index ' +
224 str(i) + ' because (' + str(reason) + ')')
225 raise ValueError, failure_reason
233 raise ValueError, "did not match any possible templates"
236 compilers[types.ListType] = compile_or_template
237 compilers[types.TupleType] = compile_or_template
239 def compile_inner(template):
240 while hasattr(template, 'get_real_template'):
241 template = template.get_real_template()
242 if callable(template):
244 return compilers[type(template)](template)
246 def compile_template(template):
247 def func(thing, verbose = None, t = compile_inner(template), s = `template`):
248 if verbose is not None:
257 except ValueError, reason:
258 raise ValueError, 'failed template check because: (' + str(reason) + ') target was: (' + `thing` + ') template was: (' + s + ')'
266 class TestBTemplate(unittest.TestCase):
268 def test_slice(self):
269 f = compile_template(slice(4))
288 f = compile_template(slice(-2, 3))
308 f = compile_template(0)
322 f = compile_template(-1)
332 f = compile_template(1)
351 f = compile_template(None)
359 def test_string(self):
360 f = compile_template('a')
373 def test_generic_string(self):
374 f = compile_template(st)
382 def test_values(self):
383 vt = compile_template(ValuesMarker('a', exact_length(1)))
403 f = compile_template(ListMarker('a'))
423 f = compile_template(['a', 'b'])
432 f = compile_template(('a', 'b'))
442 f = compile_template({'a': 'b', 'c': OptionMarker('d')})
459 f({'a': 'b', 'c': 'd'})
461 f({'a': 'e', 'c': 'd'})
471 f({'a': 'b', 'c': 'f'})
476 f({'a': 'e', 'c': 'f'})
486 def test_other_func(self):
487 def check3(thing, verbose):
490 f = compile_template(check3)
498 def test_max_depth(self):
500 t = {'a': OptionMarker(ListMarker(md))}
502 f = compile_template(md)
503 f({'a': [{'a': []}]})
504 f({'a': [{'a': []}]})
506 f({'a': [{'a': [{}]}]})
511 f({'a': [{'a': [{}]}]})
515 f({'a': [{'a': []}]})
517 f({'a': [{'a': [{}]}]})
522 def test_use_compiled(self):
523 x = compile_template('a')
524 y = compile_template(ListMarker(x))
527 if __name__ == "__main__":