home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2012 January / maximum-cd-2012-01.iso / DiscContents / digsby_setup.exe / lib / tenjin.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2011-10-05  |  32.1 KB  |  1,280 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.6)
  3.  
  4. __release__ = '0.8.1'
  5. __license__ = 'MIT License'
  6. __all__ = [
  7.     'Template',
  8.     'Engine',
  9.     'helpers',
  10.     'html']
  11. import re
  12. import sys
  13. import os
  14. import time
  15. import marshal
  16. python3 = sys.version_info[0] == 3
  17. python2 = sys.version_info[0] == 2
  18. logger = None
  19.  
  20. def _write_binary_file(filename, content):
  21.     f = None
  22.     
  23.     try:
  24.         import random
  25.         tmpfile = filename + str(random.random())[1:]
  26.         f = open(tmpfile, 'wb')
  27.         f.write(content)
  28.     finally:
  29.         if f:
  30.             f.close()
  31.             os.rename(tmpfile, filename)
  32.         
  33.  
  34.  
  35.  
  36. def _read_binary_file(filename):
  37.     f = None
  38.     
  39.     try:
  40.         f = open(filename, 'rb')
  41.         return f.read()
  42.     finally:
  43.         if f:
  44.             f.close()
  45.         
  46.  
  47.  
  48. if python2:
  49.     
  50.     def _read_template_file(filename, encoding = None):
  51.         s = _read_binary_file(filename)
  52.         if encoding:
  53.             s = s.decode(encoding)
  54.         
  55.         return s
  56.  
  57. elif python3:
  58.     
  59.     def _read_template_file(filename, encoding = None):
  60.         s = _read_binary_file(filename)
  61.         if not encoding:
  62.             pass
  63.         return s.decode('utf-8')
  64.  
  65.  
  66.  
  67. def _create_module(module_name):
  68.     import types
  69.     mod = types.ModuleType(module_name)
  70.     mod.__file__ = __file__
  71.     sys.modules[module_name] = mod
  72.     return mod
  73.  
  74.  
  75. def _create_helpers_module():
  76.     if python2:
  77.         
  78.         def generate_tostrfunc(encode = None, decode = None):
  79.             if encode:
  80.                 if decode:
  81.                     raise ValueError("can't specify both encode and decode encoding.")
  82.                 decode
  83.                 
  84.                 def to_str(val):
  85.                     if val is None:
  86.                         return ''
  87.                     if isinstance(val, str):
  88.                         return val
  89.                     if isinstance(val, unicode):
  90.                         return val.encode(encode)
  91.                     return str(val)
  92.  
  93.             elif decode:
  94.                 
  95.                 def to_str(val):
  96.                     if val is None:
  97.                         return ''
  98.                     if isinstance(val, str):
  99.                         return val.decode(decode)
  100.                     if isinstance(val, unicode):
  101.                         return val
  102.                     return unicode(val)
  103.  
  104.             else:
  105.                 
  106.                 def to_str(val):
  107.                     if val is None:
  108.                         return ''
  109.                     if isinstance(val, str):
  110.                         return val
  111.                     if isinstance(val, unicode):
  112.                         return val
  113.                     return str(val)
  114.  
  115.             return to_str
  116.  
  117.     elif python3:
  118.         
  119.         def generate_tostrfunc(decode = None, encode = None):
  120.             if encode:
  121.                 if decode:
  122.                     raise ValueError("can't specify both encode and decode encoding.")
  123.                 decode
  124.                 
  125.                 def to_str(val):
  126.                     if val is None:
  127.                         return ''
  128.                     if isinstance(val, str):
  129.                         return val.encode(encode)
  130.                     if isinstance(val, bytes):
  131.                         return val
  132.                     return str(val).encode(encode)
  133.  
  134.             elif decode:
  135.                 
  136.                 def to_str(val):
  137.                     if val is None:
  138.                         return ''
  139.                     if isinstance(val, str):
  140.                         return val
  141.                     if isinstance(val, bytes):
  142.                         return val.decode(decode)
  143.                     return str(val)
  144.  
  145.             else:
  146.                 
  147.                 def to_str(val):
  148.                     if val is None:
  149.                         return ''
  150.                     if isinstance(val, str):
  151.                         return val
  152.                     if isinstance(val, bytes):
  153.                         return val
  154.                     return str(val)
  155.  
  156.             return to_str
  157.  
  158.     
  159.     if python2:
  160.         to_str = generate_tostrfunc(encode = 'utf-8')
  161.     elif python3:
  162.         to_str = generate_tostrfunc(decode = 'utf-8')
  163.     
  164.     
  165.     def echo(string):
  166.         frame = sys._getframe(1)
  167.         context = frame.f_locals
  168.         context['_buf'].append(string)
  169.  
  170.     
  171.     def start_capture(varname = None):
  172.         frame = sys._getframe(1)
  173.         context = frame.f_locals
  174.         context['_buf_tmp'] = context['_buf']
  175.         context['_capture_varname'] = varname
  176.         context['_buf'] = []
  177.  
  178.     
  179.     def stop_capture(store_to_context = True):
  180.         frame = sys._getframe(1)
  181.         context = frame.f_locals
  182.         result = ''.join(context['_buf'])
  183.         context['_buf'] = context.pop('_buf_tmp')
  184.         varname = context.pop('_capture_varname')
  185.         if varname:
  186.             context[varname] = result
  187.             if store_to_context:
  188.                 context['_context'][varname] = result
  189.             
  190.         
  191.         return result
  192.  
  193.     
  194.     def captured_as(name):
  195.         frame = sys._getframe(1)
  196.         context = frame.f_locals
  197.         if name in context:
  198.             _buf = context['_buf']
  199.             _buf.append(context[name])
  200.             return True
  201.         return False
  202.  
  203.     
  204.     def _p(arg):
  205.         return '<`#%s#`>' % arg
  206.  
  207.     
  208.     def _P(arg):
  209.         return '<`$%s$`>' % arg
  210.  
  211.     
  212.     def _decode_params(s):
  213.         import urllib
  214.         if python2:
  215.             unquote = unquote
  216.             import urllib
  217.         elif python3:
  218.             unquote = unquote
  219.             import urllib.parse
  220.         
  221.         dct = {
  222.             'lt': '<',
  223.             'gt': '>',
  224.             'amp': '&',
  225.             'quot': '"',
  226.             '#039': "'" }
  227.         
  228.         def unescape(s):
  229.             return re.sub(('&(lt|gt|quot|amp|#039);',), (lambda m: dct[m.group(1)]), s)
  230.  
  231.         s = to_str(s)
  232.         s = re.sub(('%3C%60%23(.*?)%23%60%3E',), (lambda m: '#{%s}' % unquote(m.group(1))), s)
  233.         s = re.sub(('%3C%60%24(.*?)%24%60%3E',), (lambda m: '${%s}' % unquote(m.group(1))), s)
  234.         s = re.sub(('<`#(.*?)#`>',), (lambda m: '#{%s}' % unescape(m.group(1))), s)
  235.         s = re.sub(('<`\\$(.*?)\\$`>',), (lambda m: '${%s}' % unescape(m.group(1))), s)
  236.         s = re.sub('<`#(.*?)#`>', '#{\\1}', s)
  237.         s = re.sub('<`\\$(.*?)\\$`>', '${\\1}', s)
  238.         return s
  239.  
  240.     mod = _create_module('tenjin.helpers')
  241.     mod.to_str = to_str
  242.     mod.generate_tostrfunc = generate_tostrfunc
  243.     mod.echo = echo
  244.     mod.start_capture = start_capture
  245.     mod.stop_capture = stop_capture
  246.     mod.captured_as = captured_as
  247.     mod._p = _p
  248.     mod._P = _P
  249.     mod._decode_params = _decode_params
  250.     mod.__all__ = [
  251.         'escape',
  252.         'to_str',
  253.         'echo',
  254.         'generate_tostrfunc',
  255.         'start_capture',
  256.         'stop_capture',
  257.         'captured_as',
  258.         '_p',
  259.         '_P',
  260.         '_decode_params']
  261.     return mod
  262.  
  263. helpers = _create_helpers_module()
  264. del _create_helpers_module
  265. generate_tostrfunc = helpers.generate_tostrfunc
  266.  
  267. def _create_html_module():
  268.     to_str = helpers.to_str
  269.     _escape_table = {
  270.         '&': '&',
  271.         '<': '<',
  272.         '>': '>',
  273.         '"': '"' }
  274.     _escape_pattern = re.compile('[&<>"]')
  275.     
  276.     _escape_callable = lambda m: _escape_table[m.group(0)]
  277.     
  278.     def escape_xml(s):
  279.         return _escape_pattern.sub(_escape_callable, s)
  280.  
  281.     
  282.     def tagattr(name, expr, value = (None, (None,)), escape = (None, True)):
  283.         if not expr:
  284.             return ''
  285.         if value is None:
  286.             value = expr
  287.         
  288.         if escape:
  289.             value = escape_xml(to_str(value))
  290.         
  291.         return ' %s="%s"' % (name, value)
  292.  
  293.     
  294.     def tagattrs(**kwargs):
  295.         if 'klass' in kwargs:
  296.             kwargs['class'] = kwargs.pop('klass')
  297.         
  298.         if 'checked' in kwargs:
  299.             if not kwargs.pop('checked') or 'checked':
  300.                 pass
  301.             kwargs['checked'] = None
  302.         
  303.         if 'selected' in kwargs:
  304.             if not kwargs.pop('selected') or 'selected':
  305.                 pass
  306.             kwargs['selected'] = None
  307.         
  308.         if 'disabled' in kwargs:
  309.             if not kwargs.pop('disabled') or 'disabled':
  310.                 pass
  311.             kwargs['disabled'] = None
  312.         
  313.         return [](_[1])
  314.  
  315.     
  316.     def checked(expr):
  317.         if not expr or ' checked="checked"':
  318.             pass
  319.         return ''
  320.  
  321.     
  322.     def selected(expr):
  323.         if not expr or ' selected="selected"':
  324.             pass
  325.         return ''
  326.  
  327.     
  328.     def disabled(expr):
  329.         if not expr or ' disabled="disabled"':
  330.             pass
  331.         return ''
  332.  
  333.     
  334.     def nl2br(text):
  335.         if not text:
  336.             return ''
  337.         return text.replace('\n', '<br />\n')
  338.  
  339.     
  340.     def text2html(text):
  341.         if not text:
  342.             return ''
  343.         return nl2br(escape_xml(text).replace('  ', '  '))
  344.  
  345.     
  346.     def nv(name, value, sep = ((None, (None, None)), None), **kwargs):
  347.         if not sep or 'name="%s" value="%s" id="%s"' % (name, value, name + sep + value):
  348.             pass
  349.         s = 'name="%s" value="%s"' % (name, escape_xml(value))
  350.         if not kwargs or s + tagattrs(**kwargs):
  351.             pass
  352.         return s
  353.  
  354.     
  355.     def new_cycle(*values):
  356.         
  357.         def gen(values):
  358.             n = len(values)
  359.             i = 0
  360.             while True:
  361.                 yield values[i]
  362.                 i = (i + 1) % n
  363.  
  364.         if python2:
  365.             return gen(values).next
  366.         if python3:
  367.             return gen(values).__next__
  368.  
  369.     mod = _create_module('tenjin.helpers.html')
  370.     mod._escape_table = _escape_table
  371.     mod.escape_xml = escape_xml
  372.     mod.escape = escape_xml
  373.     mod.tagattr = tagattr
  374.     mod.tagattrs = tagattrs
  375.     mod.checked = checked
  376.     mod.selected = selected
  377.     mod.disabled = disabled
  378.     mod.nl2br = nl2br
  379.     mod.text2html = text2html
  380.     mod.nv = nv
  381.     mod.new_cycle = new_cycle
  382.     return mod
  383.  
  384. helpers.html = _create_html_module()
  385. del _create_html_module
  386. helpers.escape = helpers.html.escape_xml
  387.  
  388. class Template(object):
  389.     filename = None
  390.     encoding = None
  391.     escapefunc = 'escape'
  392.     tostrfunc = 'to_str'
  393.     indent = 4
  394.     preamble = None
  395.     postamble = None
  396.     smarttrim = None
  397.     args = None
  398.     timestamp = None
  399.     
  400.     def __init__(self, filename = None, encoding = None, escapefunc = None, tostrfunc = None, indent = None, preamble = None, postamble = None, smarttrim = None):
  401.         if encoding is not None:
  402.             self.encoding = encoding
  403.         
  404.         if escapefunc is not None:
  405.             self.escapefunc = escapefunc
  406.         
  407.         if tostrfunc is not None:
  408.             self.tostrfunc = tostrfunc
  409.         
  410.         if indent is not None:
  411.             self.indent = indent
  412.         
  413.         if preamble is not None:
  414.             self.preamble = preamble
  415.         
  416.         if postamble is not None:
  417.             self.postamble = postamble
  418.         
  419.         if smarttrim is not None:
  420.             self.smarttrim = smarttrim
  421.         
  422.         if preamble is True:
  423.             self.preamble = '_buf = []'
  424.         
  425.         if postamble is True:
  426.             self.postamble = "print ''.join(_buf)"
  427.         
  428.         if filename:
  429.             self.convert_file(filename)
  430.         else:
  431.             self._reset()
  432.  
  433.     
  434.     def _reset(self, input = None, filename = None):
  435.         self._spaces = ''
  436.         self.script = None
  437.         self.bytecode = None
  438.         self.input = input
  439.         self.filename = filename
  440.         if input != None:
  441.             i = input.find('\n')
  442.             if i < 0:
  443.                 self.newline = '\n'
  444.             elif len(input) >= 2 and input[i - 1] == '\r':
  445.                 self.newline = '\r\n'
  446.             else:
  447.                 self.newline = '\n'
  448.         
  449.         self._stmt_not_added_yet = True
  450.  
  451.     
  452.     def before_convert(self, buf):
  453.         if self.preamble:
  454.             buf.append(self.preamble)
  455.             if not self.input.startswith('<?py') or '\n':
  456.                 pass
  457.             buf.append('; ')
  458.         
  459.  
  460.     
  461.     def after_convert(self, buf):
  462.         if self.postamble:
  463.             if not buf[-1].endswith('\n'):
  464.                 buf.append('\n')
  465.             
  466.             buf.append(self.postamble + '\n')
  467.         
  468.  
  469.     
  470.     def convert_file(self, filename):
  471.         input = _read_template_file(filename)
  472.         return self.convert(input, filename)
  473.  
  474.     
  475.     def convert(self, input, filename = None):
  476.         if python2:
  477.             if self.encoding and isinstance(input, str):
  478.                 input = input.decode(self.encoding)
  479.             
  480.         
  481.         self._reset(input, filename)
  482.         buf = []
  483.         self.before_convert(buf)
  484.         self.parse_stmts(buf, input)
  485.         self.after_convert(buf)
  486.         script = ''.join(buf)
  487.         self.script = script
  488.         return script
  489.  
  490.     
  491.     def compile_stmt_pattern(pi):
  492.         return re.compile('<\\?%s( |\\t|\\r?\\n)(.*?) ?\\?>([ \\t]*\\r?\\n)?' % pi, re.S)
  493.  
  494.     STMT_PATTERN = None
  495.     compile_stmt_pattern = staticmethod(compile_stmt_pattern)
  496.     
  497.     def stmt_pattern(self):
  498.         pat = Template.STMT_PATTERN
  499.         if not pat:
  500.             pat = Template.STMT_PATTERN = Template.compile_stmt_pattern('py')
  501.         
  502.         return pat
  503.  
  504.     
  505.     def parse_stmts(self, buf, input):
  506.         if not input:
  507.             return None
  508.         rexp = self.stmt_pattern()
  509.         is_bol = True
  510.         index = 0
  511.         for m in rexp.finditer(input):
  512.             (mspace, code, rspace) = m.groups()
  513.             text = input[index:m.start()]
  514.             index = m.end()
  515.             lspace = None
  516.             if text == '':
  517.                 if is_bol:
  518.                     lspace = ''
  519.                 
  520.             elif text[-1] == '\n':
  521.                 lspace = ''
  522.             else:
  523.                 rindex = text.rfind('\n')
  524.                 if rindex < 0:
  525.                     if is_bol and text.isspace():
  526.                         lspace = text
  527.                         text = ''
  528.                     
  529.                 else:
  530.                     s = text[rindex + 1:]
  531.                     if s.isspace():
  532.                         lspace = s
  533.                         text = text[:rindex + 1]
  534.                     
  535.             self.parse_exprs(buf, text, is_bol)
  536.             is_bol = rspace is not None
  537.             if lspace:
  538.                 buf.append(lspace)
  539.             
  540.             if mspace != ' ':
  541.                 if not mspace == '\t' or '\t':
  542.                     pass
  543.                 buf.append('\n')
  544.             
  545.             if code:
  546.                 code = self.statement_hook(code)
  547.                 self.add_stmt(buf, code)
  548.             
  549.             self._set_spaces(code, lspace, mspace)
  550.             if rspace:
  551.                 buf.append('\n')
  552.                 continue
  553.         
  554.         rest = input[index:]
  555.         if rest:
  556.             self.parse_exprs(buf, rest)
  557.         
  558.  
  559.     
  560.     def statement_hook(self, stmt):
  561.         if self.args is None:
  562.             args_pattern = '^ *#@ARGS(?:[ \\t]+(.*?))?$'
  563.             m = re.match(args_pattern, stmt)
  564.             if m:
  565.                 if not m.group(1):
  566.                     pass
  567.                 arr = ''.split(',')
  568.                 args = []
  569.                 declares = []
  570.                 for s in arr:
  571.                     arg = s.strip()
  572.                     if not s:
  573.                         continue
  574.                     
  575.                     if not re.match('^[a-zA-Z_]\\w*$', arg):
  576.                         raise ValueError('%s: invalid template argument.' % arg)
  577.                     re.match('^[a-zA-Z_]\\w*$', arg)
  578.                     args.append(arg)
  579.                     declares.append("%s = _context.get('%s'); " % (arg, arg))
  580.                 
  581.                 self.args = args
  582.                 return ''.join(declares)
  583.         
  584.         return stmt
  585.  
  586.     EXPR_PATTERN = None
  587.     
  588.     def expr_pattern(self):
  589.         pat = Template.EXPR_PATTERN
  590.         if not pat:
  591.             pat = Template.EXPR_PATTERN = re.compile('([#$])\\{(.*?)\\}', re.S)
  592.         
  593.         return pat
  594.  
  595.     
  596.     def get_expr_and_escapeflag(self, match):
  597.         return (match.group(2), match.group(1) == '$')
  598.  
  599.     
  600.     def parse_exprs(self, buf, input, is_bol = False):
  601.         if not input:
  602.             return None
  603.         if self._spaces:
  604.             buf.append(self._spaces)
  605.         
  606.         self.start_text_part(buf)
  607.         rexp = self.expr_pattern()
  608.         smarttrim = self.smarttrim
  609.         nl = self.newline
  610.         nl_len = len(nl)
  611.         pos = 0
  612.         for m in rexp.finditer(input):
  613.             start = m.start()
  614.             text = input[pos:start]
  615.             pos = m.end()
  616.             (expr, flag_escape) = self.get_expr_and_escapeflag(m)
  617.             if text:
  618.                 self.add_text(buf, text)
  619.             
  620.             self.add_expr(buf, expr, flag_escape)
  621.             if smarttrim:
  622.                 if not text.endswith(nl) and not text and start > 0:
  623.                     pass
  624.                 flag_bol = is_bol
  625.                 if flag_bol and not flag_escape and input[pos:pos + nl_len] == nl:
  626.                     pos += nl_len
  627.                     buf.append('\n')
  628.                 
  629.             input[pos:pos + nl_len] == nl
  630.         
  631.         if smarttrim:
  632.             if buf and buf[-1] == '\n':
  633.                 buf.pop()
  634.             
  635.         
  636.         rest = input[pos:]
  637.         if rest:
  638.             self.add_text(buf, rest, True)
  639.         
  640.         self.stop_text_part(buf)
  641.         if input[-1] == '\n':
  642.             buf.append('\n')
  643.         
  644.  
  645.     
  646.     def start_text_part(self, buf):
  647.         buf.append('_buf.extend((')
  648.  
  649.     
  650.     def stop_text_part(self, buf):
  651.         buf.append('));')
  652.  
  653.     _quote_rexp = None
  654.     
  655.     def add_text(self, buf, text, encode_newline = False):
  656.         if not text:
  657.             return None
  658.         if self.encoding and python2:
  659.             buf.append("u'''")
  660.         else:
  661.             buf.append("'''")
  662.         rexp = Template._quote_rexp
  663.         if not rexp:
  664.             rexp = Template._quote_rexp = re.compile("(['\\\\\\\\])")
  665.         
  666.         text = rexp.sub('\\\\\\1', text)
  667.         if not encode_newline or text[-1] != '\n':
  668.             buf.append(text)
  669.             buf.append("''', ")
  670.         elif len(text) >= 2 and text[-2] == '\r':
  671.             buf.append(text[0:-2])
  672.             buf.append("\\r\\n''', ")
  673.         else:
  674.             buf.append(text[0:-1])
  675.             buf.append("\\n''', ")
  676.  
  677.     _add_text = add_text
  678.     
  679.     def add_expr(self, buf, code, flag_escape = None):
  680.         if not code or code.isspace():
  681.             return None
  682.         if flag_escape is None:
  683.             buf.append(code)
  684.             buf.append(', ')
  685.         elif flag_escape is False:
  686.             buf.extend((self.tostrfunc, '(', code, '), '))
  687.         else:
  688.             buf.extend((self.escapefunc, '(', self.tostrfunc, '(', code, ')), '))
  689.  
  690.     
  691.     def add_stmt(self, buf, code):
  692.         if self._stmt_not_added_yet:
  693.             if buf and buf[-1] != '\n' and buf[-1].isspace():
  694.                 buf[-1:-1] = ('if True: ## dummy\n',)
  695.             
  696.             self._stmt_not_added_yet = False
  697.         
  698.         if self.newline == '\r\n':
  699.             code = code.replace('\r\n', '\n')
  700.         
  701.         buf.append(code)
  702.  
  703.     
  704.     def _set_spaces(self, code, lspace, mspace):
  705.         if lspace:
  706.             if mspace == ' ':
  707.                 code = lspace + code
  708.             elif mspace == '\t':
  709.                 code = lspace + '\t' + code
  710.             
  711.         
  712.         i = code.rstrip().rfind('\n') + 1
  713.         indent = 0
  714.         n = len(code)
  715.         ch = None
  716.         while i < n:
  717.             ch = code[i]
  718.             if ch == ' ':
  719.                 indent += 1
  720.             elif ch == '\t':
  721.                 indent += 8
  722.             else:
  723.                 break
  724.             i += 1
  725.         if ch:
  726.             if code.rstrip()[-1] == ':':
  727.                 indent += self.indent
  728.             
  729.             self._spaces = ' ' * indent
  730.         
  731.  
  732.     
  733.     def render(self, context = None, globals = None, _buf = None):
  734.         if context is None:
  735.             locals = context = { }
  736.         elif self.args is None:
  737.             locals = context.copy()
  738.         else:
  739.             locals = { }
  740.             if '_engine' in context:
  741.                 context.get('_engine').hook_context(locals)
  742.             
  743.         locals['_context'] = context
  744.         if globals is None:
  745.             globals = sys._getframe(1).f_globals
  746.         
  747.         bufarg = _buf
  748.         if _buf is None:
  749.             _buf = []
  750.         
  751.         locals['_buf'] = _buf
  752.         if not self.bytecode:
  753.             self.compile()
  754.         
  755.         exec (self.bytecode, globals, locals)
  756.         if bufarg is not None:
  757.             return bufarg
  758.         if not logger:
  759.             return ''.join(_buf)
  760.         
  761.         try:
  762.             return ''.join(_buf)
  763.         except UnicodeDecodeError:
  764.             logger
  765.             logger
  766.             bufarg is not None
  767.             ex = sys.exc_info()[1]
  768.             logger.error('[tenjin.Template] ' + str(ex))
  769.             logger.error('[tenjin.Template] (_buf=%s)' % repr(_buf))
  770.             raise 
  771.         except:
  772.             logger
  773.  
  774.  
  775.     
  776.     def compile(self):
  777.         filename = self.filename
  778.         if not filename:
  779.             filename = '(tenjin)'
  780.         
  781.         if isinstance(filename, unicode):
  782.             filename = filename.encode('utf8')
  783.         
  784.         self.bytecode = compile(self.script, filename, 'exec')
  785.  
  786.  
  787.  
  788. class Preprocessor(Template):
  789.     STMT_PATTERN = None
  790.     
  791.     def stmt_pattern(self):
  792.         pat = Preprocessor.STMT_PATTERN
  793.         if not pat:
  794.             pat = Preprocessor.STMT_PATTERN = Template.compile_stmt_pattern('PY')
  795.         
  796.         return Preprocessor.STMT_PATTERN
  797.  
  798.     EXPR_PATTERN = None
  799.     
  800.     def expr_pattern(self):
  801.         pat = Preprocessor.EXPR_PATTERN
  802.         if not pat:
  803.             pat = Preprocessor.EXPR_PATTERN = re.compile('([#$])\\{\\{(.*?)\\}\\}', re.S)
  804.         
  805.         return Preprocessor.EXPR_PATTERN
  806.  
  807.     
  808.     def add_expr(self, buf, code, flag_escape = None):
  809.         if not code or code.isspace():
  810.             return None
  811.         code = '_decode_params(%s)' % code
  812.         Template.add_expr(self, buf, code, flag_escape)
  813.  
  814.  
  815.  
  816. class CacheStorage(object):
  817.     
  818.     def __init__(self, postfix = '.cache'):
  819.         self.postfix = postfix
  820.         self.items = { }
  821.  
  822.     
  823.     def get(self, fullpath, create_template):
  824.         template = self.items.get(fullpath)
  825.         if not template:
  826.             dict = self._load(fullpath)
  827.             if dict:
  828.                 template = create_template()
  829.                 for k, v in dict.items():
  830.                     setattr(template, k, v)
  831.                 
  832.                 self.items[fullpath] = template
  833.             
  834.         
  835.         return template
  836.  
  837.     
  838.     def set(self, fullpath, template):
  839.         self.items[fullpath] = template
  840.         dict = self._save_data_of(template)
  841.         return self._store(fullpath, dict)
  842.  
  843.     
  844.     def _save_data_of(self, template):
  845.         return {
  846.             'args': template.args,
  847.             'bytecode': template.bytecode,
  848.             'script': template.script,
  849.             'timestamp': template.timestamp }
  850.  
  851.     
  852.     def unset(self, fullpath):
  853.         self.items.pop(fullpath, None)
  854.         return self._delete(fullpath)
  855.  
  856.     
  857.     def clear(self):
  858.         for k, v in self.items.items():
  859.             self._delete(k)
  860.         
  861.         self.items.clear()
  862.  
  863.     
  864.     def _load(self, fullpath):
  865.         raise NotImplementedError.new('%s#_load(): not implemented yet.' % self.__class__.__name__)
  866.  
  867.     
  868.     def _store(self, fullpath, template):
  869.         raise NotImplementedError.new('%s#_store(): not implemented yet.' % self.__class__.__name__)
  870.  
  871.     
  872.     def _delete(self, fullpath):
  873.         raise NotImplementedError.new('%s#_delete(): not implemented yet.' % self.__class__.__name__)
  874.  
  875.     
  876.     def _cachename(self, fullpath):
  877.         return fullpath + self.postfix
  878.  
  879.  
  880.  
  881. class MemoryCacheStorage(CacheStorage):
  882.     
  883.     def _load(self, fullpath):
  884.         pass
  885.  
  886.     
  887.     def _store(self, fullpath, template):
  888.         pass
  889.  
  890.     
  891.     def _delete(self, fullpath):
  892.         pass
  893.  
  894.  
  895.  
  896. class FileCacheStorage(CacheStorage):
  897.     
  898.     def _delete(self, fullpath):
  899.         cachepath = self._cachename(fullpath)
  900.         if os.path.isfile(cachepath):
  901.             os.unlink(cachepath)
  902.         
  903.  
  904.  
  905.  
  906. class MarshalCacheStorage(FileCacheStorage):
  907.     
  908.     def _load(self, fullpath):
  909.         cachepath = self._cachename(fullpath)
  910.         if not os.path.isfile(cachepath):
  911.             return None
  912.         if logger:
  913.             logger.info('[tenjin.MarshalCacheStorage] load cache (file=%s)' % repr(cachepath))
  914.         
  915.         dump = _read_binary_file(cachepath)
  916.         return marshal.loads(dump)
  917.  
  918.     
  919.     def _store(self, fullpath, dict):
  920.         cachepath = self._cachename(fullpath)
  921.         if logger:
  922.             logger.info('[tenjin.MarshalCacheStorage] store cache (file=%s)' % repr(cachepath))
  923.         
  924.         _write_binary_file(cachepath, marshal.dumps(dict))
  925.  
  926.  
  927.  
  928. class PickleCacheStorage(FileCacheStorage):
  929.     
  930.     def _load(self, fullpath):
  931.         
  932.         try:
  933.             import cPickle as pickle
  934.         except:
  935.             import pickle
  936.  
  937.         cachepath = self._cachename(fullpath)
  938.         if not os.path.isfile(cachepath):
  939.             return None
  940.         if logger:
  941.             logger.info('[tenjin.PickleCacheStorage] load cache (file=%s)' % repr(cachepath))
  942.         
  943.         dump = _read_binary_file(cachepath)
  944.         return pickle.loads(dump)
  945.  
  946.     
  947.     def _store(self, fullpath, dict):
  948.         
  949.         try:
  950.             import cPickle as pickle
  951.         except:
  952.             import pickle
  953.  
  954.         if 'bytecode' in dict:
  955.             dict.pop('bytecode')
  956.         
  957.         cachepath = self._cachename(fullpath)
  958.         if logger:
  959.             logger.info('[tenjin.PickleCacheStorage] store cache (file=%s)' % repr(cachepath))
  960.         
  961.         _write_binary_file(cachepath, pickle.dumps(dict))
  962.  
  963.  
  964.  
  965. class TextCacheStorage(FileCacheStorage):
  966.     
  967.     def _load(self, fullpath):
  968.         cachepath = self._cachename(fullpath)
  969.         if not os.path.isfile(cachepath):
  970.             return None
  971.         if logger:
  972.             logger.info('[tenjin.TextCacheStorage] load cache (file=%s)' % repr(cachepath))
  973.         
  974.         s = _read_binary_file(cachepath)
  975.         if python2:
  976.             (header, script) = s.split('\n\n', 1)
  977.         elif python3:
  978.             (header, script) = s.split('\n\n'.encode('ascii'), 1)
  979.             header = header.decode('ascii')
  980.         
  981.         timestamp = None
  982.         encoding = None
  983.         args = None
  984.         for line in header.split('\n'):
  985.             (key, val) = line.split(': ', 1)
  986.             if key == 'timestamp':
  987.                 timestamp = float(val)
  988.                 continue
  989.             if key == 'encoding':
  990.                 encoding = val
  991.                 continue
  992.             if key == 'args':
  993.                 args = val.split(', ')
  994.                 continue
  995.         
  996.         if python2:
  997.             if encoding:
  998.                 script = script.decode(encoding)
  999.             
  1000.         elif python3:
  1001.             if not encoding:
  1002.                 pass
  1003.             script = script.decode('utf-8')
  1004.         
  1005.         return {
  1006.             'args': args,
  1007.             'script': script,
  1008.             'timestamp': timestamp }
  1009.  
  1010.     
  1011.     def _store(self, fullpath, dict):
  1012.         s = dict['script']
  1013.         if python2:
  1014.             if dict.get('encoding') and isinstance(s, unicode):
  1015.                 s = s.encode(dict['encoding'])
  1016.             
  1017.         
  1018.         sb = []
  1019.         sb.append('timestamp: %s\n' % dict['timestamp'])
  1020.         if dict.get('encoding'):
  1021.             sb.append('encoding: %s\n' % dict['encoding'])
  1022.         
  1023.         if dict.get('args') is not None:
  1024.             sb.append('args: %s\n' % ', '.join(dict['args']))
  1025.         
  1026.         sb.append('\n')
  1027.         sb.append(s)
  1028.         s = ''.join(sb)
  1029.         if python3:
  1030.             if isinstance(s, str):
  1031.                 if not dict.get('encoding'):
  1032.                     pass
  1033.                 s = s.encode('utf-8')
  1034.             
  1035.         
  1036.         cachepath = self._cachename(fullpath)
  1037.         if logger:
  1038.             logger.info('[tenjin.TextCacheStorage] store cache (file=%s)' % repr(cachepath))
  1039.         
  1040.         _write_binary_file(cachepath, s)
  1041.  
  1042.     
  1043.     def _save_data_of(self, template):
  1044.         dict = FileCacheStorage._save_data_of(self, template)
  1045.         dict['encoding'] = template.encoding
  1046.         return dict
  1047.  
  1048.  
  1049.  
  1050. class GaeMemcacheCacheStorage(CacheStorage):
  1051.     lifetime = 0
  1052.     
  1053.     def __init__(self, lifetime = None, postfix = '.cache'):
  1054.         CacheStorage.__init__(self, postfix)
  1055.         if lifetime is not None:
  1056.             self.lifetime = lifetime
  1057.         
  1058.  
  1059.     
  1060.     def _load(self, fullpath):
  1061.         memcache = memcache
  1062.         import google.appengine.api
  1063.         key = self._cachename(fullpath)
  1064.         if logger:
  1065.             logger.info('[tenjin.GaeMemcacheCacheStorage] load cache (key=%s)' % repr(key))
  1066.         
  1067.         return memcache.get(key)
  1068.  
  1069.     
  1070.     def _store(self, fullpath, dict):
  1071.         if 'bytecode' in dict:
  1072.             dict.pop('bytecode')
  1073.         
  1074.         memcache = memcache
  1075.         import google.appengine.api
  1076.         key = self._cachename(fullpath)
  1077.         if logger:
  1078.             logger.info('[tenjin.GaeMemcacheCacheStorage] store cache (key=%s)' % repr(key))
  1079.         
  1080.         ret = memcache.set(key, dict, self.lifetime)
  1081.         if not ret:
  1082.             if logger:
  1083.                 logger.info('[tenjin.GaeMemcacheCacheStorage: failed to store cache (key=%s)' % repr(key))
  1084.             
  1085.         
  1086.  
  1087.     
  1088.     def _delete(self, fullpath):
  1089.         memcache = memcache
  1090.         import google.appengine.api
  1091.         memcache.delete(self._cachename(fullpath))
  1092.  
  1093.  
  1094.  
  1095. class Engine(object):
  1096.     prefix = ''
  1097.     postfix = ''
  1098.     layout = None
  1099.     templateclass = Template
  1100.     path = None
  1101.     cache = None
  1102.     preprocess = False
  1103.     _cache_storage_classes = {
  1104.         'marshal': MarshalCacheStorage,
  1105.         'pickle': PickleCacheStorage,
  1106.         'text': TextCacheStorage }
  1107.     
  1108.     def __init__(self, prefix = None, postfix = None, layout = None, path = None, cache = True, preprocess = None, templateclass = None, **kwargs):
  1109.         if prefix:
  1110.             self.prefix = prefix
  1111.         
  1112.         if postfix:
  1113.             self.postfix = postfix
  1114.         
  1115.         if layout:
  1116.             self.layout = layout
  1117.         
  1118.         if templateclass:
  1119.             self.templateclass = templateclass
  1120.         
  1121.         if path is not None:
  1122.             self.path = path
  1123.         
  1124.         if preprocess is not None:
  1125.             self.preprocess = preprocess
  1126.         
  1127.         self.kwargs = kwargs
  1128.         self.encoding = kwargs.get('encoding')
  1129.         self._filepaths = { }
  1130.         self._set_cache_storage(cache)
  1131.  
  1132.     
  1133.     def _set_cache_storage(self, cache):
  1134.         if cache is True:
  1135.             self.cache = MarshalCacheStorage()
  1136.         elif cache is None:
  1137.             self.cache = MemoryCacheStorage()
  1138.         elif cache is False:
  1139.             self.cache = None
  1140.         elif isinstance(cache, CacheStorage):
  1141.             self.cache = cache
  1142.         elif self._cache_storage_classes.get(cache):
  1143.             self.cache = self._cache_storage_classes[cache]()
  1144.         else:
  1145.             raise ValueError('%s: invalid cache object.' % repr(cache))
  1146.         return cache is True
  1147.  
  1148.     
  1149.     def to_filename(self, template_name):
  1150.         if template_name[0] == ':':
  1151.             return self.prefix + template_name[1:] + self.postfix
  1152.         return template_name
  1153.  
  1154.     
  1155.     def _relative_and_absolute_path(self, template_name):
  1156.         pair = self._filepaths.get(template_name)
  1157.         if pair:
  1158.             return pair
  1159.         filename = self.to_filename(template_name)
  1160.         filepath = self._find_file(filename)
  1161.         if not filepath:
  1162.             raise IOError('%s: filename not found (path=%s).' % (filename, repr(self.path)))
  1163.         filepath
  1164.         fullpath = os.path.abspath(filepath)
  1165.         return pair
  1166.  
  1167.     
  1168.     def _find_file(self, filename):
  1169.         if self.path:
  1170.             for dirname in self.path:
  1171.                 filepath = os.path.join(dirname, filename)
  1172.                 if os.path.isfile(filepath):
  1173.                     return filepath
  1174.             
  1175.         elif os.path.isfile(filename):
  1176.             return filename
  1177.  
  1178.     
  1179.     def _create_template(self, filepath, _context, _globals):
  1180.         if filepath and self.preprocess:
  1181.             s = self._preprocess(filepath, _context, _globals)
  1182.             template = self.templateclass(None, **self.kwargs)
  1183.             template.convert(s, filepath)
  1184.         else:
  1185.             template = self.templateclass(filepath, **self.kwargs)
  1186.         return template
  1187.  
  1188.     
  1189.     def _preprocess(self, filepath, _context, _globals):
  1190.         if '_engine' not in _context:
  1191.             self.hook_context(_context)
  1192.         
  1193.         preprocessor = Preprocessor(filepath)
  1194.         return preprocessor.render(_context, globals = _globals)
  1195.  
  1196.     
  1197.     def get_template(self, template_name, _context = None, _globals = None):
  1198.         (filename, fullpath) = self._relative_and_absolute_path(template_name)
  1199.         cache = self.cache
  1200.         if not cache or cache.get(fullpath, self.templateclass):
  1201.             pass
  1202.         template = None
  1203.         mtime = None
  1204.         if template:
  1205.             mtime = os.path.getmtime(filename)
  1206.             if template.timestamp != mtime:
  1207.                 template = None
  1208.                 if logger:
  1209.                     logger.info('[tenjin.Engine] cache is old (filename=%s, template=%s)' % (repr(filename), repr(template)))
  1210.                 
  1211.             
  1212.         
  1213.         if not template:
  1214.             if not mtime:
  1215.                 mtime = os.path.getmtime(filename)
  1216.             
  1217.             if self.preprocess:
  1218.                 if _context is None:
  1219.                     _context = { }
  1220.                 
  1221.                 if _globals is None:
  1222.                     _globals = sys._getframe(1).f_globals
  1223.                 
  1224.             
  1225.             template = self._create_template(filename, _context, _globals)
  1226.             template.timestamp = mtime
  1227.             if cache:
  1228.                 if not template.bytecode:
  1229.                     template.compile()
  1230.                 
  1231.                 cache.set(fullpath, template)
  1232.             
  1233.         
  1234.         return template
  1235.  
  1236.     
  1237.     def include(self, template_name, append_to_buf = True):
  1238.         frame = sys._getframe(1)
  1239.         locals = frame.f_locals
  1240.         globals = frame.f_globals
  1241.         context = locals['_context']
  1242.         template = self.get_template(template_name, context, globals)
  1243.         if append_to_buf:
  1244.             _buf = locals['_buf']
  1245.         else:
  1246.             _buf = None
  1247.         return template.render(context, globals, _buf = _buf)
  1248.  
  1249.     
  1250.     def render(self, template_name, context = None, globals = None, layout = True):
  1251.         if context is None:
  1252.             context = { }
  1253.         
  1254.         if globals is None:
  1255.             globals = sys._getframe(1).f_globals
  1256.         
  1257.         self.hook_context(context)
  1258.         while True:
  1259.             template = self.get_template(template_name, context, globals)
  1260.             content = template.render(context, globals)
  1261.             layout = context.pop('_layout', layout)
  1262.             if layout is True or layout is None:
  1263.                 layout = self.layout
  1264.             
  1265.             if not layout:
  1266.                 break
  1267.             
  1268.             template_name = layout
  1269.             layout = False
  1270.             context['_content'] = content
  1271.         context.pop('_content', None)
  1272.         return content
  1273.  
  1274.     
  1275.     def hook_context(self, context):
  1276.         context['_engine'] = self
  1277.         context['include'] = self.include
  1278.  
  1279.  
  1280.