0001"""
0002simple, elegant templating
0003(part of web.py)
0004"""
0005
0006import re, glob, os, os.path
0007from types import FunctionType as function
0008from utils import storage, group
0009from net import websafe
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025global_globals = {'None':None, 'False':False, 'True': True}
0026MAX_ITERS = 100000
0027
0028WHAT = 0
0029ARGS = 4
0030KWARGS = 6
0031NAME = 2
0032BODY = 4
0033CLAUSE = 2
0034ELIF = 6
0035ELSE = 8
0036IN = 6
0037NAME = 2
0038EXPR = 4
0039FILTER = 4
0040THING = 2
0041ATTR = 4
0042ITEM = 4
0043NEGATE = 4
0044X = 2
0045OP = 4
0046Y = 6
0047LINENO = -1
0048
0049
0050r_var = '[a-zA-Z_][a-zA-Z0-9_]*'
0051
0052class ParseError(Exception): pass
0053class Parser:
0054 def __init__(self, text):
0055 self.t = text
0056 self.p = 0
0057 self._lock = [False]
0058
0059 def lock(self):
0060 self._lock[-1] = True
0061
0062 def curline(self):
0063 return self.t[:self.p].count('
')+1
0064
0065 def csome(self):
0066 return repr(self.t[self.p:self.p+5]+'...')
0067
0068 def Error(self, x, y=None):
0069 if y is None: y = self.csome()
0070 raise ParseError, "expected %s, got %s (line %s)" % (x, y, self.curline())
0071
0072 def q(self, f):
0073 def internal(*a, **kw):
0074 checkp = self.p
0075 self._lock.append(False)
0076 try:
0077 q = f(*a, **kw)
0078 except ParseError:
0079 if self._lock[-1]:
0080 raise
0081 self.p = checkp
0082 self._lock.pop()
0083 return False
0084 self._lock.pop()
0085 return q or True
0086 return internal
0087
0088 def tokr(self, t):
0089 text = self.c(len(t))
0090 if text != t:
0091 self.Error(repr(t), repr(text))
0092 return t
0093
0094 def ltokr(self, *l):
0095 for x in l:
0096 o = self.tokq(x)
0097 if o: return o
0098 self.Error('one of '+repr(l))
0099
0100 def rer(self, r):
0101 x = re.match(r, self.t[self.p:])
0102 if not x:
0103 self.Error('r'+repr(r))
0104 return self.tokr(x.group())
0105
0106 def endr(self):
0107 if self.p != len(self.t):
0108 self.Error('EOF')
0109
0110 def c(self, n=1):
0111 out = self.t[self.p:self.p+n]
0112 if out == '' and n != 0:
0113 self.Error('character', 'EOF')
0114 self.p += n
0115 return out
0116
0117 def lookbehind(self, t):
0118 return self.t[self.p-len(t):self.p] == t
0119
0120 def __getattr__(self, a):
0121 if a.endswith('q'):
0122 return self.q(getattr(self, a[:-1]+'r'))
0123 raise AttributeError, a
0124
0125class TemplateParser(Parser):
0126 def __init__(self, *a, **kw):
0127 Parser.__init__(self, *a, **kw)
0128 self.curws = ''
0129 self.curind = ''
0130
0131 def o(self, *a):
0132 return a+('lineno', self.curline())
0133
0134 def go(self):
0135
0136 return self.gor()
0137
0138 def gor(self):
0139 header = self.defwithq()
0140 results = self.lines(start=True)
0141 self.endr()
0142 return header, results
0143
0144 def ws(self):
0145 n = 0
0146 while self.tokq(" "): n += 1
0147 return " " * n
0148
0149 def defwithr(self):
0150 self.tokr('$def with ')
0151 self.lock()
0152 self.tokr('(')
0153 args = []
0154 kw = []
0155 x = self.req(r_var)
0156 while x:
0157 if self.tokq('='):
0158 v = self.exprr()
0159 kw.append((x, v))
0160 else:
0161 args.append(x)
0162 x = self.tokq(', ') and self.req(r_var)
0163 self.tokr(')
')
0164 return self.o('defwith', 'null', None, 'args', args, 'kwargs', kw)
0165
0166 def literalr(self):
0167 o = (
0168 self.req('"[^"]*"') or
0169 self.req("'[^']*'")
0170 )
0171 if o is False:
0172 o = self.req('\-?[0-9]+(\.[0-9]*)?')
0173 if o is not False:
0174 if '.' in o: o = float(o)
0175 else: o = int(o)
0176
0177 if o is False: self.Error('literal')
0178 return self.o('literal', 'thing', o)
0179
0180 def listr(self):
0181 self.tokr('[')
0182 self.lock()
0183 x = []
0184 if not self.tokq(']'):
0185 while True:
0186 t = self.exprr()
0187 x.append(t)
0188 if not self.tokq(', '): break
0189 self.tokr(']')
0190 return self.o('list', 'thing', x)
0191
0192 def dictr(self):
0193 self.tokr('{')
0194 self.lock()
0195 x = {}
0196 if not self.tokq('}'):
0197 while True:
0198 k = self.exprr()
0199 self.tokr(': ')
0200 v = self.exprr()
0201 x[k] = v
0202 if not self.tokq(', '): break
0203 self.tokr('}')
0204 return self.o('dict', 'thing', x)
0205
0206 def parenr(self):
0207 self.tokr('(')
0208 self.lock()
0209 o = self.exprr()
0210 self.tokr(')')
0211 return self.o('paren', 'thing', o)
0212
0213 def atomr(self):
0214 """returns var, literal, paren, dict, or list"""
0215 o = (
0216 self.varq() or
0217 self.parenq() or
0218 self.dictq() or
0219 self.listq() or
0220 self.literalq()
0221 )
0222 if o is False: self.Error('atom')
0223 return o
0224
0225 def primaryr(self):
0226 """returns getattr, call, or getitem"""
0227 n = self.atomr()
0228 while 1:
0229 if self.tokq('.'):
0230 v = self.req(r_var)
0231 if not v:
0232 self.p -= 1
0233 break
0234 else:
0235 n = self.o('getattr', 'thing', n, 'attr', v)
0236 elif self.tokq('('):
0237 args = []
0238 kw = []
0239
0240 while 1:
0241
0242 checkp = self.p
0243 k = self.req(r_var)
0244 if k and self.tokq('='):
0245 v = self.exprr()
0246 kw.append((k, v))
0247 else:
0248 self.p = checkp
0249 x = self.exprq()
0250 if x:
0251 args.append(x)
0252 else:
0253 break
0254
0255 if not self.tokq(', '): break
0256 self.tokr(')')
0257 n = self.o('call', 'thing', n, 'args', args, 'kwargs', kw)
0258 elif self.tokq('['):
0259 v = self.exprr()
0260 self.tokr(']')
0261 n = self.o('getitem', 'thing', n, 'item', v)
0262 else:
0263 break
0264
0265 return n
0266
0267 def exprr(self):
0268 negate = self.tokq('not ')
0269 x = self.primaryr()
0270 if self.tokq(' '):
0271 operator = self.ltokr('not in', 'in', 'is not', 'is', '==', '!=', '>=', '<=', '<', '>', 'and', 'or', '*', '+', '-', '/', '%')
0272 self.tokr(' ')
0273 y = self.exprr()
0274 x = self.o('test', 'x', x, 'op', operator, 'y', y)
0275
0276 return self.o('expr', 'thing', x, 'negate', negate)
0277
0278 def varr(self):
0279 return self.o('var', 'name', self.rer(r_var))
0280
0281 def liner(self):
0282 out = []
0283 o = self.curws
0284 while 1:
0285 c = self.c()
0286 self.lock()
0287 if c == '
':
0288 self.p -= 1
0289 break
0290 if c == '$':
0291 if self.lookbehind('\$'):
0292 o = o[:-1] + c
0293 else:
0294 filter = not bool(self.tokq(':'))
0295
0296 if self.tokq('{'):
0297 out.append(o)
0298 out.append(self.o('itpl', 'name', self.exprr(), 'filter', filter))
0299 self.tokr('}')
0300 o = ''
0301 else:
0302 g = self.primaryq()
0303 if g:
0304 out.append(o)
0305 out.append(self.o('itpl', 'name', g, 'filter', filter))
0306 o = ''
0307 else:
0308 o += c
0309 else:
0310 o += c
0311 self.tokr('
')
0312 if not self.lookbehind('\
'):
0313 o += '
'
0314 else:
0315 o = o[:-1]
0316 out.append(o)
0317 return self.o('line', 'thing', out)
0318
0319 def varsetr(self):
0320 self.tokr('$var ')
0321 self.lock()
0322 what = self.rer(r_var)
0323 self.tokr(':')
0324 body = self.lines()
0325 return self.o('varset', 'name', what, 'body', body)
0326
0327 def ifr(self):
0328 self.tokr("$if ")
0329 self.lock()
0330 expr = self.exprr()
0331 self.tokr(":")
0332 ifc = self.lines()
0333
0334 elifs = []
0335 while self.tokq(self.curws + self.curind + '$elif '):
0336 v = self.exprr()
0337 self.tokr(':')
0338 c = self.lines()
0339 elifs.append(self.o('elif', 'clause', v, 'body', c))
0340
0341 if self.tokq(self.curws + self.curind + "$else:"):
0342 elsec = self.lines()
0343 else:
0344 elsec = None
0345
0346 return self.o('if', 'clause', expr, 'then', ifc, 'elif', elifs, 'else', elsec)
0347
0348 def forr(self):
0349 self.tokr("$for ")
0350 self.lock()
0351 v = self.setabler()
0352 self.tokr(" in ")
0353 g = self.exprr()
0354 self.tokr(":")
0355 l = self.lines()
0356
0357 if self.tokq(self.curws + self.curind + '$else:'):
0358 elsec = self.lines()
0359 else:
0360 elsec = None
0361
0362 return self.o('for', 'name', v, 'body', l, 'in', g, 'else', elsec)
0363
0364 def whiler(self):
0365 self.tokr('$while ')
0366 self.lock()
0367 v = self.exprr()
0368 self.tokr(":")
0369 l = self.lines()
0370
0371 if self.tokq(self.curws + self.curind + '$else:'):
0372 elsec = self.lines()
0373 else:
0374 elsec = None
0375
0376 return self.o('while', 'clause', v, 'body', l, 'null', None, 'else', elsec)
0377
0378 def assignr(self):
0379 self.tokr('$ ')
0380 assign = self.rer(r_var)
0381 self.tokr(' = ')
0382 expr = self.exprr()
0383 self.tokr('
')
0384
0385 return self.o('assign', 'name', assign, 'expr', expr)
0386
0387 def commentr(self):
0388 self.tokr('$#')
0389 self.lock()
0390 while self.c() != '
': pass
0391 return self.o('comment')
0392
0393 def setabler(self):
0394 out = [self.varr()]
0395 while self.tokq(', '):
0396 out.append(self.varr())
0397 return out
0398
0399 def lines(self, start=False):
0400 """
0401 This function gets called from two places:
0402 1. at the start, where it's matching the document itself
0403 2. after any command, where it matches one line or an indented block
0404 """
0405 o = []
0406 if not start:
0407 singleline = self.tokq(' ') and self.lineq()
0408 if singleline:
0409 return [singleline]
0410 else:
0411 self.rer(' *')
0412 self.tokr('
')
0413 oldind = self.curind
0414 self.curind += ' '
0415 while 1:
0416 oldws = self.curws
0417 t = self.tokq(oldws + self.curind)
0418 if not t: break
0419
0420 self.curws += self.ws()
0421 x = t and (
0422 self.varsetq() or
0423 self.ifq() or
0424 self.forq() or
0425 self.whileq() or
0426 self.assignq() or
0427 self.commentq() or
0428 self.lineq())
0429 self.curws = oldws
0430 if not x:
0431 break
0432 elif x[WHAT] == 'comment':
0433 pass
0434 else:
0435 o.append(x)
0436
0437 if not start: self.curind = oldind
0438 return o
0439
0440class Stowage(storage):
0441 def __str__(self): return self.get('_str')
0442
0443 def __add__(self, other):
0444 if isinstance(other, (unicode, str)):
0445 self._str += other
0446 return self
0447 else:
0448 raise TypeError, 'cannot add'
0449 def __radd__(self, other):
0450 if isinstance(other, (unicode, str)):
0451 self._str = other + self._str
0452 return self
0453 else:
0454 raise TypeError, 'cannot add'
0455
0456class WTF(AssertionError): pass
0457class SecurityError(Exception):
0458 """The template seems to be trying to do something naughty."""
0459 pass
0460
0461Required = object()
0462class Template:
0463 globals = {}
0464 def __init__(self, text, filter=None):
0465 self.filter = filter
0466
0467 text = text.replace('
', '
').replace('
', '
')
0468 if not text.endswith('
'): text += '
'
0469 header, tree = TemplateParser(text).go()
0470 self.tree = tree
0471 if header:
0472 self.h_defwith(header)
0473 else:
0474 self.args, self.kwargs = (), {}
0475
0476 def __call__(self, *a, **kw):
0477 d = self.globals.copy()
0478 d.update(self._parseargs(a, kw))
0479 f = Fill(self.tree, d=d)
0480 if self.filter: f.filter = self.filter
0481 return f.go()
0482
0483 def _parseargs(self, inargs, inkwargs):
0484
0485
0486 d = {}
0487 for arg in self.args:
0488 d[arg] = Required
0489 for kw, val in self.kwargs:
0490 d[kw] = val
0491
0492 for n, val in enumerate(inargs):
0493 if n < len(self.args):
0494 d[self.args[n]] = val
0495 elif n < len(self.args)+len(self.kwargs):
0496 kw = self.kwargs[n - len(self.args)][0]
0497 d[kw] = val
0498
0499 for kw, val in inkwargs.iteritems():
0500 d[kw] = val
0501
0502 unset = []
0503 for k, v in d.iteritems():
0504 if v is Required:
0505 unset.append(k)
0506 if unset:
0507 raise TypeError, 'values for %s are required' % unset
0508
0509 return d
0510
0511 def h_defwith(self, header):
0512 assert header[WHAT] == 'defwith'
0513 f = Fill(self.tree, d={})
0514
0515 self.args = header[ARGS]
0516 self.kwargs = []
0517 for var, valexpr in header[KWARGS]:
0518 self.kwargs.append((var, f.h(valexpr)))
0519
0520class Handle:
0521 def __init__(self, parsetree, **kw):
0522 self._funccache = {}
0523 self.parsetree = parsetree
0524 for (k, v) in kw.iteritems(): setattr(self, k, v)
0525
0526 def h(self, item):
0527 return getattr(self, 'h_' + item[WHAT])(item)
0528
0529class Fill(Handle):
0530 builtins = global_globals
0531 def filter(self, text):
0532 if text is None: return ''
0533 else: return str(text)
0534
0535
0536 def h_literal(self, i):
0537 item = i[THING]
0538 if isinstance(item, str) and item[0] in ['"', "'"]:
0539 item = item[1:-1]
0540 elif isinstance(item, (float, int)):
0541 pass
0542 return item
0543
0544 def h_list(self, i):
0545 x = i[THING]
0546 out = []
0547 for item in x:
0548 out.append(self.h(item))
0549 return out
0550
0551 def h_dict(self, i):
0552 x = i[THING]
0553 out = {}
0554 for k, v in x.iteritems():
0555 out[self.h(k)] = self.h(v)
0556 return out
0557
0558 def h_paren(self, i):
0559 item = i[THING]
0560 if isinstance(item, list):
0561 raise NotImplementedError, 'tuples'
0562 return self.h(item)
0563
0564 def h_getattr(self, i):
0565 thing, attr = i[THING], i[ATTR]
0566 thing = self.h(thing)
0567 if attr.startswith('_') or attr.startswith('func_') or attr.startswith('im_'):
0568 raise SecurityError, 'tried to get ' + attr
0569 try:
0570 if thing in self.builtins:
0571 raise SecurityError, 'tried to getattr on ' + repr(thing)
0572 except TypeError:
0573 pass
0574 try:
0575 return getattr(thing, attr)
0576 except AttributeError:
0577 if isinstance(thing, list) and attr == 'join':
0578 return lambda s: s.join(thing)
0579 else:
0580 raise
0581
0582 def h_call(self, i):
0583 call = self.h(i[THING])
0584 args = [self.h(x) for x in i[ARGS]]
0585 kw = dict([(x, self.h(y)) for (x, y) in i[KWARGS]])
0586 return call(*args, **kw)
0587
0588 def h_getitem(self, i):
0589 thing, item = i[THING], i[ITEM]
0590 thing = self.h(thing)
0591 item = self.h(item)
0592 return thing[item]
0593
0594 def h_expr(self, i):
0595 item = self.h(i[THING])
0596 if i[NEGATE]:
0597 item = not item
0598 return item
0599
0600 def h_test(self, item):
0601 ox, op, oy = item[X], item[OP], item[Y]
0602
0603 e = self.h
0604 if op == 'is':
0605 return e(ox) is e(oy)
0606 elif op == 'is not':
0607 return e(ox) is not e(oy)
0608 elif op == 'in':
0609 return e(ox) in e(oy)
0610 elif op == 'not in':
0611 return e(ox) not in e(oy)
0612 elif op == '==':
0613 return e(ox) == e(oy)
0614 elif op == '!=':
0615 return e(ox) != e(oy)
0616 elif op == '>':
0617 return e(ox) > e(oy)
0618 elif op == '<':
0619 return e(ox) < e(oy)
0620 elif op == '<=':
0621 return e(ox) <= e(oy)
0622 elif op == '>=':
0623 return e(ox) >= e(oy)
0624 elif op == 'and':
0625 return e(ox) and e(oy)
0626 elif op == 'or':
0627 return e(ox) or e(oy)
0628 elif op == '+':
0629 return e(ox) + e(oy)
0630 elif op == '-':
0631 return e(ox) - e(oy)
0632 elif op == '*':
0633 return e(ox) * e(oy)
0634 elif op == '/':
0635 return e(ox) / e(oy)
0636 elif op == '%':
0637 return e(ox) % e(oy)
0638 else:
0639 raise WTF, 'op ' + op
0640
0641 def h_var(self, i):
0642 v = i[NAME]
0643 if v in self.d:
0644 return self.d[v]
0645 elif v in self.builtins:
0646 return self.builtins[v]
0647 elif v == 'self':
0648 return self.output
0649 else:
0650 raise NameError, 'could not find %s (line %s)' % (repr(i[NAME]), i[LINENO])
0651
0652 def h_line(self, i):
0653 out = []
0654 for x in i[THING]:
0655 if isinstance(x, str):
0656 out.append(x)
0657 elif x[WHAT] == 'itpl':
0658 o = self.h(x[NAME])
0659 if x[FILTER]:
0660 o = self.filter(o)
0661 else:
0662 if isinstance(o, Stowage):
0663 o = o._str
0664 out.append(o)
0665 else:
0666 raise WTF, x
0667 return ''.join(out)
0668
0669 def h_varset(self, i):
0670 self.output[i[NAME]] = ''.join(self.h_lines(i[BODY]))
0671 return ''
0672
0673 def h_if(self, i):
0674 expr = self.h(i[CLAUSE])
0675 if expr:
0676 do = i[BODY]
0677 else:
0678 for e in i[ELIF]:
0679 expr = self.h(e[CLAUSE])
0680 if expr:
0681 do = e[BODY]
0682 break
0683 else:
0684 do = i[ELSE]
0685 return ''.join(self.h_lines(do))
0686
0687 def h_for(self, i):
0688 out = []
0689 assert i[IN][WHAT] == 'expr'
0690 invar = self.h(i[IN])
0691 forvar = i[NAME]
0692 if invar:
0693 for nv in invar:
0694 if len(forvar) == 1:
0695 fv = forvar[0]
0696 assert fv[WHAT] == 'var'
0697 self.d[fv[NAME]] = nv
0698 else:
0699 for x, y in zip(forvar, nv):
0700 assert x[WHAT] == 'var'
0701 self.d[x[NAME]] = y
0702
0703 out.extend(self.h_lines(i[BODY]))
0704 else:
0705 if i[ELSE]:
0706 out.extend(self.h_lines(i[ELSE]))
0707 return ''.join(out)
0708
0709 def h_while(self, i):
0710 out = []
0711 expr = self.h(i[CLAUSE])
0712 if not expr:
0713 return ''.join(self.h_lines(i[ELSE]))
0714 c = 0
0715 while expr:
0716 c += 1
0717 if c >= MAX_ITERS:
0718 raise RuntimeError, 'too many while-loop iterations (line %s)' % i[LINENO]
0719 out.extend(self.h_lines(i[BODY]))
0720 expr = self.h(i[CLAUSE])
0721 return ''.join(out)
0722
0723 def h_assign(self, i):
0724 self.d[i[NAME]] = self.h(i[EXPR])
0725 return ''
0726
0727 def h_comment(self, i): pass
0728
0729 def h_lines(self, lines):
0730 if lines is None: return []
0731 return map(self.h, lines)
0732
0733 def go(self):
0734 self.output = Stowage()
0735 self.output._str = ''.join(map(self.h, self.parsetree))
0736 if self.output.keys() == ['_str']:
0737 self.output = self.output['_str']
0738 return self.output
0739
0740class render:
0741 def __init__(self, loc='templates/', cache=True):
0742 self.loc = loc
0743 if cache:
0744 self.cache = {}
0745 else:
0746 self.cache = False
0747
0748 def _do(self, name, filter=None):
0749 if self.cache is False or name not in self.cache:
0750 p = glob.glob(self.loc + name + '.*')
0751 if not p and os.path.isdir(self.loc + name):
0752 return render(self.loc + name + '/', cache=self.cache)
0753 elif not p:
0754 raise AttributeError, 'no template named ' + name
0755 p = p[0]
0756 c = Template(open(p).read())
0757 if self.cache is not False: self.cache[name] = (p, c)
0758
0759 if self.cache is not False: p, c = self.cache[name]
0760
0761 if p.endswith('.html'):
0762 import webapi as web
0763 if 'headers' in web.ctx:
0764 web.header('Content-Type', 'text/html; charset=utf-8', unique=True)
0765 if not filter: c.filter = websafe
0766 elif p.endswith('.xml'):
0767 if not filter: c.filter = websafe
0768
0769 return c
0770
0771 def __getattr__(self, p):
0772 return self._do(p)
0773
0774def frender(fn, *a, **kw):
0775 return Template(open(fn).read(), *a, **kw)
0776
0777def test():
0778 import sys
0779 verbose = '-v' in sys.argv
0780 def assertEqual(a, b):
0781 if a == b:
0782 if verbose:
0783 sys.stderr.write('.')
0784 sys.stderr.flush()
0785 else:
0786 assert a == b, "
expected: %s
got: %s" % (repr(a), repr(b))
0787
0788 from utils import storage, group
0789 t = Template
0790
0791 tests = [
0792 lambda: t('1')(), '1
',
0793 lambda: t('$def with ()
1')(), '1
',
0794 lambda: t('$def with (a)
$a')(1), '1
',
0795 lambda: t('$def with (a=0)
$a')(1), '1
',
0796 lambda: t('$def with (a=0)
$a')(a=1), '1
',
0797 lambda: t('$if 1: 1')(), '1
',
0798 lambda: t('$if 1:
1')(), '1
',
0799 lambda: t('$if 0: 0
$elif 1: 1')(), '1
',
0800 lambda: t('$if 0: 0
$elif None: 0
$else: 1')(), '1
',
0801 lambda: t('$if (0 < 1) and (1 < 2): 1')(), '1
',
0802 lambda: t('$for x in [1, 2, 3]: $x')(), '1
2
3
',
0803 lambda: t('$for x in []: 0
$else: 1')(), '1
',
0804 lambda: t('$def with (a)
$while a and a.pop(): 1')([1, 2, 3]), '1
1
1
',
0805 lambda: t('$while 0: 0
$else: 1')(), '1
',
0806 lambda: t('$ a = 1
$a')(), '1
',
0807 lambda: t('$# 0')(), '',
0808 lambda: t('$def with (d)
$for k, v in d.iteritems(): $k')({1: 1}), '1
',
0809 lambda: t('$def with (a)
$(a)')(1), '1
',
0810 lambda: t('$def with (a)
$a')(1), '1
',
0811 lambda: t('$def with (a)
$a.b')(storage(b=1)), '1
',
0812 lambda: t('$def with (a)
$a[0]')([1]), '1
',
0813 lambda: t('${0 or 1}')(), '1
',
0814 lambda: t('$ a = [1]
$a[0]')(), '1
',
0815 lambda: t('$ a = {1: 1}
$a.keys()[0]')(), '1
',
0816 lambda: t('$ a = []
$if not a: 1')(), '1
',
0817 lambda: t('$ a = {}
$if not a: 1')(), '1
',
0818 lambda: t('$ a = -1
$a')(), '-1
',
0819 lambda: t('$ a = "1"
$a')(), '1
',
0820 lambda: t('$if 1 is 1: 1')(), '1
',
0821 lambda: t('$if not 0: 1')(), '1
',
0822 lambda: t('$if 1:
$if 1: 1')(), '1
',
0823 lambda: t('$ a = 1
$a')(), '1
',
0824 lambda: t('$ a = 1.
$a')(), '1.0
',
0825 lambda: t('$({1: 1}.keys()[0])')(), '1
',
0826 ]
0827
0828 for func, value in group(tests, 2):
0829 assertEqual(func(), value)
0830
0831 j = Template("$var foo: bar")()
0832 assertEqual(str(j), '')
0833 assertEqual(j.foo, 'bar
')
0834 if verbose: sys.stderr.write('
')
0835
0836
0837if __name__ == "__main__":
0838 test()