]> git.mxchange.org Git - quix0rs-apt-p2p.git/blob - btemplate.py
Initial revision
[quix0rs-apt-p2p.git] / btemplate.py
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.
6
7 import types
8
9 def string_template(thing, verbose):
10     if type(thing) != types.StringType:
11         raise ValueError, "not a string"
12
13 st = string_template
14
15 def exact_length(l):
16     def func(s, verbose, l = l):
17         if type(s) != types.StringType:
18             raise ValueError, 'should have been string'
19         if len(s) != l:
20             raise ValueError, 'wrong length, should have been ' + str(l) + ' was ' + str(len(s))
21     return func
22
23 class MaxDepth:
24     def __init__(self, max_depth, template = None):
25         assert max_depth >= 0
26         self.max_depth = max_depth
27         self.template = template
28
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:
32             return fail_too_deep
33         self.max_depth -= 1
34         try:
35             return compile_inner(self.template)
36         finally:
37             self.max_depth += 1
38
39     def __repr__(self):
40         if hasattr(self, 'p'):
41             return '...'
42         try:
43             self.p = 1
44             return 'MaxDepth(' + str(self.max_depth) + ', ' + `self.template` + ')'
45         finally:
46             del self.p
47
48 def fail_too_deep(thing, verbose):
49     raise ValueError, 'recursed too deep'
50
51 class ListMarker:
52     def __init__(self, template):
53         self.template = template
54
55     def get_real_template(self):
56         return compile_list_template(self.template)
57
58     def __repr__(self):
59         return 'ListMarker(' + `self.template` + ')'
60
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'
65         if verbose:
66             try:
67                 for i in xrange(0, len(thing)):
68                     template(thing[i], 1)
69             except ValueError, e:
70                 reason = 'mismatch at index ' + str(i) + ': ' + str(e)
71                 raise ValueError, reason
72         else:
73             for i in thing:
74                 template(i, 0)
75     return func
76
77 class ValuesMarker:
78     def __init__(self, template, t2 = string_template):
79         self.template = template
80         self.t2 = t2
81         
82     def get_real_template(self):
83         return compile_values_template(self.template, self.t2)
84
85     def __repr__(self):
86         return 'ValuesMarker(' + `self.template` + ')'
87
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'
93         if verbose:
94             try:
95                 for key, val in thing.items():
96                     template(val, 1)
97                     t2(key, 1)
98             except ValueError, e:
99                 raise ValueError, 'mismatch in key ' + `key` + ': ' + str(e)
100         else:
101             for key, val in thing.items():
102                 template(val, 0)
103                 t2(key, 0)
104     return func
105
106 compilers = {}
107
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"
113     return func
114
115 compilers[types.StringType] = compile_string_template
116
117 def int_template(thing, verbose):
118     if type(thing) not in (types.IntType, types.LongType):
119         raise ValueError, 'thing not of integer type'
120
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'
124     if thing < 0:
125         raise ValueError, 'thing less than zero'
126
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'
130     if thing <= 0:
131         raise ValueError, 'thing less than or equal to zero'
132
133 def compile_int_template(s):
134     assert s in (-1, 0, 1)
135     if s == -1:
136         return int_template
137     elif s == 0:
138         return nonnegative_int_template
139     else:
140         return positive_int_template
141
142 compilers[types.IntType] = compile_int_template
143 compilers[types.LongType] = compile_int_template
144
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
150     if start is None:
151         start = 0
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'
155         if thing < start:
156             raise ValueError, 'thing too small'
157         if thing >= stop:
158             raise ValueError, 'thing too large'
159     return func
160
161 compilers[types.SliceType] = compile_slice
162
163 class OptionMarker:
164     def __init__(self, template):
165         self.option_template = template
166
167     def __repr__(self):
168         return 'OptionMarker(' + `self.option_template` + ')'
169
170 def compile_dict_template(template):
171     assert type(template) is types.DictType
172     agroup = []
173     bgroup = []
174     cgroup = []
175     optiongroup = []
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)))
183         else:
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'
188         try:
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:
197             if verbose:
198                 reason = 'mismatch in key ' + `key` + ': ' + str(e)
199                 raise ValueError, reason
200             else:
201                 raise
202     return func
203
204 compilers[types.DictType] = compile_dict_template
205
206 def none_template(thing, verbose):
207     if thing is not None:
208         raise ValueError, 'thing was not None'
209
210 compilers[types.NoneType] = lambda template: none_template
211
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]):
215         if verbose:
216             failure_reason = ('did not match any of the ' + 
217                 str(len(templ)) + ' possible templates;')
218             for i in xrange(len(templ)):
219                 try:
220                     templ[i](thing, 1)
221                     return
222                 except ValueError, reason:
223                     failure_reason += (' failed template at index ' + 
224                         str(i) + ' because (' + str(reason) + ')')
225             raise ValueError, failure_reason
226         else:
227             for i in templ:
228                 try:
229                     i(thing, 0)
230                     return
231                 except ValueError:
232                     pass
233             raise ValueError, "did not match any possible templates"
234     return func
235
236 compilers[types.ListType] = compile_or_template
237 compilers[types.TupleType] = compile_or_template
238
239 def compile_inner(template):
240     while hasattr(template, 'get_real_template'):
241         template = template.get_real_template()
242     if callable(template):
243         return template
244     return compilers[type(template)](template)
245
246 def compile_template(template):
247     def func(thing, verbose = None, t = compile_inner(template), s = `template`):
248         if verbose is not None:
249             t(thing, verbose)
250             return
251         try:
252             t(thing, 0)
253         except ValueError:
254             try:
255                 t(thing, 1)
256                 assert 0
257             except ValueError, reason:
258                 raise ValueError, 'failed template check because: (' + str(reason) + ') target was: (' + `thing` + ') template was: (' + s + ')'
259     return func
260
261
262
263 ######
264 import unittest
265
266 class TestBTemplate(unittest.TestCase):
267     
268     def test_slice(self):
269         f = compile_template(slice(4))
270         f(0)
271         f(3L)
272         try:
273             f(-1)
274             assert 0
275         except ValueError:
276             pass
277         try:
278             f(4L)
279             assert 0
280         except ValueError:
281             pass
282         try:
283             f('a')
284             assert 0
285         except ValueError:
286             pass
287     
288         f = compile_template(slice(-2, 3))
289         f(-2L)
290         f(2)
291         try:
292             f(-3L)
293             assert 0
294         except ValueError:
295             pass
296         try:
297             f(3)
298             assert 0
299         except ValueError:
300             pass
301         try:
302             f('a')
303             assert 0
304         except ValueError:
305             pass
306     
307     def test_int(self):
308         f = compile_template(0)
309         f(0)
310         f(1L)
311         try:
312             f(-1)
313             assert 0
314         except ValueError:
315             pass
316         try:
317             f('a')
318             assert 0
319         except ValueError:
320             pass
321     
322         f = compile_template(-1)
323         f(0)
324         f(1)
325         f(-1L)
326         try:
327             f('a')
328             assert 0
329         except ValueError:
330             pass
331     
332         f = compile_template(1)
333         try:
334             f(0)
335             assert 0
336         except ValueError:
337             pass
338         f(1)
339         try:
340             f(-1)
341             assert 0
342         except ValueError:
343             pass
344         try:
345             f('a')
346             assert 0
347         except ValueError:
348             pass
349     
350     def test_none(self):
351         f = compile_template(None)
352         f(None)
353         try:
354             f(0)
355             assert 0
356         except ValueError:
357             pass
358     
359     def test_string(self):
360         f = compile_template('a')
361         f('a')
362         try:
363             f('b')
364             assert 0
365         except ValueError:
366             pass
367         try:
368             f(0)
369             assert 0
370         except ValueError:
371             pass
372     
373     def test_generic_string(self):
374         f = compile_template(st)
375         f('a')
376         try:
377             f(0)
378             assert 0
379         except ValueError:
380             pass
381     
382     def test_values(self):
383         vt = compile_template(ValuesMarker('a', exact_length(1)))
384         vt({})
385         vt({'x': 'a'})
386         try:
387             vt(3)
388             assert 0
389         except ValueError:
390             pass
391         try:
392             vt({'x': 'b'})
393             assert 0
394         except ValueError:
395             pass
396         try:
397             vt({'xx': 'a'})
398             assert 0
399         except ValueError:
400             pass
401     
402     def test_list(self):
403         f = compile_template(ListMarker('a'))
404         f(['a'])
405         f(('a', 'a'))
406         try:
407             f(('a', 'b'))
408             assert 0
409         except ValueError:
410             pass
411         try:
412             f(('b', 'a'))
413             assert 0
414         except ValueError:
415             pass
416         try:
417             f('a')
418             assert 0
419         except ValueError:
420             pass
421     
422     def test_or(self):
423         f = compile_template(['a', 'b'])
424         f('a')
425         f('b')
426         try:
427             f('c')
428             assert 0
429         except ValueError:
430             pass
431     
432         f = compile_template(('a', 'b'))
433         f('a')
434         f('b')
435         try:
436             f('c')
437             assert 0
438         except ValueError:
439             pass
440     
441     def test_dict(self):
442         f = compile_template({'a': 'b', 'c': OptionMarker('d')})
443         try:
444             f({})
445             assert 0
446         except ValueError:
447             pass
448         f({'a': 'b'})
449         try:
450             f({'a': 'e'})
451             assert 0
452         except ValueError:
453             pass
454         try:
455             f({'c': 'd'})
456             assert 0
457         except ValueError:
458             pass
459         f({'a': 'b', 'c': 'd'})
460         try:
461             f({'a': 'e', 'c': 'd'})
462             assert 0
463         except ValueError:
464             pass
465         try:
466             f({'c': 'f'})
467             assert 0
468         except ValueError:
469             pass
470         try:
471             f({'a': 'b', 'c': 'f'})
472             assert 0
473         except ValueError:
474             pass
475         try:
476             f({'a': 'e', 'c': 'f'})
477             assert 0
478         except ValueError:
479             pass
480         try:
481             f(None)
482             assert 0
483         except ValueError:
484             pass
485     
486     def test_other_func(self):
487         def check3(thing, verbose):
488             if thing != 3:
489                 raise ValueError
490         f = compile_template(check3)
491         f(3)
492         try:
493             f(4)
494             assert 0
495         except ValueError:
496             pass
497     
498     def test_max_depth(self):
499         md = MaxDepth(2)
500         t = {'a': OptionMarker(ListMarker(md))}
501         md.template = t
502         f = compile_template(md)
503         f({'a': [{'a': []}]})
504         f({'a': [{'a': []}]})
505         try:
506             f({'a': [{'a': [{}]}]})
507             assert 0
508         except ValueError:
509             pass
510         try:
511             f({'a': [{'a': [{}]}]})
512             assert 0
513         except ValueError:
514             pass
515         f({'a': [{'a': []}]})
516         try:
517             f({'a': [{'a': [{}]}]})
518             assert 0
519         except ValueError:
520             pass
521     
522     def test_use_compiled(self):
523         x = compile_template('a')
524         y = compile_template(ListMarker(x))
525         y(['a'])
526         
527 if __name__ == "__main__":
528     unittest.main()