NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

templates.py (11839B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\werkzeug\templates.py
      2 import sys
      3 import re
      4 import __builtin__ as builtins
      5 from compiler import ast, parse
      6 from compiler.pycodegen import ModuleCodeGenerator
      7 from tokenize import PseudoToken
      8 from werkzeug import utils, urls
      9 from werkzeug._internal import _decode_unicode
     10 token_re = re.compile('%s|%s(?s)' % ('[uU]?[rR]?("""|\\\'\\\'\\\')((?<!\\\\)\\\\\\1|.)*?\\1', PseudoToken))
     11 directive_re = re.compile('(?<!\\\\)<%(?:(#)|(py(?:thon)?\\b)|(?:\\s*(\\w+))\\s*)(.*?)\\s*%>\\n?(?s)')
     12 escape_re = re.compile('\\\\\\n|\\\\(\\\\|<%)')
     13 namestart_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
     14 undefined = type('UndefinedType', (object,), {'__iter__': lambda x: iter(()),
     15  '__repr__': lambda x: 'Undefined',
     16  '__str__': lambda x: ''})()
     17 runtime_vars = dict.fromkeys(('Undefined', '__to_unicode', '__context', '__write', '__write_many'))
     18 
     19 def call_stmt(func, args, lineno):
     20     return ast.CallFunc(ast.Name(func, lineno=lineno), args, lineno=lineno)
     21 
     22 
     23 def tokenize(source, filename):
     24     escape = escape_re.sub
     25     escape_repl = lambda m: m.group(1) or ''
     26     lineno = 1
     27     pos = 0
     28     for match in directive_re.finditer(source):
     29         start, end = match.span()
     30         if start > pos:
     31             data = source[pos:start]
     32             yield (lineno, 'data', escape(escape_repl, data))
     33             lineno += data.count('\n')
     34         is_comment, is_code, cmd, args = match.groups()
     35         if is_code:
     36             yield (lineno, 'code', args)
     37         elif not is_comment:
     38             yield (lineno, 'cmd', (cmd, args))
     39         lineno += source[start:end].count('\n')
     40         pos = end
     41 
     42     if pos < len(source):
     43         yield (lineno, 'data', escape(escape_repl, source[pos:]))
     44 
     45 
     46 def transform(node, filename):
     47     root = ast.Module(None, node, lineno=1)
     48     nodes = [root]
     49     while nodes:
     50         node = nodes.pop()
     51         node.filename = filename
     52         if node.__class__ in (ast.Printnl, ast.Print):
     53             node.dest = ast.Name('__context')
     54         elif node.__class__ is ast.Const and isinstance(node.value, str):
     55             try:
     56                 node.value.decode('ascii')
     57             except UnicodeError:
     58                 node.value = node.value.decode('utf-8')
     59 
     60         nodes.extend(node.getChildNodes())
     61 
     62     return root
     63 
     64 
     65 class TemplateSyntaxError(SyntaxError):
     66 
     67     def __init__(self, msg, filename, lineno):
     68         from linecache import getline
     69         l = getline(filename, lineno)
     70         SyntaxError.__init__(self, msg, (filename,
     71          lineno,
     72          len(l) or 1,
     73          l))
     74 
     75 
     76 class Parser(object):
     77 
     78     def __init__(self, gen, filename):
     79         self.gen = gen
     80         self.filename = filename
     81         self.lineno = 1
     82 
     83     def fail(self, msg):
     84         raise TemplateSyntaxError(msg, self.filename, self.lineno)
     85 
     86     def parse_python(self, expr, type = 'exec'):
     87         if isinstance(expr, unicode):
     88             expr = '\xef\xbb\xbf' + expr.encode('utf-8')
     89         try:
     90             node = parse(expr, type)
     91         except SyntaxError as e:
     92             raise TemplateSyntaxError(str(e), self.filename, self.lineno + e.lineno - 1)
     93 
     94         nodes = [node]
     95         while nodes:
     96             n = nodes.pop()
     97             if hasattr(n, 'lineno'):
     98                 n.lineno = (n.lineno or 1) + self.lineno - 1
     99             nodes.extend(n.getChildNodes())
    100 
    101         return node.node
    102 
    103     def parse(self, needle = ()):
    104         start_lineno = self.lineno
    105         result = []
    106         add = result.append
    107         for self.lineno, token, value in self.gen:
    108             if token == 'data':
    109                 add(self.parse_data(value))
    110             elif token == 'code':
    111                 add(self.parse_code(value.splitlines()))
    112             elif token == 'cmd':
    113                 name, args = value
    114                 if name in needle:
    115                     return (name, args, ast.Stmt(result, lineno=start_lineno))
    116                 if name in ('for', 'while'):
    117                     add(self.parse_loop(args, name))
    118                 elif name == 'if':
    119                     add(self.parse_if(args))
    120                 else:
    121                     self.fail('unknown directive %s' % name)
    122 
    123         if needle:
    124             self.fail('unexpected end of template')
    125         return ast.Stmt(result, lineno=start_lineno)
    126 
    127     def parse_loop(self, args, type):
    128         rv = self.parse_python('%s %s: pass' % (type, args), 'exec').nodes[0]
    129         tag, value, rv.body = self.parse(('end' + type, 'else'))
    130         if value:
    131             self.fail('unexpected data after ' + tag)
    132         if tag == 'else':
    133             tag, value, rv.else_ = self.parse(('end' + type,))
    134             if value:
    135                 self.fail('unexpected data after else')
    136         return rv
    137 
    138     def parse_if(self, args):
    139         cond = self.parse_python('if %s: pass' % args).nodes[0]
    140         tag, value, body = self.parse(('else', 'elif', 'endif'))
    141         cond.tests[0] = (cond.tests[0][0], body)
    142         while 1:
    143             if tag == 'else':
    144                 if value:
    145                     self.fail('unexpected data after else')
    146                 tag, value, cond.else_ = self.parse(('endif',))
    147             elif tag == 'elif':
    148                 expr = self.parse_python(value, 'eval')
    149                 tag, value, body = self.parse(('else', 'elif', 'endif'))
    150                 cond.tests.append((expr, body))
    151                 continue
    152             break
    153 
    154         if value:
    155             self.fail('unexpected data after endif')
    156         return cond
    157 
    158     def parse_code(self, lines):
    159         margin = sys.maxint
    160         for line in lines[1:]:
    161             content = len(line.lstrip())
    162             if content:
    163                 indent = len(line) - content
    164                 margin = min(margin, indent)
    165 
    166         if lines:
    167             lines[0] = lines[0].lstrip()
    168         if margin < sys.maxint:
    169             for i in xrange(1, len(lines)):
    170                 lines[i] = lines[i][margin:]
    171 
    172         while lines and not lines[-1]:
    173             lines.pop()
    174 
    175         while lines and not lines[0]:
    176             lines.pop(0)
    177 
    178         return self.parse_python('\n'.join(lines))
    179 
    180     def parse_data(self, text):
    181         start_lineno = lineno = self.lineno
    182         pos = 0
    183         end = len(text)
    184         nodes = []
    185 
    186         def match_or_fail(pos):
    187             match = token_re.match(text, pos)
    188             if match is None:
    189                 self.fail('invalid syntax')
    190             return (match.group().strip(), match.end())
    191 
    192         def write_expr(code):
    193             node = self.parse_python(code, 'eval')
    194             nodes.append(call_stmt('__to_unicode', [node], lineno))
    195             return code.count('\n')
    196 
    197         def write_data(value):
    198             if value:
    199                 nodes.append(ast.Const(value, lineno=lineno))
    200                 return value.count('\n')
    201             return 0
    202 
    203         while 1:
    204             offset = text.find('$', pos)
    205             if offset < 0:
    206                 break
    207             next = text[offset + 1]
    208             if next == '{':
    209                 lineno += write_data(text[pos:offset])
    210                 pos = offset + 2
    211                 level = 1
    212                 while level:
    213                     token, pos = match_or_fail(pos)
    214                     if token in ('{', '}'):
    215                         level += token == '{' and 1 or -1
    216 
    217                 lineno += write_expr(text[offset + 2:pos - 1])
    218             elif next in namestart_chars:
    219                 lineno += write_data(text[pos:offset])
    220                 token, pos = match_or_fail(offset + 1)
    221                 while pos < end:
    222                     if text[pos] == '.' and pos + 1 < end and text[pos + 1] in namestart_chars:
    223                         token, pos = match_or_fail(pos + 1)
    224                     elif text[pos] in '([':
    225                         pos += 1
    226                         level = 1
    227                         while level:
    228                             token, pos = match_or_fail(pos)
    229                             if token in ('(', ')', '[', ']'):
    230                                 level += token in '([' and 1 or -1
    231 
    232                     else:
    233                         break
    234 
    235                 lineno += write_expr(text[offset + 1:pos])
    236             else:
    237                 lineno += write_data(text[pos:offset + 1])
    238                 pos = offset + 1 + (next == '$')
    239 
    240         write_data(text[pos:])
    241         return ast.Discard(call_stmt(len(nodes) == 1 and '__write' or '__write_many', nodes, start_lineno), lineno=start_lineno)
    242 
    243 
    244 class Context(object):
    245 
    246     def __init__(self, namespace, charset, errors):
    247         self.charset = charset
    248         self.errors = errors
    249         self._namespace = namespace
    250         self._buffer = []
    251         self._write = self._buffer.append
    252         _extend = self._buffer.extend
    253         self.runtime = dict(Undefined=undefined, __to_unicode=self.to_unicode, __context=self, __write=self._write, __write_many=lambda *a: _extend(a))
    254 
    255     def write(self, value):
    256         self._write(self.to_unicode(value))
    257 
    258     def to_unicode(self, value):
    259         if isinstance(value, str):
    260             return _decode_unicode(value, self.charset, self.errors)
    261         return unicode(value)
    262 
    263     def get_value(self, as_unicode = True):
    264         rv = u''.join(self._buffer)
    265         if not as_unicode:
    266             return rv.encode(self.charset, self.errors)
    267         return rv
    268 
    269     def __getitem__(self, key, default = undefined):
    270         try:
    271             return self._namespace[key]
    272         except KeyError:
    273             return getattr(builtins, key, default)
    274 
    275     def get(self, key, default = None):
    276         return self.__getitem__(key, default)
    277 
    278     def __setitem__(self, key, value):
    279         self._namespace[key] = value
    280 
    281     def __delitem__(self, key):
    282         del self._namespace[key]
    283 
    284 
    285 class TemplateCodeGenerator(ModuleCodeGenerator):
    286 
    287     def __init__(self, node, filename):
    288         ModuleCodeGenerator.__init__(self, transform(node, filename))
    289 
    290     def _nameOp(self, prefix, name):
    291         if name in runtime_vars:
    292             return self.emit(prefix + '_GLOBAL', name)
    293         return ModuleCodeGenerator._nameOp(self, prefix, name)
    294 
    295 
    296 class Template(object):
    297     default_context = {'escape': utils.escape,
    298      'url_quote': urls.url_quote,
    299      'url_quote_plus': urls.url_quote_plus,
    300      'url_encode': urls.url_encode}
    301 
    302     def __init__(self, source, filename = '<template>', charset = 'utf-8', errors = 'strict', unicode_mode = True):
    303         if isinstance(source, str):
    304             source = _decode_unicode(source, charset, errors)
    305         if isinstance(filename, unicode):
    306             filename = filename.encode('utf-8')
    307         node = Parser(tokenize(u'\n'.join(source.splitlines()), filename), filename).parse()
    308         self.code = TemplateCodeGenerator(node, filename).getCode()
    309         self.filename = filename
    310         self.charset = charset
    311         self.errors = errors
    312         self.unicode_mode = unicode_mode
    313 
    314     @classmethod
    315     def from_file(cls, file, charset = 'utf-8', errors = 'strict', unicode_mode = True):
    316         close = False
    317         if isinstance(file, basestring):
    318             f = open(file, 'r')
    319             close = True
    320         try:
    321             data = _decode_unicode(f.read(), charset, errors)
    322         finally:
    323             if close:
    324                 f.close()
    325 
    326         return cls(data, getattr(f, 'name', '<template>'), charset, errors, unicode_mode)
    327 
    328     def render(self, *args, **kwargs):
    329         ns = self.default_context.copy()
    330         if len(args) == 1 and isinstance(args[0], utils.MultiDict):
    331             ns.update(args[0].to_dict(flat=True))
    332         else:
    333             ns.update(dict(*args))
    334         if kwargs:
    335             ns.update(kwargs)
    336         context = Context(ns, self.charset, self.errors)
    337         exec self.code in context.runtime, context
    338         return context.get_value(self.unicode_mode)
    339 
    340     def substitute(self, *args, **kwargs):
    341         return self.render(*args, **kwargs)