aboutsummaryrefslogtreecommitdiff
path: root/cgi/tenjin.py
diff options
context:
space:
mode:
Diffstat (limited to 'cgi/tenjin.py')
-rw-r--r--cgi/tenjin.py839
1 files changed, 507 insertions, 332 deletions
diff --git a/cgi/tenjin.py b/cgi/tenjin.py
index db8cdde..ddc12bb 100644
--- a/cgi/tenjin.py
+++ b/cgi/tenjin.py
@@ -1,26 +1,26 @@
##
-## $Release: 1.1.1 $
-## $Copyright: copyright(c) 2007-2012 kuwata-lab.com all rights reserved. $
-## $License: MIT License $
+# $Release: 1.1.1 $
+# $Copyright: copyright(c) 2007-2012 kuwata-lab.com all rights reserved. $
+# $License: MIT License $
##
-## Permission is hereby granted, free of charge, to any person obtaining
-## a copy of this software and associated documentation files (the
-## "Software"), to deal in the Software without restriction, including
-## without limitation the rights to use, copy, modify, merge, publish,
-## distribute, sublicense, and/or sell copies of the Software, and to
-## permit persons to whom the Software is furnished to do so, subject to
-## the following conditions:
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
##
-## The above copyright notice and this permission notice shall be
-## included in all copies or substantial portions of the Software.
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
##
-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-## LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-## WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
##
"""Very fast and light-weight template engine based embedded Python.
@@ -29,15 +29,19 @@
http://www.kuwata-lab.com/tenjin/pytenjin-examples.html
"""
-__version__ = "$Release: 1.1.1 $"[10:-2]
-__license__ = "$License: MIT License $"[10:-2]
-__all__ = ('Template', 'Engine', )
+__version__ = "$Release: 1.1.1 $"[10:-2]
+__license__ = "$License: MIT License $"[10:-2]
+__all__ = ('Template', 'Engine', )
-import sys, os, re, time, marshal
-from time import time as _time
-from os.path import getmtime as _getmtime
from os.path import isfile as _isfile
+from os.path import getmtime as _getmtime
+from time import time as _time
+import sys
+import os
+import re
+import time
+import marshal
random = pickle = unquote = None # lazy import
python3 = sys.version_info[0] == 3
python2 = sys.version_info[0] == 2
@@ -46,12 +50,13 @@ logger = None
##
-## utilities
+# utilities
##
def _write_binary_file(filename, content):
global random
- if random is None: from random import random
+ if random is None:
+ from random import random
tmpfile = filename + str(random())[1:]
f = open(tmpfile, 'w+b') # on windows, 'w+b' is preffered than 'wb'
try:
@@ -62,9 +67,11 @@ def _write_binary_file(filename, content):
try:
os.rename(tmpfile, filename)
except:
- os.remove(filename) # on windows, existing file should be removed before renaming
+ # on windows, existing file should be removed before renaming
+ os.remove(filename)
os.rename(tmpfile, filename)
+
def _read_binary_file(filename):
f = open(filename, 'rb')
try:
@@ -72,25 +79,32 @@ def _read_binary_file(filename):
finally:
f.close()
+
codecs = None # lazy import
+
def _read_text_file(filename, encoding=None):
global codecs
- if not codecs: import codecs
+ if not codecs:
+ import codecs
f = codecs.open(filename, encoding=(encoding or 'utf-8'))
try:
return f.read()
finally:
f.close()
+
def _read_template_file(filename, encoding=None):
- s = _read_binary_file(filename) ## binary(=str)
- if encoding: s = s.decode(encoding) ## binary(=str) to unicode
+ s = _read_binary_file(filename) # binary(=str)
+ if encoding:
+ s = s.decode(encoding) # binary(=str) to unicode
return s
+
_basestring = basestring
-_unicode = unicode
-_bytes = str
+_unicode = unicode
+_bytes = str
+
def _ignore_not_found_error(f, default=None):
try:
@@ -100,6 +114,7 @@ def _ignore_not_found_error(f, default=None):
return default
raise
+
def create_module(module_name, dummy_func=None, **kwargs):
"""ex. mod = create_module('tenjin.util')"""
try:
@@ -116,12 +131,13 @@ def create_module(module_name, dummy_func=None, **kwargs):
exec(dummy_func.func_code, mod.__dict__)
return mod
+
def _raise(exception_class, *args):
raise exception_class(*args)
##
-## helper method's module
+# helper method's module
##
def _dummy():
@@ -142,13 +158,16 @@ def _dummy():
"""
if encode:
if decode:
- raise ValueError("can't specify both encode and decode encoding.")
+ raise ValueError(
+ "can't specify both encode and decode encoding.")
else:
def to_str(val, _str=str, _unicode=unicode, _isa=isinstance, _encode=encode):
"""Convert val into string or return '' if None. Unicode will be encoded into binary(=str)."""
- if _isa(val, _str): return val
- if val is None: return ''
- #if _isa(val, _unicode): return val.encode(_encode) # unicode to binary(=str)
+ if _isa(val, _str):
+ return val
+ if val is None:
+ return ''
+ # if _isa(val, _unicode): return val.encode(_encode) # unicode to binary(=str)
if _isa(val, _unicode):
return val.encode(_encode) # unicode to binary(=str)
return _str(val)
@@ -156,18 +175,23 @@ def _dummy():
if decode:
def to_str(val, _str=str, _unicode=unicode, _isa=isinstance, _decode=decode):
"""Convert val into string or return '' if None. Binary(=str) will be decoded into unicode."""
- #if _isa(val, _str): return val.decode(_decode) # binary(=str) to unicode
+ # if _isa(val, _str): return val.decode(_decode) # binary(=str) to unicode
if _isa(val, _str):
return val.decode(_decode)
- if val is None: return ''
- if _isa(val, _unicode): return val
+ if val is None:
+ return ''
+ if _isa(val, _unicode):
+ return val
return _unicode(val)
else:
def to_str(val, _str=str, _unicode=unicode, _isa=isinstance):
"""Convert val into string or return '' if None. Both binary(=str) and unicode will be retruned as-is."""
- if _isa(val, _str): return val
- if val is None: return ''
- if _isa(val, _unicode): return val
+ if _isa(val, _str):
+ return val
+ if val is None:
+ return ''
+ if _isa(val, _unicode):
+ return val
return _str(val)
return to_str
@@ -197,21 +221,21 @@ def _dummy():
class CaptureContext(object):
def __init__(self, name, store_to_context=True, lvars=None):
- self.name = name
+ self.name = name
self.store_to_context = store_to_context
self.lvars = lvars or sys._getframe(1).f_locals
def __enter__(self):
lvars = self.lvars
self._buf_orig = lvars['_buf']
- lvars['_buf'] = _buf = []
+ lvars['_buf'] = _buf = []
lvars['_extend'] = _buf.extend
return self
def __exit__(self, *args):
lvars = self.lvars
_buf = lvars['_buf']
- lvars['_buf'] = self._buf_orig
+ lvars['_buf'] = self._buf_orig
lvars['_extend'] = self._buf_orig.extend
lvars[self.name] = self.captured = ''.join(_buf)
if self.store_to_context and '_context' in lvars:
@@ -236,7 +260,8 @@ def _dummy():
lvars = sys._getframe(_depth).f_locals
capture_context = lvars.pop('_capture_context', None)
if not capture_context:
- raise Exception('stop_capture(): start_capture() is not called before.')
+ raise Exception(
+ 'stop_capture(): start_capture() is not called before.')
capture_context.store_to_context = store_to_context
capture_context.__exit__()
return capture_context.captured
@@ -270,19 +295,25 @@ def _dummy():
global unquote
if unquote is None:
from urllib import unquote
- dct = { 'lt':'<', 'gt':'>', 'amp':'&', 'quot':'"', '#039':"'", }
+ dct = {'lt': '<', 'gt': '>', 'amp': '&', 'quot': '"', '#039': "'", }
+
def unescape(s):
- #return s.replace('&lt;', '<').replace('&gt;', '>').replace('&quot;', '"').replace('&#039;', "'").replace('&amp;', '&')
- return re.sub(r'&(lt|gt|quot|amp|#039);', lambda m: dct[m.group(1)], s)
+ # return s.replace('&lt;', '<').replace('&gt;', '>').replace('&quot;', '"').replace('&#039;', "'").replace('&amp;', '&')
+ return re.sub(r'&(lt|gt|quot|amp|#039);', lambda m: dct[m.group(1)], s)
s = to_str(s)
- s = re.sub(r'%3C%60%23(.*?)%23%60%3E', lambda m: '#{%s}' % unquote(m.group(1)), s)
- s = re.sub(r'%3C%60%24(.*?)%24%60%3E', lambda m: '${%s}' % unquote(m.group(1)), s)
- s = re.sub(r'&lt;`#(.*?)#`&gt;', lambda m: '#{%s}' % unescape(m.group(1)), s)
- s = re.sub(r'&lt;`\$(.*?)\$`&gt;', lambda m: '${%s}' % unescape(m.group(1)), s)
+ s = re.sub(r'%3C%60%23(.*?)%23%60%3E',
+ lambda m: '#{%s}' % unquote(m.group(1)), s)
+ s = re.sub(r'%3C%60%24(.*?)%24%60%3E',
+ lambda m: '${%s}' % unquote(m.group(1)), s)
+ s = re.sub(r'&lt;`#(.*?)#`&gt;',
+ lambda m: '#{%s}' % unescape(m.group(1)), s)
+ s = re.sub(r'&lt;`\$(.*?)\$`&gt;',
+ lambda m: '${%s}' % unescape(m.group(1)), s)
s = re.sub(r'<`#(.*?)#`>', r'#{\1}', s)
s = re.sub(r'<`\$(.*?)\$`>', r'${\1}', s)
return s
+
helpers = create_module('tenjin.helpers', _dummy, sys=sys, re=re)
helpers.__all__ = ['to_str', 'escape', 'echo', 'new_cycle', 'generate_tostrfunc',
'start_capture', 'stop_capture', 'capture_as', 'captured_as',
@@ -293,13 +324,14 @@ generate_tostrfunc = helpers.generate_tostrfunc
##
-## escaped module
+# escaped module
##
def _dummy():
global is_escaped, as_escaped, to_escaped
global Escaped, EscapedStr, EscapedUnicode
global __all__
- __all__ = ('is_escaped', 'as_escaped', 'to_escaped', ) #'Escaped', 'EscapedStr',
+ # 'Escaped', 'EscapedStr',
+ __all__ = ('is_escaped', 'as_escaped', 'to_escaped', )
class Escaped(object):
"""marking class that object is already escaped."""
@@ -319,8 +351,10 @@ def _dummy():
def as_escaped(s):
"""mark string as escaped, without escaping."""
- if isinstance(s, str): return EscapedStr(s)
- if isinstance(s, unicode): return EscapedUnicode(s)
+ if isinstance(s, str):
+ return EscapedStr(s)
+ if isinstance(s, unicode):
+ return EscapedUnicode(s)
raise TypeError("as_escaped(%r): expected str or unicode." % (s, ))
def to_escaped(value):
@@ -329,23 +363,24 @@ def _dummy():
if hasattr(value, '__html__'):
value = value.__html__()
if is_escaped(value):
- #return value # EscapedUnicode should be convered into EscapedStr
+ # return value # EscapedUnicode should be convered into EscapedStr
return as_escaped(_helpers.to_str(value))
- #if isinstance(value, _basestring):
+ # if isinstance(value, _basestring):
# return as_escaped(_helpers.escape(value))
return as_escaped(_helpers.escape(_helpers.to_str(value)))
+
escaped = create_module('tenjin.escaped', _dummy, _helpers=helpers)
##
-## module for html
+# module for html
##
def _dummy():
global escape_html, escape_xml, escape, tagattr, tagattrs, _normalize_attrs
global checked, selected, disabled, nl2br, text2html, nv, js_link
- #_escape_table = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }
+ # _escape_table = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }
#_escape_pattern = re.compile(r'[&<>"]')
##_escape_callable = lambda m: _escape_table[m.group(0)]
##_escape_callable = lambda m: _escape_table.__get__(m.group(0))
@@ -353,22 +388,22 @@ def _dummy():
#_escape_callable = lambda m: _escape_get(m.group(0))
#_escape_sub = _escape_pattern.sub
- #def escape_html(s):
+ # def escape_html(s):
# return s # 3.02
- #def escape_html(s):
+ # def escape_html(s):
# return _escape_pattern.sub(_escape_callable, s) # 6.31
- #def escape_html(s):
+ # def escape_html(s):
# return _escape_sub(_escape_callable, s) # 6.01
- #def escape_html(s, _p=_escape_pattern, _f=_escape_callable):
+ # def escape_html(s, _p=_escape_pattern, _f=_escape_callable):
# return _p.sub(_f, s) # 6.27
- #def escape_html(s, _sub=_escape_pattern.sub, _callable=_escape_callable):
+ # def escape_html(s, _sub=_escape_pattern.sub, _callable=_escape_callable):
# return _sub(_callable, s) # 6.04
- #def escape_html(s):
+ # def escape_html(s):
# s = s.replace('&', '&amp;')
# s = s.replace('<', '&lt;')
# s = s.replace('>', '&gt;')
@@ -384,9 +419,12 @@ def _dummy():
def tagattr(name, expr, value=None, escape=True):
"""(experimental) Return ' name="value"' if expr is true value, else '' (empty string).
If value is not specified, expr is used as value instead."""
- if not expr and expr != 0: return _escaped.as_escaped('')
- if value is None: value = expr
- if escape: value = _escaped.to_escaped(value)
+ if not expr and expr != 0:
+ return _escaped.as_escaped('')
+ if value is None:
+ value = expr
+ if escape:
+ value = _escaped.to_escaped(value)
return _escaped.as_escaped(' %s="%s"' % (name, value))
def tagattrs(**kwargs):
@@ -399,14 +437,19 @@ def _dummy():
"""
kwargs = _normalize_attrs(kwargs)
esc = _escaped.to_escaped
- s = ''.join([ ' %s="%s"' % (k, esc(v)) for k, v in kwargs.iteritems() if v or v == 0 ])
+ s = ''.join([' %s="%s"' % (k, esc(v))
+ for k, v in kwargs.iteritems() if v or v == 0])
return _escaped.as_escaped(s)
def _normalize_attrs(kwargs):
- if 'klass' in kwargs: kwargs['class'] = kwargs.pop('klass')
- if 'checked' in kwargs: kwargs['checked'] = kwargs.pop('checked') and 'checked' or None
- if 'selected' in kwargs: kwargs['selected'] = kwargs.pop('selected') and 'selected' or None
- if 'disabled' in kwargs: kwargs['disabled'] = kwargs.pop('disabled') and 'disabled' or None
+ if 'klass' in kwargs:
+ kwargs['class'] = kwargs.pop('klass')
+ if 'checked' in kwargs:
+ kwargs['checked'] = kwargs.pop('checked') and 'checked' or None
+ if 'selected' in kwargs:
+ kwargs['selected'] = kwargs.pop('selected') and 'selected' or None
+ if 'disabled' in kwargs:
+ kwargs['disabled'] = kwargs.pop('disabled') and 'disabled' or None
return kwargs
def checked(expr):
@@ -432,8 +475,9 @@ def _dummy():
if not text:
return _escaped.as_escaped('')
s = _escaped.to_escaped(text)
- if use_nbsp: s = s.replace(' ', ' &nbsp;')
- #return nl2br(s)
+ if use_nbsp:
+ s = s.replace(' ', ' &nbsp;')
+ # return nl2br(s)
s = s.replace('\n', '<br />\n')
return _escaped.as_escaped(s)
@@ -449,19 +493,20 @@ def _dummy():
>>> nv('rank', 'A', '.', klass='error', style='color:red')
'name="rank" value="A" id="rank.A" class="error" style="color:red"'
"""
- name = _escaped.to_escaped(name)
+ name = _escaped.to_escaped(name)
value = _escaped.to_escaped(value)
s = sep and 'name="%s" value="%s" id="%s"' % (name, value, name+sep+value) \
- or 'name="%s" value="%s"' % (name, value)
+ or 'name="%s" value="%s"' % (name, value)
html = kwargs and s + tagattrs(**kwargs) or s
return _escaped.as_escaped(html)
def js_link(label, onclick, **kwargs):
s = kwargs and tagattrs(**kwargs) or ''
html = '<a href="javascript:undefined" onclick="%s;return false"%s>%s</a>' % \
- (_escaped.to_escaped(onclick), s, _escaped.to_escaped(label))
+ (_escaped.to_escaped(onclick), s, _escaped.to_escaped(label))
return _escaped.as_escaped(html)
+
html = create_module('tenjin.html', _dummy, helpers=helpers, _escaped=escaped)
helpers.escape = html.escape_html
helpers.html = html # for backward compatibility
@@ -469,10 +514,11 @@ sys.modules['tenjin.helpers.html'] = html
##
-## utility function to set default encoding of template files
+# utility function to set default encoding of template files
##
_template_encoding = (None, 'utf-8') # encodings for decode and encode
+
def set_template_encoding(decode=None, encode=None):
"""Set default encoding of template files.
This should be called before importing helper functions.
@@ -486,9 +532,11 @@ def set_template_encoding(decode=None, encode=None):
if _template_encoding == (decode, encode):
return
if decode and encode:
- raise ValueError("set_template_encoding(): cannot specify both decode and encode.")
+ raise ValueError(
+ "set_template_encoding(): cannot specify both decode and encode.")
if not decode and not encode:
- raise ValueError("set_template_encoding(): decode or encode should be specified.")
+ raise ValueError(
+ "set_template_encoding(): decode or encode should be specified.")
if decode:
Template.encoding = decode # unicode base template
helpers.to_str = helpers.generate_tostrfunc(decode=decode)
@@ -499,7 +547,7 @@ def set_template_encoding(decode=None, encode=None):
##
-## Template class
+# Template class
##
class TemplateSyntaxError(SyntaxError):
@@ -510,8 +558,8 @@ class TemplateSyntaxError(SyntaxError):
return self.args[0]
return ''.join([
"%s:%s:%s: %s\n" % (ex.filename, ex.lineno, ex.offset, ex.msg, ),
- "%4d: %s\n" % (ex.lineno, ex.text.rstrip(), ),
- " %s^\n" % (' ' * ex.offset, ),
+ "%4d: %s\n" % (ex.lineno, ex.text.rstrip(), ),
+ " %s^\n" % (' ' * ex.offset, ),
])
@@ -522,21 +570,21 @@ class Template(object):
http://www.kuwata-lab.com/tenjin/pytenjin-examples.html
"""
- ## default value of attributes
- filename = None
- encoding = None
+ # default value of attributes
+ filename = None
+ encoding = None
escapefunc = 'escape'
- tostrfunc = 'to_str'
- indent = 4
- preamble = None # "_buf = []; _expand = _buf.expand; _to_str = to_str; _escape = escape"
- postamble = None # "print ''.join(_buf)"
- smarttrim = None
- args = None
- timestamp = None
- trace = False # if True then '<!-- begin: file -->' and '<!-- end: file -->' are printed
+ tostrfunc = 'to_str'
+ indent = 4
+ preamble = None # "_buf = []; _expand = _buf.expand; _to_str = to_str; _escape = escape"
+ postamble = None # "print ''.join(_buf)"
+ smarttrim = None
+ args = None
+ timestamp = None
+ trace = False # if True then '<!-- begin: file -->' and '<!-- end: file -->' are printed
def __init__(self, filename=None, encoding=None, input=None, escapefunc=None, tostrfunc=None,
- indent=None, preamble=None, postamble=None, smarttrim=None, trace=None):
+ indent=None, preamble=None, postamble=None, smarttrim=None, trace=None):
"""Initailizer of Template class.
filename:str (=None)
@@ -565,29 +613,40 @@ class Template(object):
If True then "<div>\\n#{_context}\\n</div>" is parsed as
"<div>\\n#{_context}</div>".
"""
- if encoding is not None: self.encoding = encoding
- if escapefunc is not None: self.escapefunc = escapefunc
- if tostrfunc is not None: self.tostrfunc = tostrfunc
- if indent is not None: self.indent = indent
- if preamble is not None: self.preamble = preamble
- if postamble is not None: self.postamble = postamble
- if smarttrim is not None: self.smarttrim = smarttrim
- if trace is not None: self.trace = trace
+ if encoding is not None:
+ self.encoding = encoding
+ if escapefunc is not None:
+ self.escapefunc = escapefunc
+ if tostrfunc is not None:
+ self.tostrfunc = tostrfunc
+ if indent is not None:
+ self.indent = indent
+ if preamble is not None:
+ self.preamble = preamble
+ if postamble is not None:
+ self.postamble = postamble
+ if smarttrim is not None:
+ self.smarttrim = smarttrim
+ if trace is not None:
+ self.trace = trace
#
- if preamble is True: self.preamble = "_buf = []"
- if postamble is True: self.postamble = "print(''.join(_buf))"
+ if preamble is True:
+ self.preamble = "_buf = []"
+ if postamble is True:
+ self.postamble = "print(''.join(_buf))"
if input:
self.convert(input, filename)
- self.timestamp = False # False means 'file not exist' (= Engine should not check timestamp of file)
+ # False means 'file not exist' (= Engine should not check timestamp of file)
+ self.timestamp = False
elif filename:
self.convert_file(filename)
else:
self._reset()
def _reset(self, input=None, filename=None):
- self.script = None
+ self.script = None
self.bytecode = None
- self.input = input
+ self.input = input
self.filename = filename
if input != None:
i = input.find("\n")
@@ -648,7 +707,8 @@ class Template(object):
return pat
def parse_stmts(self, buf, input):
- if not input: return
+ if not input:
+ return
rexp = self.stmt_pattern()
is_bol = True
index = 0
@@ -658,7 +718,7 @@ class Template(object):
#code = input[m.start()+4+len(mspace):m.end()-len(close)-(rspace and len(rspace) or 0)]
text = input[index:m.start()]
index = m.end()
- ## detect spaces at beginning of line
+ # detect spaces at beginning of line
lspace = None
if text == '':
if is_bol:
@@ -675,13 +735,13 @@ class Template(object):
if s.isspace():
lspace, text = s, text[:rindex+1]
#is_bol = rspace is not None
- ## add text, spaces, and statement
+ # add text, spaces, and statement
self.parse_exprs(buf, text, is_bol)
is_bol = rspace is not None
- #if mspace == "\n":
+ # if mspace == "\n":
if mspace and mspace.endswith("\n"):
code = "\n" + (code or "")
- #if rspace == "\n":
+ # if rspace == "\n":
if rspace and rspace.endswith("\n"):
code = (code or "") + "\n"
if code:
@@ -708,10 +768,12 @@ class Template(object):
def _add_args_declaration(self, buf, m):
arr = (m.group(1) or '').split(',')
- args = []; declares = []
+ args = []
+ declares = []
for s in arr:
arg = s.strip()
- if not s: continue
+ if not s:
+ continue
if not re.match('^[a-zA-Z_]\w*$', arg):
raise ValueError("%r: invalid template argument." % arg)
args.append(arg)
@@ -722,7 +784,8 @@ class Template(object):
buf.append(''.join(declares) + "\n")
s = '(?:\{.*?\}.*?)*'
- EXPR_PATTERN = (r'#\{(.*?'+s+r')\}|\$\{(.*?'+s+r')\}|\{=(?:=(.*?)=|(.*?))=\}', re.S)
+ EXPR_PATTERN = (
+ r'#\{(.*?'+s+r')\}|\$\{(.*?'+s+r')\}|\{=(?:=(.*?)=|(.*?))=\}', re.S)
del s
def expr_pattern(self):
@@ -733,10 +796,14 @@ class Template(object):
def get_expr_and_flags(self, match):
expr1, expr2, expr3, expr4 = match.groups()
- if expr1 is not None: return expr1, (False, True) # not escape, call to_str
- if expr2 is not None: return expr2, (True, True) # call escape, call to_str
- if expr3 is not None: return expr3, (False, True) # not escape, call to_str
- if expr4 is not None: return expr4, (True, True) # call escape, call to_str
+ if expr1 is not None:
+ return expr1, (False, True) # not escape, call to_str
+ if expr2 is not None:
+ return expr2, (True, True) # call escape, call to_str
+ if expr3 is not None:
+ return expr3, (False, True) # not escape, call to_str
+ if expr4 is not None:
+ return expr4, (True, True) # call escape, call to_str
def parse_exprs(self, buf, input, is_bol=False):
buf2 = []
@@ -745,17 +812,18 @@ class Template(object):
buf.append(''.join(buf2))
def _parse_exprs(self, buf, input, is_bol=False):
- if not input: return
+ if not input:
+ return
self.start_text_part(buf)
rexp = self.expr_pattern()
smarttrim = self.smarttrim
nl = self.newline
- nl_len = len(nl)
+ nl_len = len(nl)
pos = 0
for m in rexp.finditer(input):
start = m.start()
- text = input[pos:start]
- pos = m.end()
+ text = input[pos:start]
+ pos = m.end()
expr, flags = self.get_expr_and_flags(m)
#
if text:
@@ -763,7 +831,8 @@ class Template(object):
self.add_expr(buf, expr, *flags)
#
if smarttrim:
- flag_bol = text.endswith(nl) or not text and (start > 0 or is_bol)
+ flag_bol = text.endswith(
+ nl) or not text and (start > 0 or is_bol)
if flag_bol and not flags[0] and input[pos:pos+nl_len] == nl:
pos += nl_len
buf.append("\n")
@@ -779,7 +848,7 @@ class Template(object):
def start_text_part(self, buf):
self._add_localvars_assignments_to_text(buf)
- #buf.append("_buf.extend((")
+ # buf.append("_buf.extend((")
buf.append("_extend((")
def _add_localvars_assignments_to_text(self, buf):
@@ -796,30 +865,43 @@ class Template(object):
return text
def add_text(self, buf, text, encode_newline=False):
- if not text: return
+ if not text:
+ return
use_unicode = self.encoding and python2
buf.append(use_unicode and "u'''" or "'''")
text = self._quote_text(text)
- if not encode_newline: buf.extend((text, "''', "))
- elif text.endswith("\r\n"): buf.extend((text[0:-2], "\\r\\n''', "))
- elif text.endswith("\n"): buf.extend((text[0:-1], "\\n''', "))
- else: buf.extend((text, "''', "))
+ if not encode_newline:
+ buf.extend((text, "''', "))
+ elif text.endswith("\r\n"):
+ buf.extend((text[0:-2], "\\r\\n''', "))
+ elif text.endswith("\n"):
+ buf.extend((text[0:-1], "\\n''', "))
+ else:
+ buf.extend((text, "''', "))
_add_text = add_text
def add_expr(self, buf, code, *flags):
- if not code or code.isspace(): return
+ if not code or code.isspace():
+ return
flag_escape, flag_tostr = flags
- if not self.tostrfunc: flag_tostr = False
- if not self.escapefunc: flag_escape = False
- if flag_tostr and flag_escape: s1, s2 = "_escape(_to_str(", ")), "
- elif flag_tostr: s1, s2 = "_to_str(", "), "
- elif flag_escape: s1, s2 = "_escape(", "), "
- else: s1, s2 = "(", "), "
+ if not self.tostrfunc:
+ flag_tostr = False
+ if not self.escapefunc:
+ flag_escape = False
+ if flag_tostr and flag_escape:
+ s1, s2 = "_escape(_to_str(", ")), "
+ elif flag_tostr:
+ s1, s2 = "_to_str(", "), "
+ elif flag_escape:
+ s1, s2 = "_escape(", "), "
+ else:
+ s1, s2 = "(", "), "
buf.extend((s1, code, s2, ))
def add_stmt(self, buf, code):
- if not code: return
+ if not code:
+ return
lines = code.splitlines(True) # keep "\n"
if lines[-1][-1] != "\n":
lines[-1] = lines[-1] + "\n"
@@ -840,59 +922,64 @@ class Template(object):
else:
buf[index] = self._localvars_assignments() + buf[index]
-
- _START_WORDS = dict.fromkeys(('for', 'if', 'while', 'def', 'try:', 'with', 'class'), True)
- _END_WORDS = dict.fromkeys(('#end', '#endfor', '#endif', '#endwhile', '#enddef', '#endtry', '#endwith', '#endclass'), True)
- _CONT_WORDS = dict.fromkeys(('elif', 'else:', 'except', 'except:', 'finally:'), True)
- _WORD_REXP = re.compile(r'\S+')
+ _START_WORDS = dict.fromkeys(
+ ('for', 'if', 'while', 'def', 'try:', 'with', 'class'), True)
+ _END_WORDS = dict.fromkeys(('#end', '#endfor', '#endif', '#endwhile',
+ '#enddef', '#endtry', '#endwith', '#endclass'), True)
+ _CONT_WORDS = dict.fromkeys(
+ ('elif', 'else:', 'except', 'except:', 'finally:'), True)
+ _WORD_REXP = re.compile(r'\S+')
depth = -1
##
- ## ex.
- ## input = r"""
- ## if items:
+ # ex.
+ # input = r"""
+ # if items:
## _buf.extend(('<ul>\n', ))
## i = 0
- ## for item in items:
+ # for item in items:
## i += 1
## _buf.extend(('<li>', to_str(item), '</li>\n', ))
- ## #endfor
+ # endfor
## _buf.extend(('</ul>\n', ))
- ## #endif
- ## """[1:]
+ # endif
+ # """[1:]
## lines = input.splitlines(True)
## block = self.parse_lines(lines)
- ## #=> [ "if items:\n",
- ## [ "_buf.extend(('<ul>\n', ))\n",
+ # => [ "if items:\n",
+ # [ "_buf.extend(('<ul>\n', ))\n",
## "i = 0\n",
## "for item in items:\n",
- ## [ "i += 1\n",
+ # [ "i += 1\n",
## "_buf.extend(('<li>', to_str(item), '</li>\n', ))\n",
- ## ],
- ## "#endfor\n",
+ # ],
+ # "#endfor\n",
## "_buf.extend(('</ul>\n', ))\n",
- ## ],
- ## "#endif\n",
- ## ]
+ # ],
+ # "#endif\n",
+ # ]
def parse_lines(self, lines):
block = []
try:
self._parse_lines(lines.__iter__(), False, block, 0)
except StopIteration:
if self.depth > 0:
- fname, linenum, colnum, linetext = self.filename, len(lines), None, None
- raise TemplateSyntaxError("unexpected EOF.", (fname, linenum, colnum, linetext))
+ fname, linenum, colnum, linetext = self.filename, len(
+ lines), None, None
+ raise TemplateSyntaxError(
+ "unexpected EOF.", (fname, linenum, colnum, linetext))
else:
pass
return block
def _parse_lines(self, lines_iter, end_block, block, linenum):
- if block is None: block = []
+ if block is None:
+ block = []
_START_WORDS = self._START_WORDS
- _END_WORDS = self._END_WORDS
- _CONT_WORDS = self._CONT_WORDS
- _WORD_REXP = self._WORD_REXP
+ _END_WORDS = self._END_WORDS
+ _CONT_WORDS = self._CONT_WORDS
+ _WORD_REXP = self._WORD_REXP
get_line = lines_iter.next
while True:
line = get_line()
@@ -905,11 +992,13 @@ class Template(object):
if word in _END_WORDS:
if word != end_block and word != '#end':
if end_block is False:
- msg = "'%s' found but corresponding statement is missing." % (word, )
+ msg = "'%s' found but corresponding statement is missing." % (
+ word, )
else:
msg = "'%s' expected but got '%s'." % (end_block, word)
colnum = m.start() + 1
- raise TemplateSyntaxError(msg, (self.filename, linenum, colnum, line))
+ raise TemplateSyntaxError(
+ msg, (self.filename, linenum, colnum, line))
return block, line, None, linenum
elif line.endswith(':\n') or line.endswith(':\r\n'):
if word in _CONT_WORDS:
@@ -920,16 +1009,19 @@ class Template(object):
cont_word = None
try:
child_block, line, cont_word, linenum = \
- self._parse_lines(lines_iter, '#end'+word, [], linenum)
+ self._parse_lines(
+ lines_iter, '#end'+word, [], linenum)
block.extend((child_block, line, ))
while cont_word: # 'elif' or 'else:'
child_block, line, cont_word, linenum = \
- self._parse_lines(lines_iter, '#end'+word, [], linenum)
+ self._parse_lines(
+ lines_iter, '#end'+word, [], linenum)
block.extend((child_block, line, ))
except StopIteration:
msg = "'%s' is not closed." % (cont_word or word)
colnum = m.start() + 1
- raise TemplateSyntaxError(msg, (self.filename, linenum, colnum, line))
+ raise TemplateSyntaxError(
+ msg, (self.filename, linenum, colnum, line))
self.depth -= 1
else:
block.append(line)
@@ -953,7 +1045,6 @@ class Template(object):
buf[:] = []
self._join_block(block, buf, 0)
-
def render(self, context=None, globals=None, _buf=None):
"""Evaluate python code with context dictionary.
If _buf is None then return the result of evaluation as str,
@@ -1003,11 +1094,12 @@ class Template(object):
def compile(self):
"""compile self.script into self.bytecode"""
- self.bytecode = compile(self.script, self.filename or '(tenjin)', 'exec')
+ self.bytecode = compile(
+ self.script, self.filename or '(tenjin)', 'exec')
##
-## preprocessor class
+# preprocessor class
##
class Preprocessor(Template):
@@ -1015,7 +1107,8 @@ class Preprocessor(Template):
STMT_PATTERN = (r'<\?PY( |\t|\r?\n)(.*?) ?\?>([ \t]*\r?\n)?', re.S)
- EXPR_PATTERN = (r'#\{\{(.*?)\}\}|\$\{\{(.*?)\}\}|\{#=(?:=(.*?)=|(.*?))=#\}', re.S)
+ EXPR_PATTERN = (
+ r'#\{\{(.*?)\}\}|\$\{\{(.*?)\}\}|\{#=(?:=(.*?)=|(.*?))=#\}', re.S)
def add_expr(self, buf, code, *flags):
if not code or code.isspace():
@@ -1028,13 +1121,14 @@ class TemplatePreprocessor(object):
factory = Preprocessor
def __init__(self, factory=None):
- if factory is not None: self.factory = factory
+ if factory is not None:
+ self.factory = factory
self.globals = sys._getframe(1).f_globals
def __call__(self, input, **kwargs):
filename = kwargs.get('filename')
- context = kwargs.get('context') or {}
- globals = kwargs.get('globals') or self.globals
+ context = kwargs.get('context') or {}
+ globals = kwargs.get('globals') or self.globals
template = self.factory()
template.convert(input, filename)
return template.render(context, globals=globals)
@@ -1042,7 +1136,7 @@ class TemplatePreprocessor(object):
class TrimPreprocessor(object):
- _rexp = re.compile(r'^[ \t]+<', re.M)
+ _rexp = re.compile(r'^[ \t]+<', re.M)
_rexp_all = re.compile(r'^[ \t]+', re.M)
def __init__(self, all=False):
@@ -1062,22 +1156,25 @@ class PrefixedLinePreprocessor(object):
self.regexp = re.compile(r'^([ \t]*)' + prefix + r'(.*)', re.M)
def convert_prefixed_lines(self, text):
- fn = lambda m: "%s<?py%s ?>" % (m.group(1), m.group(2))
+ def fn(m): return "%s<?py%s ?>" % (m.group(1), m.group(2))
return self.regexp.sub(fn, text)
STMT_REXP = re.compile(r'<\?py\s.*?\?>', re.S)
def __call__(self, input, **kwargs):
- buf = []; append = buf.append
+ buf = []
+ append = buf.append
pos = 0
for m in self.STMT_REXP.finditer(input):
text = input[pos:m.start()]
stmt = m.group(0)
pos = m.end()
- if text: append(self.convert_prefixed_lines(text))
+ if text:
+ append(self.convert_prefixed_lines(text))
append(stmt)
rest = input[pos:]
- if rest: append(self.convert_prefixed_lines(rest))
+ if rest:
+ append(self.convert_prefixed_lines(rest))
return "".join(buf)
@@ -1098,7 +1195,8 @@ class JavaScriptPreprocessor(object):
self._parse_chunks(input, buf, filename)
return ''.join(buf)
- CHUNK_REXP = re.compile(r'(?:^( *)<|<)!-- *#(?:JS: (\$?\w+(?:\.\w+)*\(.*?\))|/JS:?) *-->([ \t]*\r?\n)?', re.M)
+ CHUNK_REXP = re.compile(
+ r'(?:^( *)<|<)!-- *#(?:JS: (\$?\w+(?:\.\w+)*\(.*?\))|/JS:?) *-->([ \t]*\r?\n)?', re.M)
def _scan_chunks(self, input, filename):
rexp = self.CHUNK_REXP
@@ -1110,23 +1208,24 @@ class JavaScriptPreprocessor(object):
pos = m.end()
if funcdecl:
if curr_funcdecl:
- raise ParseError("%s is nested in %s. (file: %s, line: %s)" % \
- (funcdecl, curr_funcdecl, filename, _linenum(input, m.start()), ))
+ raise ParseError("%s is nested in %s. (file: %s, line: %s)" %
+ (funcdecl, curr_funcdecl, filename, _linenum(input, m.start()), ))
curr_funcdecl = funcdecl
else:
if not curr_funcdecl:
- raise ParseError("unexpected '<!-- #/JS -->'. (file: %s, line: %s)" % \
- (filename, _linenum(input, m.start()), ))
+ raise ParseError("unexpected '<!-- #/JS -->'. (file: %s, line: %s)" %
+ (filename, _linenum(input, m.start()), ))
curr_funcdecl = None
yield text, lspace, funcdecl, rspace, False
if curr_funcdecl:
- raise ParseError("%s is not closed by '<!-- #/JS -->'. (file: %s, line: %s)" % \
- (curr_funcdecl, filename, _linenum(input, m.start()), ))
+ raise ParseError("%s is not closed by '<!-- #/JS -->'. (file: %s, line: %s)" %
+ (curr_funcdecl, filename, _linenum(input, m.start()), ))
rest = input[pos:]
yield rest, None, None, None, True
def _parse_chunks(self, input, buf, filename=None):
- if not input: return
+ if not input:
+ return
stag = '<script'
if self._attrs:
for k in self._attrs:
@@ -1134,21 +1233,26 @@ class JavaScriptPreprocessor(object):
stag += '>'
etag = '</script>'
for text, lspace, funcdecl, rspace, end_p in self._scan_chunks(input, filename):
- if end_p: break
+ if end_p:
+ break
if funcdecl:
buf.append(text)
if re.match(r'^\$?\w+\(', funcdecl):
- buf.extend((lspace or '', stag, 'function ', funcdecl, "{var _buf='';", rspace or ''))
+ buf.extend((lspace or '', stag, 'function ',
+ funcdecl, "{var _buf='';", rspace or ''))
else:
m = re.match(r'(.+?)\((.*)\)', funcdecl)
- buf.extend((lspace or '', stag, m.group(1), '=function(', m.group(2), "){var _buf='';", rspace or ''))
+ buf.extend((lspace or '', stag, m.group(
+ 1), '=function(', m.group(2), "){var _buf='';", rspace or ''))
else:
self._parse_stmts(text, buf)
- buf.extend((lspace or '', "return _buf;};", etag, rspace or ''))
+ buf.extend(
+ (lspace or '', "return _buf;};", etag, rspace or ''))
#
buf.append(text)
- STMT_REXP = re.compile(r'(?:^( *)<|<)\?js(\s.*?) ?\?>([ \t]*\r?\n)?', re.M | re.S)
+ STMT_REXP = re.compile(
+ r'(?:^( *)<|<)\?js(\s.*?) ?\?>([ \t]*\r?\n)?', re.M | re.S)
def _scan_stmts(self, input):
rexp = self.STMT_REXP
@@ -1162,9 +1266,11 @@ class JavaScriptPreprocessor(object):
yield rest, None, None, None, True
def _parse_stmts(self, input, buf):
- if not input: return
+ if not input:
+ return
for text, lspace, code, rspace, end_p in self._scan_stmts(input):
- if end_p: break
+ if end_p:
+ break
if lspace is not None and rspace is not None:
self._parse_exprs(text, buf)
buf.extend((lspace, code, rspace))
@@ -1207,7 +1313,8 @@ class JavaScriptPreprocessor(object):
yield rest, None, None, True
def _parse_exprs(self, input, buf):
- if not input: return
+ if not input:
+ return
buf.append("_buf+=")
extend = buf.extend
op = ''
@@ -1231,7 +1338,7 @@ class JavaScriptPreprocessor(object):
def _escape_text(self, text):
lines = text.splitlines(True)
fn = self._escape_str
- s = "\\\n".join( fn(line) for line in lines )
+ s = "\\\n".join(fn(line) for line in lines)
return "".join(("'", s, "'"))
def _escape_str(self, string):
@@ -1251,9 +1358,8 @@ function _EF(c){return _ET[c];};
JS_FUNC = escaped.EscapedStr(JS_FUNC)
-
##
-## cache storages
+# cache storages
##
class CacheStorage(object):
@@ -1281,8 +1387,8 @@ class CacheStorage(object):
return self._store(cachepath, dct)
def _save_data_of(self, template):
- return { 'args' : template.args, 'bytecode' : template.bytecode,
- 'script': template.script, 'timestamp': template.timestamp }
+ return {'args': template.args, 'bytecode': template.bytecode,
+ 'script': template.script, 'timestamp': template.timestamp}
def unset(self, cachepath):
"""remove template object from dict and cache file."""
@@ -1298,15 +1404,18 @@ class CacheStorage(object):
def _load(self, cachepath):
"""(abstract) load dict object which represents template object attributes from cache file."""
- raise NotImplementedError.new("%s#_load(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError.new(
+ "%s#_load(): not implemented yet." % self.__class__.__name__)
def _store(self, cachepath, template):
"""(abstract) load dict object which represents template object attributes from cache file."""
- raise NotImplementedError.new("%s#_store(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError.new(
+ "%s#_store(): not implemented yet." % self.__class__.__name__)
def _delete(self, cachepath):
"""(abstract) remove template object from cache file."""
- raise NotImplementedError.new("%s#_delete(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError.new(
+ "%s#_delete(): not implemented yet." % self.__class__.__name__)
class MemoryCacheStorage(CacheStorage):
@@ -1324,21 +1433,28 @@ class MemoryCacheStorage(CacheStorage):
class FileCacheStorage(CacheStorage):
def _load(self, cachepath):
- if not _isfile(cachepath): return None
- if logger: logger.info("[tenjin.%s] load cache (file=%r)" % (self.__class__.__name__, cachepath))
+ if not _isfile(cachepath):
+ return None
+ if logger:
+ logger.info("[tenjin.%s] load cache (file=%r)" %
+ (self.__class__.__name__, cachepath))
data = _read_binary_file(cachepath)
return self._restore(data)
def _store(self, cachepath, dct):
- if logger: logger.info("[tenjin.%s] store cache (file=%r)" % (self.__class__.__name__, cachepath))
+ if logger:
+ logger.info("[tenjin.%s] store cache (file=%r)" %
+ (self.__class__.__name__, cachepath))
data = self._dump(dct)
_write_binary_file(cachepath, data)
def _restore(self, data):
- raise NotImplementedError("%s._restore(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError(
+ "%s._restore(): not implemented yet." % self.__class__.__name__)
def _dump(self, dct):
- raise NotImplementedError("%s._dump(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError(
+ "%s._dump(): not implemented yet." % self.__class__.__name__)
def _delete(self, cachepath):
_ignore_not_found_error(lambda: os.unlink(cachepath))
@@ -1376,16 +1492,20 @@ class TextCacheStorage(FileCacheStorage):
timestamp = encoding = args = None
for line in header.split("\n"):
key, val = line.split(": ", 1)
- if key == 'timestamp': timestamp = float(val)
- elif key == 'encoding': encoding = val
- elif key == 'args': args = val.split(', ')
- if encoding: script = script.decode(encoding) ## binary(=str) to unicode
+ if key == 'timestamp':
+ timestamp = float(val)
+ elif key == 'encoding':
+ encoding = val
+ elif key == 'args':
+ args = val.split(', ')
+ if encoding:
+ script = script.decode(encoding) # binary(=str) to unicode
return {'args': args, 'script': script, 'timestamp': timestamp}
def _dump(self, dct):
s = dct['script']
if dct.get('encoding') and isinstance(s, unicode):
- s = s.encode(dct['encoding']) ## unicode to binary(=str)
+ s = s.encode(dct['encoding']) # unicode to binary(=str)
sb = []
sb.append("timestamp: %s\n" % dct['timestamp'])
if dct.get('encoding'):
@@ -1397,7 +1517,8 @@ class TextCacheStorage(FileCacheStorage):
s = ''.join(sb)
if python3:
if isinstance(s, str):
- s = s.encode(dct.get('encoding') or 'utf-8') ## unicode(=str) to binary
+ # unicode(=str) to binary
+ s = s.encode(dct.get('encoding') or 'utf-8')
return s
def _save_data_of(self, template):
@@ -1406,27 +1527,30 @@ class TextCacheStorage(FileCacheStorage):
return dct
-
##
-## abstract class for data cache
+# abstract class for data cache
##
class KeyValueStore(object):
def get(self, key, *options):
- raise NotImplementedError("%s.get(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError(
+ "%s.get(): not implemented yet." % self.__class__.__name__)
def set(self, key, value, *options):
- raise NotImplementedError("%s.set(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError(
+ "%s.set(): not implemented yet." % self.__class__.__name__)
def delete(self, key, *options):
- raise NotImplementedError("%s.del(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError(
+ "%s.del(): not implemented yet." % self.__class__.__name__)
def has(self, key, *options):
- raise NotImplementedError("%s.has(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError(
+ "%s.has(): not implemented yet." % self.__class__.__name__)
##
-## memory base data cache
+# memory base data cache
##
class MemoryBaseStore(KeyValueStore):
@@ -1471,7 +1595,7 @@ class MemoryBaseStore(KeyValueStore):
##
-## file base data cache
+# file base data cache
##
class FileBaseStore(KeyValueStore):
@@ -1492,7 +1616,7 @@ class FileBaseStore(KeyValueStore):
def get(self, key, original_timestamp=None):
fpath = self.filepath(key)
- #if not _isfile(fpath): return None
+ # if not _isfile(fpath): return None
stat = _ignore_not_found_error(lambda: os.stat(fpath), None)
if stat is None:
return None
@@ -1505,9 +1629,9 @@ class FileBaseStore(KeyValueStore):
self.delete(key)
return None
if self.encoding:
- f = lambda: _read_text_file(fpath, self.encoding)
+ def f(): return _read_text_file(fpath, self.encoding)
else:
- f = lambda: _read_binary_file(fpath)
+ def f(): return _read_binary_file(fpath)
return _ignore_not_found_error(f, None)
def set(self, key, value, lifetime=0):
@@ -1538,20 +1662,21 @@ class FileBaseStore(KeyValueStore):
return True
-
##
-## html fragment cache helper class
+# html fragment cache helper class
##
class FragmentCacheHelper(object):
"""html fragment cache helper class."""
lifetime = 60 # 1 minute
- prefix = None
+ prefix = None
def __init__(self, store, lifetime=None, prefix=None):
self.store = store
- if lifetime is not None: self.lifetime = lifetime
- if prefix is not None: self.prefix = prefix
+ if lifetime is not None:
+ self.lifetime = lifetime
+ if prefix is not None:
+ self.prefix = prefix
def not_cached(self, cache_key, lifetime=None):
"""(obsolete. use cache_as() instead of this.)
@@ -1560,14 +1685,19 @@ class FragmentCacheHelper(object):
context['_cache_key'] = cache_key
key = self.prefix and self.prefix + cache_key or cache_key
value = self.store.get(key)
- if value: ## cached
- if logger: logger.debug('[tenjin.not_cached] %r: cached.' % (cache_key, ))
+ if value: # cached
+ if logger:
+ logger.debug('[tenjin.not_cached] %r: cached.' % (cache_key, ))
context[key] = value
return False
- else: ## not cached
- if logger: logger.debug('[tenjin.not_cached]: %r: not cached.' % (cache_key, ))
- if key in context: del context[key]
- if lifetime is None: lifetime = self.lifetime
+ else: # not cached
+ if logger:
+ logger.debug(
+ '[tenjin.not_cached]: %r: not cached.' % (cache_key, ))
+ if key in context:
+ del context[key]
+ if lifetime is None:
+ lifetime = self.lifetime
context['_cache_lifetime'] = lifetime
helpers.start_capture(cache_key, _depth=2)
return True
@@ -1579,9 +1709,9 @@ class FragmentCacheHelper(object):
context = f_locals['_context']
cache_key = context.pop('_cache_key')
key = self.prefix and self.prefix + cache_key or cache_key
- if key in context: ## cached
+ if key in context: # cached
value = context.pop(key)
- else: ## not cached
+ else: # not cached
value = helpers.stop_capture(False, _depth=2)
lifetime = context.pop('_cache_lifetime')
self.store.set(key, value, lifetime)
@@ -1596,31 +1726,36 @@ class FragmentCacheHelper(object):
_buf = sys._getframe(1).f_locals['_buf']
value = self.store.get(key)
if value:
- if logger: logger.debug('[tenjin.cache_as] %r: cache found.' % (cache_key, ))
+ if logger:
+ logger.debug('[tenjin.cache_as] %r: cache found.' %
+ (cache_key, ))
_buf.append(value)
else:
- if logger: logger.debug('[tenjin.cache_as] %r: expired or not cached yet.' % (cache_key, ))
+ if logger:
+ logger.debug(
+ '[tenjin.cache_as] %r: expired or not cached yet.' % (cache_key, ))
_buf_len = len(_buf)
yield None
value = ''.join(_buf[_buf_len:])
self.store.set(key, value, lifetime)
-## you can change default store by 'tenjin.helpers.fragment_cache.store = ...'
+
+# you can change default store by 'tenjin.helpers.fragment_cache.store = ...'
helpers.fragment_cache = FragmentCacheHelper(MemoryBaseStore())
-helpers.not_cached = helpers.fragment_cache.not_cached
+helpers.not_cached = helpers.fragment_cache.not_cached
helpers.echo_cached = helpers.fragment_cache.echo_cached
-helpers.cache_as = helpers.fragment_cache.cache_as
+helpers.cache_as = helpers.fragment_cache.cache_as
helpers.__all__.extend(('not_cached', 'echo_cached', 'cache_as'))
-
##
-## helper class to find and read template
+# helper class to find and read template
##
class Loader(object):
def exists(self, filepath):
- raise NotImplementedError("%s.exists(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError(
+ "%s.exists(): not implemented yet." % self.__class__.__name__)
def find(self, filename, dirs=None):
#: if dirs provided then search template file from it.
@@ -1637,18 +1772,20 @@ class Loader(object):
return None
def abspath(self, filename):
- raise NotImplementedError("%s.abspath(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError(
+ "%s.abspath(): not implemented yet." % self.__class__.__name__)
def timestamp(self, filepath):
- raise NotImplementedError("%s.timestamp(): not implemented yet." % self.__class__.__name__)
+ raise NotImplementedError(
+ "%s.timestamp(): not implemented yet." % self.__class__.__name__)
def load(self, filepath):
- raise NotImplementedError("%s.timestamp(): not implemented yet." % self.__class__.__name__)
-
+ raise NotImplementedError(
+ "%s.timestamp(): not implemented yet." % self.__class__.__name__)
##
-## helper class to find and read files
+# helper class to find and read files
##
class FileSystemLoader(Loader):
@@ -1676,7 +1813,8 @@ class FileSystemLoader(Loader):
mtime2 = _getmtime(filepath)
if mtime != mtime2:
if logger:
- logger.warn("[tenjin] %s.load(): timestamp is changed while reading file." % self.__class__.__name__)
+ logger.warn(
+ "[tenjin] %s.load(): timestamp is changed while reading file." % self.__class__.__name__)
return input, mtime
#: if file not exist, return None
return _ignore_not_found_error(f)
@@ -1689,9 +1827,8 @@ class TemplateNotFoundError(Exception):
pass
-
##
-## template engine class
+# template engine class
##
class Engine(object):
@@ -1701,15 +1838,15 @@ class Engine(object):
http://www.kuwata-lab.com/tenjin/pytenjin-examples.html
"""
- ## default value of attributes
- prefix = ''
- postfix = ''
- layout = None
+ # default value of attributes
+ prefix = ''
+ postfix = ''
+ layout = None
templateclass = Template
- path = None
- cache = TextCacheStorage() # save converted Python code into text file
- lang = None
- loader = FileSystemLoader()
+ path = None
+ cache = TextCacheStorage() # save converted Python code into text file
+ lang = None
+ loader = FileSystemLoader()
preprocess = False
preprocessorclass = Preprocessor
timestamp_interval = 1 # seconds
@@ -1743,18 +1880,30 @@ class Engine(object):
Options for Template class constructor.
See document of Template.__init__() for details.
"""
- if prefix: self.prefix = prefix
- if postfix: self.postfix = postfix
- if layout: self.layout = layout
- if templateclass: self.templateclass = templateclass
- if preprocessorclass: self.preprocessorclass = preprocessorclass
- if path is not None: self.path = path
- if lang is not None: self.lang = lang
- if loader is not None: self.loader = loader
- if preprocess is not None: self.preprocess = preprocess
- if pp is None: pp = []
- elif isinstance(pp, list): pass
- elif isinstance(pp, tuple): pp = list(pp)
+ if prefix:
+ self.prefix = prefix
+ if postfix:
+ self.postfix = postfix
+ if layout:
+ self.layout = layout
+ if templateclass:
+ self.templateclass = templateclass
+ if preprocessorclass:
+ self.preprocessorclass = preprocessorclass
+ if path is not None:
+ self.path = path
+ if lang is not None:
+ self.lang = lang
+ if loader is not None:
+ self.loader = loader
+ if preprocess is not None:
+ self.preprocess = preprocess
+ if pp is None:
+ pp = []
+ elif isinstance(pp, list):
+ pass
+ elif isinstance(pp, tuple):
+ pp = list(pp)
else:
raise TypeError("'pp' expected to be a list but got %r." % (pp,))
self.pp = pp
@@ -1798,7 +1947,7 @@ class Engine(object):
'list'
"""
#: if template_name starts with ':', add prefix and postfix to it.
- if template_name[0] == ':' :
+ if template_name[0] == ':':
return self.prefix + template_name[1:] + self.postfix
#: if template_name doesn't start with ':', just return it.
return template_name
@@ -1816,12 +1965,13 @@ class Engine(object):
#if _globals is None: _globals = sys._getframe(3).f_globals
#: preprocess template and return result
#preprocessor = self.preprocessorclass(filepath, input=input)
- #return preprocessor.render(_context, globals=_globals)
+ # return preprocessor.render(_context, globals=_globals)
#: preprocesses input with _context and returns result.
if '_engine' not in _context:
self.hook_context(_context)
for pp in self.pp:
- input = pp.__call__(input, filename=filepath, context=_context, globals=_globals)
+ input = pp.__call__(input, filename=filepath,
+ context=_context, globals=_globals)
return input
def add_template(self, template):
@@ -1837,7 +1987,7 @@ class Engine(object):
now = _time()
last_checked = getattr(template, '_last_checked_at', None)
if last_checked and now < last_checked + self.timestamp_interval:
- #if logger: logger.trace('[tenjin.%s] timestamp check skipped (%f < %f + %f)' % \
+ # if logger: logger.trace('[tenjin.%s] timestamp check skipped (%f < %f + %f)' % \
# (self.__class__.__name__, now, template._last_checked_at, self.timestamp_interval))
return template
#: if timestamp of template objectis same as file, return it.
@@ -1845,9 +1995,10 @@ class Engine(object):
template._last_checked_at = now
return template
#: if timestamp of template object is different from file, clear it
- #cache._delete(cachepath)
- if logger: logger.info("[tenjin.%s] cache expired (filepath=%r)" % \
- (self.__class__.__name__, filepath))
+ # cache._delete(cachepath)
+ if logger:
+ logger.info("[tenjin.%s] cache expired (filepath=%r)" %
+ (self.__class__.__name__, filepath))
return None
def get_template(self, template_name, _context=None, _globals=None):
@@ -1868,7 +2019,8 @@ class Engine(object):
#: if template file is not found then raise TemplateNotFoundError.
filepath = self.loader.find(filename, self.path)
if not filepath:
- raise TemplateNotFoundError('%s: filename not found (path=%r).' % (filename, self.path))
+ raise TemplateNotFoundError(
+ '%s: filename not found (path=%r).' % (filename, self.path))
#
fullpath = self.loader.abspath(filepath)
self._filepaths[filename] = (filepath, fullpath)
@@ -1876,19 +2028,24 @@ class Engine(object):
cachepath = self.cachename(fullpath)
#: get template object from cache
cache = self.cache
- template = cache and self._get_template_from_cache(cachepath, filepath) or None
+ template = cache and self._get_template_from_cache(
+ cachepath, filepath) or None
#: if template object is not found in cache or is expired...
if not template:
ret = self.loader.load(filepath)
if not ret:
- raise TemplateNotFoundError("%r: template not found." % filepath)
+ raise TemplateNotFoundError(
+ "%r: template not found." % filepath)
input, timestamp = ret
- if self.pp: ## required for preprocessing
- if _context is None: _context = {}
- if _globals is None: _globals = sys._getframe(1).f_globals
+ if self.pp: # required for preprocessing
+ if _context is None:
+ _context = {}
+ if _globals is None:
+ _globals = sys._getframe(1).f_globals
input = self._preprocess(input, filepath, _context, _globals)
#: create template object.
- template = self._create_template(input, filepath, _context, _globals)
+ template = self._create_template(
+ input, filepath, _context, _globals)
#: set timestamp and filename of template object.
template.timestamp = timestamp
template._last_checked_at = _time()
@@ -1896,10 +2053,12 @@ class Engine(object):
if cache:
if not template.bytecode:
#: ignores syntax error when compiling.
- try: template.compile()
- except SyntaxError: pass
+ try:
+ template.compile()
+ except SyntaxError:
+ pass
cache.set(cachepath, template)
- #else:
+ # else:
# template.compile()
#:
template.filename = filepath
@@ -1921,7 +2080,7 @@ class Engine(object):
"""
#: get local and global vars of caller.
frame = sys._getframe(1)
- locals = frame.f_locals
+ locals = frame.f_locals
globals = frame.f_globals
#: get _context from caller's local vars.
assert '_context' in locals
@@ -1930,12 +2089,14 @@ class Engine(object):
if kwargs:
context.update(kwargs)
#: get template object with context data and global vars.
- ## (context and globals are passed to get_template() only for preprocessing.)
+ # (context and globals are passed to get_template() only for preprocessing.)
template = self.get_template(template_name, context, globals)
#: if append_to_buf is true then add output to _buf.
#: if append_to_buf is false then don't add output to _buf.
- if append_to_buf: _buf = locals['_buf']
- else: _buf = None
+ if append_to_buf:
+ _buf = locals['_buf']
+ else:
+ _buf = None
#: render template and return output.
s = template.render(context, globals, _buf=_buf)
#: kwargs are removed from context data.
@@ -1967,10 +2128,10 @@ class Engine(object):
globals = sys._getframe(1).f_globals
self.hook_context(context)
while True:
- ## context and globals are passed to get_template() only for preprocessing
+ # context and globals are passed to get_template() only for preprocessing
template = self.get_template(template_name, context, globals)
- content = template.render(context, globals)
- layout = context.pop('_layout', layout)
+ content = template.render(context, globals)
+ layout = context.pop('_layout', layout)
if layout is True or layout is None:
layout = self.layout
if not layout:
@@ -1990,7 +2151,7 @@ class Engine(object):
##
-## safe template and engine
+# safe template and engine
##
class SafeTemplate(Template):
@@ -1998,7 +2159,7 @@ class SafeTemplate(Template):
'#{...}' is not allowed with this class. Use '[==...==]' instead.
"""
- tostrfunc = 'to_str'
+ tostrfunc = 'to_str'
escapefunc = 'to_escaped'
def get_expr_and_flags(self, match):
@@ -2007,7 +2168,7 @@ class SafeTemplate(Template):
class SafePreprocessor(Preprocessor):
- tostrfunc = 'to_str'
+ tostrfunc = 'to_str'
escapefunc = 'to_escaped'
def get_expr_and_flags(self, match):
@@ -2018,20 +2179,23 @@ def _get_expr_and_flags(match, errmsg):
expr1, expr2, expr3, expr4 = match.groups()
if expr1 is not None:
raise TemplateSyntaxError(errmsg % match.group(1))
- if expr2 is not None: return expr2, (True, False) # #{...} : call escape, not to_str
- if expr3 is not None: return expr3, (False, True) # [==...==] : not escape, call to_str
- if expr4 is not None: return expr4, (True, False) # [=...=] : call escape, not to_str
+ if expr2 is not None:
+ return expr2, (True, False) # #{...} : call escape, not to_str
+ if expr3 is not None:
+ return expr3, (False, True) # [==...==] : not escape, call to_str
+ if expr4 is not None:
+ return expr4, (True, False) # [=...=] : call escape, not to_str
class SafeEngine(Engine):
- templateclass = SafeTemplate
+ templateclass = SafeTemplate
preprocessorclass = SafePreprocessor
##
-## for Google App Engine
-## (should separate into individual file or module?)
+# for Google App Engine
+# (should separate into individual file or module?)
##
def _dummy():
@@ -2045,44 +2209,55 @@ def _dummy():
def __init__(self, lifetime=None, namespace=None):
CacheStorage.__init__(self)
- if lifetime is not None: self.lifetime = lifetime
+ if lifetime is not None:
+ self.lifetime = lifetime
self.namespace = namespace
def _load(self, cachepath):
key = cachepath
- if _tenjin.logger: _tenjin.logger.info("[tenjin.gae.GaeMemcacheCacheStorage] load cache (key=%r)" % (key, ))
+ if _tenjin.logger:
+ _tenjin.logger.info(
+ "[tenjin.gae.GaeMemcacheCacheStorage] load cache (key=%r)" % (key, ))
return memcache.get(key, namespace=self.namespace)
def _store(self, cachepath, dct):
dct.pop('bytecode', None)
key = cachepath
- if _tenjin.logger: _tenjin.logger.info("[tenjin.gae.GaeMemcacheCacheStorage] store cache (key=%r)" % (key, ))
- ret = memcache.set(key, dct, self.lifetime, namespace=self.namespace)
+ if _tenjin.logger:
+ _tenjin.logger.info(
+ "[tenjin.gae.GaeMemcacheCacheStorage] store cache (key=%r)" % (key, ))
+ ret = memcache.set(key, dct, self.lifetime,
+ namespace=self.namespace)
if not ret:
- if _tenjin.logger: _tenjin.logger.info("[tenjin.gae.GaeMemcacheCacheStorage] failed to store cache (key=%r)" % (key, ))
+ if _tenjin.logger:
+ _tenjin.logger.info(
+ "[tenjin.gae.GaeMemcacheCacheStorage] failed to store cache (key=%r)" % (key, ))
def _delete(self, cachepath):
key = cachepath
memcache.delete(key, namespace=self.namespace)
-
class GaeMemcacheStore(KeyValueStore):
lifetime = 0
def __init__(self, lifetime=None, namespace=None):
- if lifetime is not None: self.lifetime = lifetime
+ if lifetime is not None:
+ self.lifetime = lifetime
self.namespace = namespace
def get(self, key):
return memcache.get(key, namespace=self.namespace)
def set(self, key, value, lifetime=None):
- if lifetime is None: lifetime = self.lifetime
+ if lifetime is None:
+ lifetime = self.lifetime
if memcache.set(key, value, lifetime, namespace=self.namespace):
return True
else:
- if _tenjin.logger: _tenjin.logger.info("[tenjin.gae.GaeMemcacheStore] failed to set (key=%r)" % (key, ))
+ if _tenjin.logger:
+ _tenjin.logger.info(
+ "[tenjin.gae.GaeMemcacheStore] failed to set (key=%r)" % (key, ))
return False
def delete(self, key):
@@ -2095,19 +2270,19 @@ def _dummy():
else:
return True
-
def init():
global memcache, _tenjin
if not memcache:
from google.appengine.api import memcache
- if not _tenjin: import tenjin as _tenjin
- ## avoid cache confliction between versions
- ver = os.environ.get('CURRENT_VERSION_ID', '1.1')#.split('.')[0]
+ if not _tenjin:
+ import tenjin as _tenjin
+ # avoid cache confliction between versions
+ ver = os.environ.get('CURRENT_VERSION_ID', '1.1') # .split('.')[0]
Engine.cache = GaeMemcacheCacheStorage(namespace=ver)
- ## set fragment cache store
- helpers.fragment_cache.store = GaeMemcacheStore(namespace=ver)
- helpers.fragment_cache.lifetime = 60 # 1 minute
- helpers.fragment_cache.prefix = 'fragment.'
+ # set fragment cache store
+ helpers.fragment_cache.store = GaeMemcacheStore(namespace=ver)
+ helpers.fragment_cache.lifetime = 60 # 1 minute
+ helpers.fragment_cache.prefix = 'fragment.'
gae = create_module('tenjin.gae', _dummy,