📄 template.py
字号:
class Stowage(storage): def __str__(self): return self.get('_str') #@@ edits in place def __add__(self, other): if isinstance(other, (unicode, str)): self._str += other return self else: raise TypeError, 'cannot add' def __radd__(self, other): if isinstance(other, (unicode, str)): self._str = other + self._str return self else: raise TypeError, 'cannot add' class WTF(AssertionError): passclass SecurityError(Exception): """The template seems to be trying to do something naughty.""" passRequired = object()class Template: globals = {} content_types = { '.html' : 'text/html; charset=utf-8', '.txt' : 'text/plain', } def __init__(self, text, filter=None, filename=""): self.filter = filter self.filename = filename # universal newlines: text = text.replace('\r\n', '\n').replace('\r', '\n').expandtabs() if not text.endswith('\n'): text += '\n' header, tree = TemplateParser(text, filename).go() self.tree = tree if header: self.h_defwith(header) else: self.args, self.kwargs = (), {} def __call__(self, *a, **kw): d = self.globals.copy() d.update(self._parseargs(a, kw)) f = Fill(self.tree, d=d) if self.filter: f.filter = self.filter import webapi as web if 'headers' in web.ctx and self.filename: content_type = self.find_content_type() if content_type: web.header('Content-Type', content_type, unique=True) return f.go() def find_content_type(self): for ext, content_type in self.content_types.iteritems(): if self.filename.endswith(ext): return content_type def _parseargs(self, inargs, inkwargs): # difference from Python: # no error on setting a keyword arg twice d = {} for arg in self.args: d[arg] = Required for kw, val in self.kwargs: d[kw] = val for n, val in enumerate(inargs): if n < len(self.args): d[self.args[n]] = val elif n < len(self.args)+len(self.kwargs): kw = self.kwargs[n - len(self.args)][0] d[kw] = val for kw, val in inkwargs.iteritems(): d[kw] = val unset = [] for k, v in d.iteritems(): if v is Required: unset.append(k) if unset: raise TypeError, 'values for %s are required' % unset return d def h_defwith(self, header): assert header[WHAT] == 'defwith' f = Fill(self.tree, d={}) self.args = header[ARGS] self.kwargs = [] for var, valexpr in header[KWARGS]: self.kwargs.append((var, f.h(valexpr))) def __repr__(self): return "<Template: %s>" % self.filenameclass Handle: def __init__(self, parsetree, **kw): self._funccache = {} self.parsetree = parsetree for (k, v) in kw.iteritems(): setattr(self, k, v) def h(self, item): return getattr(self, 'h_' + item[WHAT])(item) class Fill(Handle): builtins = global_globals def filter(self, text): if text is None: return '' else: return utf8(text) # often replaced with stuff like net.websafe def h_literal(self, i): item = i[THING] if isinstance(item, (unicode, str)) and item[0] in ['"', "'"]: item = item[1:-1] elif isinstance(item, (float, int)): pass return item def h_list(self, i): x = i[THING] out = [] for item in x: out.append(self.h(item)) return out def h_dict(self, i): x = i[THING] out = {} for k, v in x.iteritems(): out[self.h(k)] = self.h(v) return out def h_paren(self, i): item = i[THING] if isinstance(item, list): raise NotImplementedError, 'tuples' return self.h(item) def h_getattr(self, i): thing, attr = i[THING], i[ATTR] thing = self.h(thing) if attr.startswith('_') or attr.startswith('func_') or attr.startswith('im_'): raise SecurityError, 'tried to get ' + attr try: if thing in self.builtins: raise SecurityError, 'tried to getattr on ' + repr(thing) except TypeError: pass # raised when testing an unhashable object try: return getattr(thing, attr) except AttributeError: if isinstance(thing, list) and attr == 'join': return lambda s: s.join(thing) else: raise def h_call(self, i): call = self.h(i[THING]) args = [self.h(x) for x in i[ARGS]] kw = dict([(x, self.h(y)) for (x, y) in i[KWARGS]]) return call(*args, **kw) def h_getitem(self, i): thing, item = i[THING], i[ITEM] thing = self.h(thing) item = self.h(item) return thing[item] def h_expr(self, i): item = self.h(i[THING]) if i[NEGATE]: item = not item return item def h_test(self, item): ox, op, oy = item[X], item[OP], item[Y] # for short-circuiting to work, we can't eval these here e = self.h if op == 'is': return e(ox) is e(oy) elif op == 'is not': return e(ox) is not e(oy) elif op == 'in': return e(ox) in e(oy) elif op == 'not in': return e(ox) not in e(oy) elif op == '==': return e(ox) == e(oy) elif op == '!=': return e(ox) != e(oy) elif op == '>': return e(ox) > e(oy) elif op == '<': return e(ox) < e(oy) elif op == '<=': return e(ox) <= e(oy) elif op == '>=': return e(ox) >= e(oy) elif op == 'and': return e(ox) and e(oy) elif op == 'or': return e(ox) or e(oy) elif op == '+': return e(ox) + e(oy) elif op == '-': return e(ox) - e(oy) elif op == '*': return e(ox) * e(oy) elif op == '/': return e(ox) / e(oy) elif op == '%': return e(ox) % e(oy) else: raise WTF, 'op ' + op def h_var(self, i): v = i[NAME] if v in self.d: return self.d[v] elif v in self.builtins: return self.builtins[v] elif v == 'self': return self.output else: raise NameError, 'could not find %s (line %s)' % (repr(i[NAME]), i[LINENO]) def h_line(self, i): out = [] for x in i[THING]: #@@ what if x is unicode if isinstance(x, str): out.append(x) elif x[WHAT] == 'itpl': o = self.h(x[NAME]) if x[FILTER]: o = self.filter(o) else: o = (o is not None and utf8(o)) or "" out.append(o) else: raise WTF, x return ''.join(out) def h_varset(self, i): self.output[i[NAME]] = ''.join(self.h_lines(i[BODY])) return '' def h_if(self, i): expr = self.h(i[CLAUSE]) if expr: do = i[BODY] else: for e in i[ELIF]: expr = self.h(e[CLAUSE]) if expr: do = e[BODY] break else: do = i[ELSE] return ''.join(self.h_lines(do)) def h_for(self, i): out = [] assert i[IN][WHAT] == 'expr' invar = self.h(i[IN]) forvar = i[NAME] if invar: for nv in invar: if len(forvar) == 1: fv = forvar[0] assert fv[WHAT] == 'var' self.d[fv[NAME]] = nv # same (lack of) scoping as Python else: for x, y in zip(forvar, nv): assert x[WHAT] == 'var' self.d[x[NAME]] = y out.extend(self.h_lines(i[BODY])) else: if i[ELSE]: out.extend(self.h_lines(i[ELSE])) return ''.join(out) def h_while(self, i): out = [] expr = self.h(i[CLAUSE]) if not expr: return ''.join(self.h_lines(i[ELSE])) c = 0 while expr: c += 1 if c >= MAX_ITERS: raise RuntimeError, 'too many while-loop iterations (line %s)' % i[LINENO] out.extend(self.h_lines(i[BODY])) expr = self.h(i[CLAUSE]) return ''.join(out) def h_assign(self, i): self.d[i[NAME]] = self.h(i[EXPR]) return '' def h_comment(self, i): pass def h_lines(self, lines): if lines is None: return [] return map(self.h, lines) def go(self): self.output = Stowage() self.output._str = ''.join(map(self.h, self.parsetree)) if self.output.keys() == ['_str']: self.output = self.output['_str'] return self.outputclass render: def __init__(self, loc='templates/', cache=True): self.loc = loc if cache: self.cache = {} else: self.cache = False def _do(self, name, filter=None): if self.cache is False or name not in self.cache: tmplpath = os.path.join(self.loc, name) p = [f for f in glob.glob(tmplpath + '.*') if not f.endswith('~')] # skip backup files if not p and os.path.isdir(tmplpath): return render(tmplpath, cache=self.cache) elif not p: raise AttributeError, 'no template named ' + name p = p[0] c = Template(open(p).read(), filename=p) if self.cache is not False: self.cache[name] = (p, c) if self.cache is not False: p, c = self.cache[name] if p.endswith('.html') or p.endswith('.xml'): if not filter: c.filter = websafe return c def __getattr__(self, p): return self._do(p)def frender(fn, *a, **kw): return Template(open(fn).read(), *a, **kw)def test(): import sys verbose = '-v' in sys.argv def assertEqual(a, b): if a == b: if verbose: sys.stderr.write('.') sys.stderr.flush() else: assert a == b, "\nexpected: %s\ngot: %s" % (repr(b), repr(a)) from utils import storage, group class t: def __init__(self, text): self.text = text def __call__(self, *a, **kw): return TestResult(self.text, Template(self.text)(*a, **kw)) class TestResult: def __init__(self, source, value): self.source = source self.value = value def __eq__(self, other): if self.value == other: if verbose: sys.stderr.write('.') else: print >> sys.stderr, 'FAIL:', repr(self.source), 'expected', repr(other), ', got', repr(self.value) sys.stderr.flush() t('1')() == '1\n' t('$def with ()\n1')() == '1\n' t('$def with (a)\n$a')(1) == '1\n' t('$def with (a=0)\n$a')(1) == '1\n' t('$def with (a=0)\n$a')(a=1) == '1\n' t('$if 1: 1')() == '1\n' t('$if 1:\n 1')() == '1\n' t('$if 0: 0\n$elif 1: 1')() == '1\n' t('$if 0: 0\n$elif None: 0\n$else: 1')() == '1\n' t('$if (0 < 1) and (1 < 2): 1')() == '1\n' t('$for x in [1, 2, 3]: $x')() == '1\n2\n3\n' t('$for x in []: 0\n$else: 1')() == '1\n' t('$def with (a)\n$while a and a.pop(): 1')([1, 2, 3]) == '1\n1\n1\n' t('$while 0: 0\n$else: 1')() == '1\n' t('$ a = 1\n$a')() == '1\n' t('$# 0')() == '' t('$def with (d)\n$for k, v in d.iteritems(): $k')({1: 1}) == '1\n' t('$def with (a)\n$(a)')(1) == '1\n' t('$def with (a)\n$a')(1) == '1\n' t('$def with (a)\n$a.b')(storage(b=1)) == '1\n' t('$def with (a)\n$a[0]')([1]) == '1\n' t('${0 or 1}')() == '1\n' t('$ a = [1]\n$a[0]')() == '1\n' t('$ a = {1: 1}\n$a.keys()[0]')() == '1\n' t('$ a = []\n$if not a: 1')() == '1\n' t('$ a = {}\n$if not a: 1')() == '1\n' t('$ a = -1\n$a')() == '-1\n' t('$ a = "1"\n$a')() == '1\n' t('$if 1 is 1: 1')() == '1\n' t('$if not 0: 1')() == '1\n' t('$if 1:\n $if 1: 1')() == '1\n' t('$ a = 1\n$a')() == '1\n' t('$ a = 1.\n$a')() == '1.0\n' t('$({1: 1}.keys()[0])')() == '1\n' t('$for x in [1, 2, 3]:\n\t$x')() == ' 1\n 2\n 3\n' t('$def with (a)\n$:a')(1) == '1\n' t('$def with (a)\n$a')(u'\u203d') == '\xe2\x80\xbd\n' t(u'$def with (f)\n$:f("x")')(lambda x: x) == 'x\n' j = Template("$var foo: bar")() assertEqual(str(j), '') assertEqual(j.foo, 'bar\n') if verbose: sys.stderr.write('\n') if __name__ == "__main__": test()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -