Updated mako lib

This commit is contained in:
rembo10
2014-03-31 21:50:41 -07:00
parent 708f5fcd70
commit 0c714a78de
22 changed files with 570 additions and 318 deletions
+2 -2
View File
@@ -1,9 +1,9 @@
# mako/__init__.py # mako/__init__.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
__version__ = '0.7.2' __version__ = '0.9.1'
+10 -4
View File
@@ -1,5 +1,5 @@
# mako/_ast_util.py # mako/_ast_util.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -31,7 +31,7 @@
:license: Python License. :license: Python License.
""" """
from _ast import * from _ast import *
from mako.compat import arg_stringname
BOOLOP_SYMBOLS = { BOOLOP_SYMBOLS = {
And: 'and', And: 'and',
@@ -403,10 +403,10 @@ class SourceGenerator(NodeVisitor):
self.visit(default) self.visit(default)
if node.vararg is not None: if node.vararg is not None:
write_comma() write_comma()
self.write('*' + node.vararg) self.write('*' + arg_stringname(node.vararg))
if node.kwarg is not None: if node.kwarg is not None:
write_comma() write_comma()
self.write('**' + node.kwarg) self.write('**' + arg_stringname(node.kwarg))
def decorators(self, node): def decorators(self, node):
for decorator in node.decorator_list: for decorator in node.decorator_list:
@@ -659,6 +659,12 @@ class SourceGenerator(NodeVisitor):
def visit_Name(self, node): def visit_Name(self, node):
self.write(node.id) self.write(node.id)
def visit_NameConstant(self, node):
self.write(str(node.value))
def visit_arg(self, node):
self.write(node.arg)
def visit_Str(self, node): def visit_Str(self, node):
self.write(repr(node.s)) self.write(repr(node.s))
+7 -6
View File
@@ -1,5 +1,5 @@
# mako/ast.py # mako/ast.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -7,7 +7,8 @@
"""utilities for analyzing expressions and blocks of Python """utilities for analyzing expressions and blocks of Python
code, as well as generating Python from AST nodes""" code, as well as generating Python from AST nodes"""
from mako import exceptions, pyparser, util from mako import exceptions, pyparser, compat
from mako.compat import arg_stringname
import re import re
class PythonCode(object): class PythonCode(object):
@@ -33,7 +34,7 @@ class PythonCode(object):
# - AST is less likely to break with version changes # - AST is less likely to break with version changes
# (for example, the behavior of co_names changed a little bit # (for example, the behavior of co_names changed a little bit
# in python version 2.5) # in python version 2.5)
if isinstance(code, basestring): if isinstance(code, compat.string_types):
expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs) expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
else: else:
expr = code expr = code
@@ -48,7 +49,7 @@ class ArgumentList(object):
self.args = [] self.args = []
self.declared_identifiers = set() self.declared_identifiers = set()
self.undeclared_identifiers = set() self.undeclared_identifiers = set()
if isinstance(code, basestring): if isinstance(code, compat.string_types):
if re.match(r"\S", code) and not re.match(r",\s*$", code): if re.match(r"\S", code) and not re.match(r",\s*$", code):
# if theres text and no trailing comma, insure its parsed # if theres text and no trailing comma, insure its parsed
# as a tuple by adding a trailing comma # as a tuple by adding a trailing comma
@@ -126,10 +127,10 @@ class FunctionDecl(object):
for arg in argnames: for arg in argnames:
default = None default = None
if kwargs: if kwargs:
arg = "**" + arg arg = "**" + arg_stringname(arg)
kwargs = False kwargs = False
elif varargs: elif varargs:
arg = "*" + arg arg = "*" + arg_stringname(arg)
varargs = False varargs = False
else: else:
default = len(defaults) and defaults.pop() or None default = len(defaults) and defaults.pop() or None
+3 -3
View File
@@ -1,10 +1,10 @@
# mako/cache.py # mako/cache.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
from mako import exceptions, util from mako import compat, util
_cache_plugins = util.PluginLoader("mako.cache") _cache_plugins = util.PluginLoader("mako.cache")
@@ -64,7 +64,7 @@ class Cache(object):
def __init__(self, template, *args): def __init__(self, template, *args):
# check for a stale template calling the # check for a stale template calling the
# constructor # constructor
if isinstance(template, basestring) and args: if isinstance(template, compat.string_types) and args:
return return
self.template = template self.template = template
self.id = template.module.__name__ self.id = template.module.__name__
+32 -19
View File
@@ -1,5 +1,5 @@
# mako/codegen.py # mako/codegen.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -11,8 +11,10 @@ import time
import re import re
from mako.pygen import PythonPrinter from mako.pygen import PythonPrinter
from mako import util, ast, parsetree, filters, exceptions from mako import util, ast, parsetree, filters, exceptions
from mako import compat
MAGIC_NUMBER = 8
MAGIC_NUMBER = 9
# names which are hardwired into the # names which are hardwired into the
# template and are not accessed via the # template and are not accessed via the
@@ -25,12 +27,13 @@ def compile(node,
default_filters=None, default_filters=None,
buffer_filters=None, buffer_filters=None,
imports=None, imports=None,
future_imports=None,
source_encoding=None, source_encoding=None,
generate_magic_comment=True, generate_magic_comment=True,
disable_unicode=False, disable_unicode=False,
strict_undefined=False, strict_undefined=False,
enable_loop=True, enable_loop=True,
reserved_names=()): reserved_names=frozenset()):
"""Generate module source code given a parsetree node, """Generate module source code given a parsetree node,
uri, and optional source filename""" uri, and optional source filename"""
@@ -39,7 +42,7 @@ def compile(node,
# a bytestring itself, as we will be embedding it into # a bytestring itself, as we will be embedding it into
# the generated source and we don't want to coerce the # the generated source and we don't want to coerce the
# result into a unicode object, in "disable_unicode" mode # result into a unicode object, in "disable_unicode" mode
if not util.py3k and isinstance(source_encoding, unicode): if not compat.py3k and isinstance(source_encoding, compat.text_type):
source_encoding = source_encoding.encode(source_encoding) source_encoding = source_encoding.encode(source_encoding)
@@ -52,6 +55,7 @@ def compile(node,
default_filters, default_filters,
buffer_filters, buffer_filters,
imports, imports,
future_imports,
source_encoding, source_encoding,
generate_magic_comment, generate_magic_comment,
disable_unicode, disable_unicode,
@@ -68,6 +72,7 @@ class _CompileContext(object):
default_filters, default_filters,
buffer_filters, buffer_filters,
imports, imports,
future_imports,
source_encoding, source_encoding,
generate_magic_comment, generate_magic_comment,
disable_unicode, disable_unicode,
@@ -79,6 +84,7 @@ class _CompileContext(object):
self.default_filters = default_filters self.default_filters = default_filters
self.buffer_filters = buffer_filters self.buffer_filters = buffer_filters
self.imports = imports self.imports = imports
self.future_imports = future_imports
self.source_encoding = source_encoding self.source_encoding = source_encoding
self.generate_magic_comment = generate_magic_comment self.generate_magic_comment = generate_magic_comment
self.disable_unicode = disable_unicode self.disable_unicode = disable_unicode
@@ -97,7 +103,6 @@ class _GenerateRenderMethod(object):
self.compiler = compiler self.compiler = compiler
self.node = node self.node = node
self.identifier_stack = [None] self.identifier_stack = [None]
self.in_def = isinstance(node, (parsetree.DefTag, parsetree.BlockTag)) self.in_def = isinstance(node, (parsetree.DefTag, parsetree.BlockTag))
if self.in_def: if self.in_def:
@@ -153,7 +158,6 @@ class _GenerateRenderMethod(object):
inherit = [] inherit = []
namespaces = {} namespaces = {}
module_code = [] module_code = []
encoding =[None]
self.compiler.pagetag = None self.compiler.pagetag = None
@@ -184,9 +188,12 @@ class _GenerateRenderMethod(object):
# module-level names, python code # module-level names, python code
if self.compiler.generate_magic_comment and \ if self.compiler.generate_magic_comment and \
self.compiler.source_encoding: self.compiler.source_encoding:
self.printer.writeline("# -*- encoding:%s -*-" % self.printer.writeline("# -*- coding:%s -*-" %
self.compiler.source_encoding) self.compiler.source_encoding)
if self.compiler.future_imports:
self.printer.writeline("from __future__ import %s" %
(", ".join(self.compiler.future_imports),))
self.printer.writeline("from mako import runtime, filters, cache") self.printer.writeline("from mako import runtime, filters, cache")
self.printer.writeline("UNDEFINED = runtime.UNDEFINED") self.printer.writeline("UNDEFINED = runtime.UNDEFINED")
self.printer.writeline("__M_dict_builtin = dict") self.printer.writeline("__M_dict_builtin = dict")
@@ -236,7 +243,7 @@ class _GenerateRenderMethod(object):
elif len(namespaces): elif len(namespaces):
self.write_namespaces(namespaces) self.write_namespaces(namespaces)
return main_identifiers.topleveldefs.values() return list(main_identifiers.topleveldefs.values())
def write_render_callable(self, node, name, args, buffered, filtered, def write_render_callable(self, node, name, args, buffered, filtered,
cached): cached):
@@ -315,13 +322,13 @@ class _GenerateRenderMethod(object):
"except KeyError:", "except KeyError:",
"_mako_generate_namespaces(context)", "_mako_generate_namespaces(context)",
"return context.namespaces[(__name__, name)]", "return context.namespaces[(__name__, name)]",
None,None None, None
) )
self.printer.writeline("def _mako_generate_namespaces(context):") self.printer.writeline("def _mako_generate_namespaces(context):")
for node in namespaces.values(): for node in namespaces.values():
if node.attributes.has_key('import'): if 'import' in node.attributes:
self.compiler.has_ns_imports = True self.compiler.has_ns_imports = True
self.write_source_comment(node) self.write_source_comment(node)
if len(node.nodes): if len(node.nodes):
@@ -456,8 +463,8 @@ class _GenerateRenderMethod(object):
if toplevel and getattr(self.compiler, 'has_ns_imports', False): if toplevel and getattr(self.compiler, 'has_ns_imports', False):
self.printer.writeline("_import_ns = {}") self.printer.writeline("_import_ns = {}")
self.compiler.has_imports = True self.compiler.has_imports = True
for ident, ns in self.compiler.namespaces.iteritems(): for ident, ns in self.compiler.namespaces.items():
if ns.attributes.has_key('import'): if 'import' in ns.attributes:
self.printer.writeline( self.printer.writeline(
"_mako_get_namespace(context, %r)."\ "_mako_get_namespace(context, %r)."\
"_populate(_import_ns, %r)" % "_populate(_import_ns, %r)" %
@@ -541,7 +548,7 @@ class _GenerateRenderMethod(object):
if not self.in_def and ( if not self.in_def and (
len(self.identifiers.locally_assigned) > 0 or len(self.identifiers.locally_assigned) > 0 or
len(self.identifiers.argument_declared) > 0): len(self.identifiers.argument_declared) > 0):
nameargs.insert(0, 'context.locals_(__M_locals)') nameargs.insert(0, 'context._locals(__M_locals)')
else: else:
nameargs.insert(0, 'context') nameargs.insert(0, 'context')
self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls))) self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls)))
@@ -691,21 +698,21 @@ class _GenerateRenderMethod(object):
"cache._ctx_get_or_create("\ "cache._ctx_get_or_create("\
"%s, lambda:__M_%s(%s), context, %s__M_defname=%r)" % \ "%s, lambda:__M_%s(%s), context, %s__M_defname=%r)" % \
(cachekey, name, ','.join(pass_args), (cachekey, name, ','.join(pass_args),
''.join(["%s=%s, " % (k,v) ''.join(["%s=%s, " % (k, v)
for k, v in cache_args.items()]), for k, v in cache_args.items()]),
name name
) )
# apply buffer_filters # apply buffer_filters
s = self.create_filter_callable(self.compiler.buffer_filters, s, s = self.create_filter_callable(self.compiler.buffer_filters, s,
False) False)
self.printer.writelines("return " + s,None) self.printer.writelines("return " + s, None)
else: else:
self.printer.writelines( self.printer.writelines(
"__M_writer(context.get('local')." "__M_writer(context.get('local')."
"cache._ctx_get_or_create("\ "cache._ctx_get_or_create("\
"%s, lambda:__M_%s(%s), context, %s__M_defname=%r))" % "%s, lambda:__M_%s(%s), context, %s__M_defname=%r))" %
(cachekey, name, ','.join(pass_args), (cachekey, name, ','.join(pass_args),
''.join(["%s=%s, " % (k,v) ''.join(["%s=%s, " % (k, v)
for k, v in cache_args.items()]), for k, v in cache_args.items()]),
name, name,
), ),
@@ -784,10 +791,10 @@ class _GenerateRenderMethod(object):
# and end control lines, and # and end control lines, and
# 3) any control line with no content other than comments # 3) any control line with no content other than comments
if not children or ( if not children or (
util.all(isinstance(c, (parsetree.Comment, compat.all(isinstance(c, (parsetree.Comment,
parsetree.ControlLine)) parsetree.ControlLine))
for c in children) and for c in children) and
util.all((node.is_ternary(c.keyword) or c.isend) compat.all((node.is_ternary(c.keyword) or c.isend)
for c in children for c in children
if isinstance(c, parsetree.ControlLine))): if isinstance(c, parsetree.ControlLine))):
self.printer.writeline("pass") self.printer.writeline("pass")
@@ -1115,7 +1122,7 @@ class _Identifiers(object):
% (node.name, ), **node.exception_kwargs) % (node.name, ), **node.exception_kwargs)
for ident in node.undeclared_identifiers(): for ident in node.undeclared_identifiers():
if ident != 'context' and\ if ident != 'context' and \
ident not in self.declared.union(self.locally_declared): ident not in self.declared.union(self.locally_declared):
self.undeclared.add(ident) self.undeclared.add(ident)
@@ -1129,6 +1136,12 @@ class _Identifiers(object):
for n in node.nodes: for n in node.nodes:
n.accept_visitor(self) n.accept_visitor(self)
def visitTextTag(self, node):
for ident in node.undeclared_identifiers():
if ident != 'context' and \
ident not in self.declared.union(self.locally_declared):
self.undeclared.add(ident)
def visitIncludeTag(self, node): def visitIncludeTag(self, node):
self.check_declared(node) self.check_declared(node)
+167
View File
@@ -0,0 +1,167 @@
import sys
import time
py3k = sys.version_info >= (3, 0)
py33 = sys.version_info >= (3, 3)
py26 = sys.version_info >= (2, 6)
py25 = sys.version_info >= (2, 5)
jython = sys.platform.startswith('java')
win32 = sys.platform.startswith('win')
pypy = hasattr(sys, 'pypy_version_info')
if py3k:
from io import StringIO
import builtins as compat_builtins
from urllib.parse import quote_plus, unquote_plus
from html.entities import codepoint2name, name2codepoint
string_types = str,
binary_type = bytes
text_type = str
from io import BytesIO as byte_buffer
def u(s):
return s
def octal(lit):
return eval("0o" + lit)
else:
import __builtin__ as compat_builtins
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
byte_buffer = StringIO
from urllib import quote_plus, unquote_plus
from htmlentitydefs import codepoint2name, name2codepoint
string_types = basestring,
binary_type = str
text_type = unicode
def u(s):
return unicode(s, "utf-8")
def octal(lit):
return eval("0" + lit)
if py33:
from importlib import machinery
def load_module(module_id, path):
return machinery.SourceFileLoader(module_id, path).load_module()
else:
import imp
def load_module(module_id, path):
fp = open(path, 'rb')
try:
return imp.load_source(module_id, path, fp)
finally:
fp.close()
def exception_as():
return sys.exc_info()[1]
try:
import threading
if py3k:
import _thread as thread
else:
import thread
except ImportError:
import dummy_threading as threading
if py3k:
import _dummy_thread as thread
else:
import dummy_thread as thread
if win32 or jython:
time_func = time.clock
else:
time_func = time.time
try:
from functools import partial
except:
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
return newfunc
if not py25:
def all(iterable):
for i in iterable:
if not i:
return False
return True
def exception_name(exc):
try:
return exc.__class__.__name__
except AttributeError:
return exc.__name__
else:
all = all
def exception_name(exc):
return exc.__class__.__name__
try:
from inspect import CO_VARKEYWORDS, CO_VARARGS
def inspect_func_args(fn):
if py3k:
co = fn.__code__
else:
co = fn.func_code
nargs = co.co_argcount
names = co.co_varnames
args = list(names[:nargs])
varargs = None
if co.co_flags & CO_VARARGS:
varargs = co.co_varnames[nargs]
nargs = nargs + 1
varkw = None
if co.co_flags & CO_VARKEYWORDS:
varkw = co.co_varnames[nargs]
if py3k:
return args, varargs, varkw, fn.__defaults__
else:
return args, varargs, varkw, fn.func_defaults
except ImportError:
import inspect
def inspect_func_args(fn):
return inspect.getargspec(fn)
if py3k:
def callable(fn):
return hasattr(fn, '__call__')
else:
callable = callable
################################################
# cross-compatible metaclass implementation
# Copyright (c) 2010-2012 Benjamin Peterson
def with_metaclass(meta, base=object):
"""Create a base class with a metaclass."""
return meta("%sBase" % meta.__name__, (base,), {})
################################################
def arg_stringname(func_arg):
"""Gets the string name of a kwarg or vararg
In Python3.4 a function's args are
of _ast.arg type not _ast.name
"""
if hasattr(func_arg, 'arg'):
return func_arg.arg
else:
return str(func_arg)
+22 -10
View File
@@ -1,13 +1,15 @@
# mako/exceptions.py # mako/exceptions.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
"""exception classes""" """exception classes"""
import traceback, sys, re import traceback
from mako import util import sys
import re
from mako import util, compat
class MakoException(Exception): class MakoException(Exception):
pass pass
@@ -84,12 +86,12 @@ class RichTraceback(object):
@property @property
def errorname(self): def errorname(self):
return util.exception_name(self.error) return compat.exception_name(self.error)
def _init_message(self): def _init_message(self):
"""Find a unicode representation of self.error""" """Find a unicode representation of self.error"""
try: try:
self.message = unicode(self.error) self.message = compat.text_type(self.error)
except UnicodeError: except UnicodeError:
try: try:
self.message = str(self.error) self.message = str(self.error)
@@ -97,8 +99,8 @@ class RichTraceback(object):
# Fallback to args as neither unicode nor # Fallback to args as neither unicode nor
# str(Exception(u'\xe6')) work in Python < 2.6 # str(Exception(u'\xe6')) work in Python < 2.6
self.message = self.error.args[0] self.message = self.error.args[0]
if not isinstance(self.message, unicode): if not isinstance(self.message, compat.text_type):
self.message = unicode(self.message, 'ascii', 'replace') self.message = compat.text_type(self.message, 'ascii', 'replace')
def _get_reformatted_records(self, records): def _get_reformatted_records(self, records):
for rec in records: for rec in records:
@@ -150,7 +152,7 @@ class RichTraceback(object):
template_filename = info.template_filename or filename template_filename = info.template_filename or filename
except KeyError: except KeyError:
# A normal .py file (not a Template) # A normal .py file (not a Template)
if not util.py3k: if not compat.py3k:
try: try:
fp = open(filename, 'rb') fp = open(filename, 'rb')
encoding = util.parse_encoding(fp) encoding = util.parse_encoding(fp)
@@ -233,15 +235,25 @@ ${tback.errorname}: ${tback.message}
""") """)
try: def _install_pygments():
global syntax_highlight, pygments_html_formatter
from mako.ext.pygmentplugin import syntax_highlight,\ from mako.ext.pygmentplugin import syntax_highlight,\
pygments_html_formatter pygments_html_formatter
except ImportError:
def _install_fallback():
global syntax_highlight, pygments_html_formatter
from mako.filters import html_escape from mako.filters import html_escape
pygments_html_formatter = None pygments_html_formatter = None
def syntax_highlight(filename='', language=None): def syntax_highlight(filename='', language=None):
return html_escape return html_escape
def _install_highlighting():
try:
_install_pygments()
except ImportError:
_install_fallback()
_install_highlighting()
def html_error_template(): def html_error_template():
"""Provides a template that renders a stack trace in an HTML format, """Provides a template that renders a stack trace in an HTML format,
providing an excerpt of code as well as substituting source template providing an excerpt of code as well as substituting source template
+1 -1
View File
@@ -1,5 +1,5 @@
# ext/autohandler.py # ext/autohandler.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
+14 -20
View File
@@ -1,14 +1,13 @@
# ext/babelplugin.py # ext/babelplugin.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
"""gettext message extraction via Babel: http://babel.edgewall.org/""" """gettext message extraction via Babel: http://babel.edgewall.org/"""
from StringIO import StringIO
from babel.messages.extract import extract_python from babel.messages.extract import extract_python
from mako.compat import StringIO
from mako import compat
from mako import lexer, parsetree from mako import lexer, parsetree
def extract(fileobj, keywords, comment_tags, options): def extract(fileobj, keywords, comment_tags, options):
@@ -77,46 +76,41 @@ def extract_nodes(nodes, keywords, comment_tags, options):
elif isinstance(node, parsetree.PageTag): elif isinstance(node, parsetree.PageTag):
code = node.body_decl.code code = node.body_decl.code
elif isinstance(node, parsetree.CallNamespaceTag): elif isinstance(node, parsetree.CallNamespaceTag):
attribs = ', '.join(['%s=%s' % (key, val) code = node.expression
for key, val in node.attributes.iteritems()])
code = '{%s}' % attribs
child_nodes = node.nodes child_nodes = node.nodes
elif isinstance(node, parsetree.ControlLine): elif isinstance(node, parsetree.ControlLine):
if node.isend: if node.isend:
translator_comments = []
in_translator_comments = False in_translator_comments = False
continue continue
code = node.text code = node.text
elif isinstance(node, parsetree.Code): elif isinstance(node, parsetree.Code):
# <% and <%! blocks would provide their own translator comments
translator_comments = []
in_translator_comments = False in_translator_comments = False
code = node.code.code code = node.code.code
elif isinstance(node, parsetree.Expression): elif isinstance(node, parsetree.Expression):
code = node.code.code code = node.code.code
else: else:
translator_comments = []
in_translator_comments = False
continue continue
# Comments don't apply unless they immediately preceed the message # Comments don't apply unless they immediately preceed the message
if translator_comments and \ if translator_comments and \
translator_comments[-1][0] < node.lineno - 1: translator_comments[-1][0] < node.lineno - 1:
translator_comments = [] translator_comments = []
else:
translator_comments = \
[comment[1] for comment in translator_comments]
if isinstance(code, unicode): translator_strings = [comment[1] for comment in translator_comments]
if isinstance(code, compat.text_type):
code = code.encode('ascii', 'backslashreplace') code = code.encode('ascii', 'backslashreplace')
code = StringIO(code)
used_translator_comments = False
code = compat.byte_buffer(code)
for lineno, funcname, messages, python_translator_comments \ for lineno, funcname, messages, python_translator_comments \
in extract_python(code, keywords, comment_tags, options): in extract_python(code, keywords, comment_tags, options):
yield (node.lineno + (lineno - 1), funcname, messages, yield (node.lineno + (lineno - 1), funcname, messages,
translator_comments + python_translator_comments) translator_strings + python_translator_comments)
used_translator_comments = True
translator_comments = [] if used_translator_comments:
translator_comments = []
in_translator_comments = False in_translator_comments = False
if child_nodes: if child_nodes:
+1 -1
View File
@@ -19,7 +19,7 @@ class BeakerCacheImpl(CacheImpl):
if _beaker_cache is None: if _beaker_cache is None:
try: try:
from beaker import cache as beaker_cache from beaker import cache as beaker_cache
except ImportError, e: except ImportError:
raise exceptions.RuntimeException( raise exceptions.RuntimeException(
"the Beaker package is required to use cache " "the Beaker package is required to use cache "
"functionality.") "functionality.")
+1 -1
View File
@@ -1,5 +1,5 @@
# ext/preprocessors.py # ext/preprocessors.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
+3 -3
View File
@@ -1,5 +1,5 @@
# ext/pygmentplugin.py # ext/pygmentplugin.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -13,7 +13,7 @@ from pygments.token import \
Text, Comment, Operator, Keyword, Name, String, Other Text, Comment, Operator, Keyword, Name, String, Other
from pygments.formatters.html import HtmlFormatter from pygments.formatters.html import HtmlFormatter
from pygments import highlight from pygments import highlight
from mako import util from mako import compat
class MakoLexer(RegexLexer): class MakoLexer(RegexLexer):
name = 'Mako' name = 'Mako'
@@ -110,7 +110,7 @@ pygments_html_formatter = HtmlFormatter(cssclass='syntax-highlighted',
linenos=True) linenos=True)
def syntax_highlight(filename='', language=None): def syntax_highlight(filename='', language=None):
mako_lexer = MakoLexer() mako_lexer = MakoLexer()
if util.py3k: if compat.py3k:
python_lexer = Python3Lexer() python_lexer = Python3Lexer()
else: else:
python_lexer = PythonLexer() python_lexer = PythonLexer()
+5 -4
View File
@@ -1,10 +1,11 @@
# ext/turbogears.py # ext/turbogears.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
import re, inspect import inspect
from mako import compat
from mako.lookup import TemplateLookup from mako.lookup import TemplateLookup
from mako.template import Template from mako.template import Template
@@ -19,7 +20,7 @@ class TGPlugin(object):
# Pull the options out and initialize the lookup # Pull the options out and initialize the lookup
lookup_options = {} lookup_options = {}
for k, v in options.iteritems(): for k, v in options.items():
if k.startswith('mako.'): if k.startswith('mako.'):
lookup_options[k[5:]] = v lookup_options[k[5:]] = v
elif k in ['directories', 'filesystem_checks', 'module_directory']: elif k in ['directories', 'filesystem_checks', 'module_directory']:
@@ -46,7 +47,7 @@ class TGPlugin(object):
return self.lookup.get_template(templatename) return self.lookup.get_template(templatename)
def render(self, info, format="html", fragment=False, template=None): def render(self, info, format="html", fragment=False, template=None):
if isinstance(template, basestring): if isinstance(template, compat.string_types):
template = self.load_template(template) template = self.load_template(template)
# Load extra vars func if provided # Load extra vars func if provided
+44 -37
View File
@@ -1,29 +1,38 @@
# mako/filters.py # mako/filters.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
import re, urllib, htmlentitydefs, codecs import re
from StringIO import StringIO import codecs
from mako import util
from mako.compat import quote_plus, unquote_plus, codepoint2name, \
name2codepoint
from mako import compat
xml_escapes = { xml_escapes = {
'&' : '&amp;', '&': '&amp;',
'>' : '&gt;', '>': '&gt;',
'<' : '&lt;', '<': '&lt;',
'"' : '&#34;', # also &quot; in html-only '"': '&#34;', # also &quot; in html-only
"'" : '&#39;' # also &apos; in html-only "'": '&#39;' # also &apos; in html-only
} }
# XXX: &quot; is valid in HTML and XML # XXX: &quot; is valid in HTML and XML
# &apos; is not valid HTML, but is valid XML # &apos; is not valid HTML, but is valid XML
def legacy_html_escape(string): def legacy_html_escape(s):
"""legacy HTML escape for non-unicode mode.""" """legacy HTML escape for non-unicode mode."""
s = s.replace("&", "&amp;")
s = s.replace(">", "&gt;")
s = s.replace("<", "&lt;")
s = s.replace('"', "&#34;")
s = s.replace("'", "&#39;")
return s
return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string)
try: try:
import markupsafe import markupsafe
@@ -31,17 +40,16 @@ try:
except ImportError: except ImportError:
html_escape = legacy_html_escape html_escape = legacy_html_escape
def xml_escape(string): def xml_escape(string):
return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string) return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string)
def url_escape(string): def url_escape(string):
# convert into a list of octets # convert into a list of octets
string = string.encode("utf8") string = string.encode("utf8")
return urllib.quote_plus(string) return quote_plus(string)
def url_unescape(string): def url_unescape(string):
text = urllib.unquote_plus(string) text = unquote_plus(string)
if not is_ascii_str(text): if not is_ascii_str(text):
text = text.decode("utf8") text = text.decode("utf8")
return text return text
@@ -53,12 +61,12 @@ def trim(string):
class Decode(object): class Decode(object):
def __getattr__(self, key): def __getattr__(self, key):
def decode(x): def decode(x):
if isinstance(x, unicode): if isinstance(x, compat.text_type):
return x return x
elif not isinstance(x, str): elif not isinstance(x, compat.binary_type):
return unicode(str(x), encoding=key) return compat.text_type(str(x), encoding=key)
else: else:
return unicode(x, encoding=key) return compat.text_type(x, encoding=key)
return decode return decode
decode = Decode() decode = Decode()
@@ -72,8 +80,8 @@ def is_ascii_str(text):
class XMLEntityEscaper(object): class XMLEntityEscaper(object):
def __init__(self, codepoint2name, name2codepoint): def __init__(self, codepoint2name, name2codepoint):
self.codepoint2entity = dict([(c, u'&%s;' % n) self.codepoint2entity = dict([(c, compat.text_type('&%s;' % n))
for c,n in codepoint2name.iteritems()]) for c, n in codepoint2name.items()])
self.name2codepoint = name2codepoint self.name2codepoint = name2codepoint
def escape_entities(self, text): def escape_entities(self, text):
@@ -81,7 +89,7 @@ class XMLEntityEscaper(object):
Only characters corresponding to a named entity are replaced. Only characters corresponding to a named entity are replaced.
""" """
return unicode(text).translate(self.codepoint2entity) return compat.text_type(text).translate(self.codepoint2entity)
def __escape(self, m): def __escape(self, m):
codepoint = ord(m.group()) codepoint = ord(m.group())
@@ -102,7 +110,7 @@ class XMLEntityEscaper(object):
The return value is guaranteed to be ASCII. The return value is guaranteed to be ASCII.
""" """
return self.__escapable.sub(self.__escape, unicode(text) return self.__escapable.sub(self.__escape, compat.text_type(text)
).encode('ascii') ).encode('ascii')
# XXX: This regexp will not match all valid XML entity names__. # XXX: This regexp will not match all valid XML entity names__.
@@ -127,7 +135,7 @@ class XMLEntityEscaper(object):
# U+FFFD = "REPLACEMENT CHARACTER" # U+FFFD = "REPLACEMENT CHARACTER"
if codepoint < 128: if codepoint < 128:
return chr(codepoint) return chr(codepoint)
return unichr(codepoint) return chr(codepoint)
def unescape(self, text): def unescape(self, text):
"""Unescape character references. """Unescape character references.
@@ -138,8 +146,7 @@ class XMLEntityEscaper(object):
return self.__characterrefs.sub(self.__unescape, text) return self.__characterrefs.sub(self.__unescape, text)
_html_entities_escaper = XMLEntityEscaper(htmlentitydefs.codepoint2name, _html_entities_escaper = XMLEntityEscaper(codepoint2name, name2codepoint)
htmlentitydefs.name2codepoint)
html_entities_escape = _html_entities_escaper.escape_entities html_entities_escape = _html_entities_escaper.escape_entities
html_entities_unescape = _html_entities_escaper.unescape html_entities_unescape = _html_entities_escaper.unescape
@@ -159,7 +166,7 @@ def htmlentityreplace_errors(ex):
# Handle encoding errors # Handle encoding errors
bad_text = ex.object[ex.start:ex.end] bad_text = ex.object[ex.start:ex.end]
text = _html_entities_escaper.escape(bad_text) text = _html_entities_escaper.escape(bad_text)
return (unicode(text), ex.end) return (compat.text_type(text), ex.end)
raise ex raise ex
codecs.register_error('htmlentityreplace', htmlentityreplace_errors) codecs.register_error('htmlentityreplace', htmlentityreplace_errors)
@@ -168,20 +175,20 @@ codecs.register_error('htmlentityreplace', htmlentityreplace_errors)
# TODO: options to make this dynamic per-compilation will be added in a later # TODO: options to make this dynamic per-compilation will be added in a later
# release # release
DEFAULT_ESCAPES = { DEFAULT_ESCAPES = {
'x':'filters.xml_escape', 'x': 'filters.xml_escape',
'h':'filters.html_escape', 'h': 'filters.html_escape',
'u':'filters.url_escape', 'u': 'filters.url_escape',
'trim':'filters.trim', 'trim': 'filters.trim',
'entity':'filters.html_entities_escape', 'entity': 'filters.html_entities_escape',
'unicode':'unicode', 'unicode': 'unicode',
'decode':'decode', 'decode': 'decode',
'str':'str', 'str': 'str',
'n':'n' 'n': 'n'
} }
if util.py3k: if compat.py3k:
DEFAULT_ESCAPES.update({ DEFAULT_ESCAPES.update({
'unicode':'str' 'unicode': 'str'
}) })
NON_UNICODE_ESCAPES = DEFAULT_ESCAPES.copy() NON_UNICODE_ESCAPES = DEFAULT_ESCAPES.copy()
+7 -8
View File
@@ -1,13 +1,14 @@
# mako/lexer.py # mako/lexer.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
"""provides the Lexer class for parsing template strings into parse trees.""" """provides the Lexer class for parsing template strings into parse trees."""
import re, codecs import re
from mako import parsetree, exceptions, util import codecs
from mako import parsetree, exceptions, compat
from mako.pygen import adjust_whitespace from mako.pygen import adjust_whitespace
_regexp_cache = {} _regexp_cache = {}
@@ -29,7 +30,7 @@ class Lexer(object):
self.disable_unicode = disable_unicode self.disable_unicode = disable_unicode
self.encoding = input_encoding self.encoding = input_encoding
if util.py3k and disable_unicode: if compat.py3k and disable_unicode:
raise exceptions.UnsupportedError( raise exceptions.UnsupportedError(
"Mako for Python 3 does not " "Mako for Python 3 does not "
"support disabling Unicode") "support disabling Unicode")
@@ -173,7 +174,7 @@ class Lexer(object):
or raw if decode_raw=False or raw if decode_raw=False
""" """
if isinstance(text, unicode): if isinstance(text, compat.text_type):
m = self._coding_re.match(text) m = self._coding_re.match(text)
encoding = m and m.group(1) or known_encoding or 'ascii' encoding = m and m.group(1) or known_encoding or 'ascii'
return encoding, text return encoding, text
@@ -198,7 +199,7 @@ class Lexer(object):
if decode_raw: if decode_raw:
try: try:
text = text.decode(parsed_encoding) text = text.decode(parsed_encoding)
except UnicodeDecodeError, e: except UnicodeDecodeError:
raise exceptions.CompileException( raise exceptions.CompileException(
"Unicode decode operation of encoding '%s' failed" % "Unicode decode operation of encoding '%s' failed" %
parsed_encoding, parsed_encoding,
@@ -344,8 +345,6 @@ class Lexer(object):
| |
(?=\${) # an expression (?=\${) # an expression
| |
(?=\#\*) # multiline comment
|
(?=</?[%&]) # a substitution or block or call start or end (?=</?[%&]) # a substitution or block or call start or end
# - don't consume # - don't consume
| |
+8 -3
View File
@@ -1,5 +1,5 @@
# mako/lookup.py # mako/lookup.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -169,9 +169,11 @@ class TemplateLookup(TemplateCollection):
buffer_filters=(), buffer_filters=(),
strict_undefined=False, strict_undefined=False,
imports=None, imports=None,
future_imports=None,
enable_loop=True, enable_loop=True,
input_encoding=None, input_encoding=None,
preprocessor=None): preprocessor=None,
lexer_cls=None):
self.directories = [posixpath.normpath(d) for d in self.directories = [posixpath.normpath(d) for d in
util.to_list(directories, ()) util.to_list(directories, ())
@@ -208,8 +210,11 @@ class TemplateLookup(TemplateCollection):
'buffer_filters':buffer_filters, 'buffer_filters':buffer_filters,
'strict_undefined':strict_undefined, 'strict_undefined':strict_undefined,
'imports':imports, 'imports':imports,
'future_imports':future_imports,
'enable_loop':enable_loop, 'enable_loop':enable_loop,
'preprocessor':preprocessor} 'preprocessor':preprocessor,
'lexer_cls':lexer_cls
}
if collection_size == -1: if collection_size == -1:
self._collection = {} self._collection = {}
+17 -12
View File
@@ -1,12 +1,12 @@
# mako/parsetree.py # mako/parsetree.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
"""defines the parse tree components for Mako templates.""" """defines the parse tree components for Mako templates."""
from mako import exceptions, ast, util, filters from mako import exceptions, ast, util, filters, compat
import re import re
class Node(object): class Node(object):
@@ -20,8 +20,8 @@ class Node(object):
@property @property
def exception_kwargs(self): def exception_kwargs(self):
return {'source':self.source, 'lineno':self.lineno, return {'source': self.source, 'lineno': self.lineno,
'pos':self.pos, 'filename':self.filename} 'pos': self.pos, 'filename': self.filename}
def get_children(self): def get_children(self):
return [] return []
@@ -204,9 +204,9 @@ class _TagMeta(type):
_classmap = {} _classmap = {}
def __init__(cls, clsname, bases, dict): def __init__(cls, clsname, bases, dict):
if cls.__keyword__ is not None: if getattr(cls, '__keyword__', None) is not None:
cls._classmap[cls.__keyword__] = cls cls._classmap[cls.__keyword__] = cls
super(_TagMeta, cls).__init__(clsname, bases, dict) super(_TagMeta, cls).__init__(clsname, bases, dict)
def __call__(cls, keyword, attributes, **kwargs): def __call__(cls, keyword, attributes, **kwargs):
if ":" in keyword: if ":" in keyword:
@@ -226,7 +226,7 @@ class _TagMeta(type):
) )
return type.__call__(cls, keyword, attributes, **kwargs) return type.__call__(cls, keyword, attributes, **kwargs)
class Tag(Node): class Tag(compat.with_metaclass(_TagMeta, Node)):
"""abstract base class for tags. """abstract base class for tags.
<%sometag/> <%sometag/>
@@ -236,8 +236,6 @@ class Tag(Node):
</%someothertag> </%someothertag>
""" """
__metaclass__ = _TagMeta
__keyword__ = None __keyword__ = None
def __init__(self, keyword, attributes, expressions, def __init__(self, keyword, attributes, expressions,
@@ -393,6 +391,13 @@ class TextTag(Tag):
attributes.get('filter', ''), attributes.get('filter', ''),
**self.exception_kwargs) **self.exception_kwargs)
def undeclared_identifiers(self):
return self.filter_args.\
undeclared_identifiers.\
difference(filters.DEFAULT_ESCAPES.keys()).union(
self.expression_undeclared_identifiers
)
class DefTag(Tag): class DefTag(Tag):
__keyword__ = 'def' __keyword__ = 'def'
@@ -405,11 +410,11 @@ class DefTag(Tag):
keyword, keyword,
attributes, attributes,
expressions, expressions,
('name','filter', 'decorator'), ('name', 'filter', 'decorator'),
('name',), ('name',),
**kwargs) **kwargs)
name = attributes['name'] name = attributes['name']
if re.match(r'^[\w_]+$',name): if re.match(r'^[\w_]+$', name):
raise exceptions.CompileException( raise exceptions.CompileException(
"Missing parenthesis in %def", "Missing parenthesis in %def",
**self.exception_kwargs) **self.exception_kwargs)
@@ -540,7 +545,7 @@ class CallNamespaceTag(Tag):
namespace, namespace,
defname, defname,
",".join(["%s=%s" % (k, v) for k, v in ",".join(["%s=%s" % (k, v) for k, v in
self.parsed_attributes.iteritems() self.parsed_attributes.items()
if k != 'args']) if k != 'args'])
) )
self.code = ast.PythonCode(self.expression, **self.exception_kwargs) self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
+2 -3
View File
@@ -1,13 +1,12 @@
# mako/pygen.py # mako/pygen.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
"""utilities for generating and formatting literal Python code.""" """utilities for generating and formatting literal Python code."""
import re, string import re
from StringIO import StringIO
from mako import exceptions from mako import exceptions
class PythonPrinter(object): class PythonPrinter(object):
+53 -16
View File
@@ -1,5 +1,5 @@
# mako/pyparser.py # mako/pyparser.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -10,11 +10,11 @@ Parsing to AST is done via _ast on Python > 2.5, otherwise the compiler
module is used. module is used.
""" """
from StringIO import StringIO from mako import exceptions, util, compat
from mako import exceptions, util from mako.compat import StringIO, arg_stringname
import operator import operator
if util.py3k: if compat.py3k:
# words that cannot be assigned to (notably # words that cannot be assigned to (notably
# smaller than the total keys in __builtins__) # smaller than the total keys in __builtins__)
reserved = set(['True', 'False', 'None', 'print']) reserved = set(['True', 'False', 'None', 'print'])
@@ -33,13 +33,14 @@ else:
try: try:
import _ast import _ast
util.restore__ast(_ast) util.restore__ast(_ast)
import _ast_util from mako import _ast_util
except ImportError: except ImportError:
_ast = None _ast = None
from compiler import parse as compiler_parse from compiler import parse as compiler_parse
from compiler import visitor from compiler import visitor
def parse(code, mode='exec', **exception_kwargs): def parse(code, mode='exec', **exception_kwargs):
"""Parse an expression into AST""" """Parse an expression into AST"""
@@ -48,14 +49,14 @@ def parse(code, mode='exec', **exception_kwargs):
if _ast: if _ast:
return _ast_util.parse(code, '<unknown>', mode) return _ast_util.parse(code, '<unknown>', mode)
else: else:
if isinstance(code, unicode): if isinstance(code, compat.text_type):
code = code.encode('ascii', 'backslashreplace') code = code.encode('ascii', 'backslashreplace')
return compiler_parse(code, mode) return compiler_parse(code, mode)
except Exception, e: except Exception:
raise exceptions.SyntaxException( raise exceptions.SyntaxException(
"(%s) %s (%r)" % ( "(%s) %s (%r)" % (
e.__class__.__name__, compat.exception_as().__class__.__name__,
e, compat.exception_as(),
code[0:50] code[0:50]
), **exception_kwargs) ), **exception_kwargs)
@@ -92,7 +93,7 @@ if _ast:
self.visit(n) self.visit(n)
self.in_assign_targets = in_a self.in_assign_targets = in_a
if util.py3k: if compat.py3k:
# ExceptHandler is in Python 2, but this block only works in # ExceptHandler is in Python 2, but this block only works in
# Python 3 (and is required there) # Python 3 (and is required there)
@@ -112,6 +113,14 @@ if _ast:
self._add_declared(node.name) self._add_declared(node.name)
self._visit_function(node, False) self._visit_function(node, False)
def _expand_tuples(self, args):
for arg in args:
if isinstance(arg, _ast.Tuple):
for n in arg.elts:
yield n
else:
yield arg
def _visit_function(self, node, islambda): def _visit_function(self, node, islambda):
# push function state onto stack. dont log any more # push function state onto stack. dont log any more
@@ -125,7 +134,7 @@ if _ast:
local_ident_stack = self.local_ident_stack local_ident_stack = self.local_ident_stack
self.local_ident_stack = local_ident_stack.union([ self.local_ident_stack = local_ident_stack.union([
arg_id(arg) for arg in node.args.args arg_id(arg) for arg in self._expand_tuples(node.args.args)
]) ])
if islambda: if islambda:
self.visit(node.body) self.visit(node.body)
@@ -207,15 +216,14 @@ if _ast:
self.listener.funcname = node.name self.listener.funcname = node.name
argnames = [arg_id(arg) for arg in node.args.args] argnames = [arg_id(arg) for arg in node.args.args]
if node.args.vararg: if node.args.vararg:
argnames.append(node.args.vararg) argnames.append(arg_stringname(node.args.vararg))
if node.args.kwarg: if node.args.kwarg:
argnames.append(node.args.kwarg) argnames.append(arg_stringname(node.args.kwarg))
self.listener.argnames = argnames self.listener.argnames = argnames
self.listener.defaults = node.args.defaults # ast self.listener.defaults = node.args.defaults # ast
self.listener.varargs = node.args.vararg self.listener.varargs = node.args.vararg
self.listener.kwargs = node.args.kwarg self.listener.kwargs = node.args.kwarg
class ExpressionGenerator(object): class ExpressionGenerator(object):
def __init__(self, astnode): def __init__(self, astnode):
@@ -261,6 +269,14 @@ else:
self._add_declared(node.name) self._add_declared(node.name)
self._visit_function(node, args) self._visit_function(node, args)
def _expand_tuples(self, args):
for arg in args:
if isinstance(arg, tuple):
for n in arg:
yield n
else:
yield arg
def _visit_function(self, node, args): def _visit_function(self, node, args):
# push function state onto stack. dont log any more # push function state onto stack. dont log any more
@@ -274,7 +290,7 @@ else:
local_ident_stack = self.local_ident_stack local_ident_stack = self.local_ident_stack
self.local_ident_stack = local_ident_stack.union([ self.local_ident_stack = local_ident_stack.union([
arg for arg in node.argnames arg for arg in self._expand_tuples(node.argnames)
]) ])
for n in node.getChildNodes(): for n in node.getChildNodes():
@@ -524,11 +540,32 @@ else:
self.visit(a) self.visit(a)
self.buf.write(')') self.buf.write(')')
def visitLambda(self, node, *args):
self.buf.write('lambda ')
argnames = list(node.argnames)
kw = arg = None
if node.kwargs > 0:
kw = argnames.pop(-1)
if node.varargs > 0:
arg = argnames.pop(-1)
if arg:
argnames.append("*%s" % arg)
if kw:
argnames.append("**%s" % kw)
self.buf.write(", ".join(argnames))
self.buf.write(': ')
self.visit(node.code)
class walker(visitor.ASTVisitor): class walker(visitor.ASTVisitor):
def dispatch(self, node, *args): def dispatch(self, node, *args):
print 'Node:', str(node) print('Node:', str(node))
# print "dir:", dir(node) # print "dir:", dir(node)
+81 -40
View File
@@ -1,5 +1,5 @@
# mako/runtime.py # mako/runtime.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -7,8 +7,11 @@
"""provides runtime services for templates, including Context, """provides runtime services for templates, including Context,
Namespace, and various helper functions.""" Namespace, and various helper functions."""
from mako import exceptions, util from mako import exceptions, util, compat
import __builtin__, inspect, sys from mako.compat import compat_builtins
import inspect
import sys
import collections
class Context(object): class Context(object):
@@ -32,7 +35,7 @@ class Context(object):
# "capture" function which proxies to the # "capture" function which proxies to the
# generic "capture" function # generic "capture" function
self._data['capture'] = util.partial(capture, self) self._data['capture'] = compat.partial(capture, self)
# "caller" stack used by def calls with content # "caller" stack used by def calls with content
self.caller_stack = self._data['caller'] = CallerStack() self.caller_stack = self._data['caller'] = CallerStack()
@@ -55,8 +58,22 @@ class Context(object):
@property @property
def kwargs(self): def kwargs(self):
"""Return the dictionary of keyword arguments associated with this """Return the dictionary of top level keyword arguments associated
:class:`.Context`. with this :class:`.Context`.
This dictionary only includes the top-level arguments passed to
:meth:`.Template.render`. It does not include names produced within
the template execution such as local variable names or special names
such as ``self``, ``next``, etc.
The purpose of this dictionary is primarily for the case that
a :class:`.Template` accepts arguments via its ``<%page>`` tag,
which are normally expected to be passed via :meth:`.Template.render`,
except the template is being called in an inheritance context,
using the ``body()`` method. :attr:`.Context.kwargs` can then be
used to propagate these arguments to the inheriting template::
${next.body(**context.kwargs)}
""" """
return self._kwargs.copy() return self._kwargs.copy()
@@ -77,13 +94,13 @@ class Context(object):
def keys(self): def keys(self):
"""Return a list of all names established in this :class:`.Context`.""" """Return a list of all names established in this :class:`.Context`."""
return self._data.keys() return list(self._data.keys())
def __getitem__(self, key): def __getitem__(self, key):
if key in self._data: if key in self._data:
return self._data[key] return self._data[key]
else: else:
return __builtin__.__dict__[key] return compat_builtins.__dict__[key]
def _push_writer(self): def _push_writer(self):
"""push a capturing buffer onto this Context and return """push a capturing buffer onto this Context and return
@@ -116,7 +133,7 @@ class Context(object):
"""Return a value from this :class:`.Context`.""" """Return a value from this :class:`.Context`."""
return self._data.get(key, return self._data.get(key,
__builtin__.__dict__.get(key, default) compat_builtins.__dict__.get(key, default)
) )
def write(self, string): def write(self, string):
@@ -141,11 +158,18 @@ class Context(object):
c.caller_stack = self.caller_stack c.caller_stack = self.caller_stack
return c return c
def locals_(self, d): def _locals(self, d):
"""Create a new :class:`.Context` with a copy of this """Create a new :class:`.Context` with a copy of this
:class:`.Context`'s current state, updated with the given dictionary.""" :class:`.Context`'s current state,
updated with the given dictionary.
if len(d) == 0: The :attr:`.Context.kwargs` collection remains
unaffected.
"""
if not d:
return self return self
c = self._copy() c = self._copy()
c._data.update(d) c._data.update(d)
@@ -165,19 +189,27 @@ class Context(object):
class CallerStack(list): class CallerStack(list):
def __init__(self): def __init__(self):
self.nextcaller = None self.nextcaller = None
def __nonzero__(self): def __nonzero__(self):
return self._get_caller() and True or False return self.__bool__()
def __bool__(self):
return len(self) and self._get_caller() and True or False
def _get_caller(self): def _get_caller(self):
# this method can be removed once # this method can be removed once
# codegen MAGIC_NUMBER moves past 7 # codegen MAGIC_NUMBER moves past 7
return self[-1] return self[-1]
def __getattr__(self, key): def __getattr__(self, key):
return getattr(self._get_caller(), key) return getattr(self._get_caller(), key)
def _push_frame(self): def _push_frame(self):
frame = self.nextcaller or None frame = self.nextcaller or None
self.append(frame) self.append(frame)
self.nextcaller = None self.nextcaller = None
return frame return frame
def _pop_frame(self): def _pop_frame(self):
self.nextcaller = self.pop() self.nextcaller = self.pop()
@@ -192,7 +224,11 @@ class Undefined(object):
""" """
def __str__(self): def __str__(self):
raise NameError("Undefined") raise NameError("Undefined")
def __nonzero__(self): def __nonzero__(self):
return self.__bool__()
def __bool__(self):
return False return False
UNDEFINED = Undefined() UNDEFINED = Undefined()
@@ -336,7 +372,7 @@ class Namespace(object):
self.context = context self.context = context
self.inherits = inherits self.inherits = inherits
if callables is not None: if callables is not None:
self.callables = dict([(c.func_name, c) for c in callables]) self.callables = dict([(c.__name__, c) for c in callables])
callables = () callables = ()
@@ -394,8 +430,13 @@ class Namespace(object):
This accessor allows templates to supply "scalar" This accessor allows templates to supply "scalar"
attributes which are particularly handy in inheritance attributes which are particularly handy in inheritance
relationships. See the example in relationships.
:ref:`inheritance_toplevel`.
.. seealso::
:ref:`inheritance_attr`
:ref:`namespace_attr_for_includes`
""" """
return _NSAttr(self) return _NSAttr(self)
@@ -502,7 +543,7 @@ class TemplateNamespace(Namespace):
self.context = context self.context = context
self.inherits = inherits self.inherits = inherits
if callables is not None: if callables is not None:
self.callables = dict([(c.func_name, c) for c in callables]) self.callables = dict([(c.__name__, c) for c in callables])
if templateuri is not None: if templateuri is not None:
self.template = _lookup_template(context, templateuri, self.template = _lookup_template(context, templateuri,
@@ -554,7 +595,7 @@ class TemplateNamespace(Namespace):
yield (key, self.callables[key]) yield (key, self.callables[key])
def get(key): def get(key):
callable_ = self.template._get_def_callable(key) callable_ = self.template._get_def_callable(key)
return util.partial(callable_, self.context) return compat.partial(callable_, self.context)
for k in self.template.module._exports: for k in self.template.module._exports:
yield (k, get(k)) yield (k, get(k))
@@ -563,7 +604,7 @@ class TemplateNamespace(Namespace):
val = self.callables[key] val = self.callables[key]
elif self.template.has_def(key): elif self.template.has_def(key):
callable_ = self.template._get_def_callable(key) callable_ = self.template._get_def_callable(key)
val = util.partial(callable_, self.context) val = compat.partial(callable_, self.context)
elif self.inherits: elif self.inherits:
val = getattr(self.inherits, key) val = getattr(self.inherits, key)
@@ -584,7 +625,7 @@ class ModuleNamespace(Namespace):
self.context = context self.context = context
self.inherits = inherits self.inherits = inherits
if callables is not None: if callables is not None:
self.callables = dict([(c.func_name, c) for c in callables]) self.callables = dict([(c.__name__, c) for c in callables])
mod = __import__(module) mod = __import__(module)
for token in module.split('.')[1:]: for token in module.split('.')[1:]:
@@ -602,19 +643,19 @@ class ModuleNamespace(Namespace):
if self.callables: if self.callables:
for key in self.callables: for key in self.callables:
yield (key, self.callables[key]) yield (key, self.callables[key])
def get(key): for key in dir(self.module):
callable_ = getattr(self.module, key) if key[0] != '_':
return util.partial(callable_, self.context) callable_ = getattr(self.module, key)
for k in dir(self.module): if compat.callable(callable_):
if k[0] != '_': yield key, compat.partial(callable_, self.context)
yield (k, get(k))
def __getattr__(self, key): def __getattr__(self, key):
if key in self.callables: if key in self.callables:
val = self.callables[key] val = self.callables[key]
elif hasattr(self.module, key): elif hasattr(self.module, key):
callable_ = getattr(self.module, key) callable_ = getattr(self.module, key)
val = util.partial(callable_, self.context) val = compat.partial(callable_, self.context)
elif self.inherits: elif self.inherits:
val = getattr(self.inherits, key) val = getattr(self.inherits, key)
else: else:
@@ -648,7 +689,7 @@ def capture(context, callable_, *args, **kwargs):
""" """
if not callable(callable_): if not compat.callable(callable_):
raise exceptions.RuntimeException( raise exceptions.RuntimeException(
"capture() function expects a callable as " "capture() function expects a callable as "
"its argument (i.e. capture(func, *args, **kwargs))" "its argument (i.e. capture(func, *args, **kwargs))"
@@ -704,10 +745,10 @@ def _inherit_from(context, uri, calling_uri):
ih = self_ns ih = self_ns
while ih.inherits is not None: while ih.inherits is not None:
ih = ih.inherits ih = ih.inherits
lclcontext = context.locals_({'next':ih}) lclcontext = context._locals({'next': ih})
ih.inherits = TemplateNamespace("self:%s" % template.uri, ih.inherits = TemplateNamespace("self:%s" % template.uri,
lclcontext, lclcontext,
template = template, template=template,
populate_self=False) populate_self=False)
context._data['parent'] = lclcontext._data['local'] = ih.inherits context._data['parent'] = lclcontext._data['local'] = ih.inherits
callable_ = getattr(template.module, '_mako_inherit', None) callable_ = getattr(template.module, '_mako_inherit', None)
@@ -730,8 +771,8 @@ def _lookup_template(context, uri, relativeto):
uri = lookup.adjust_uri(uri, relativeto) uri = lookup.adjust_uri(uri, relativeto)
try: try:
return lookup.get_template(uri) return lookup.get_template(uri)
except exceptions.TopLevelLookupException, e: except exceptions.TopLevelLookupException:
raise exceptions.TemplateLookupException(str(e)) raise exceptions.TemplateLookupException(str(compat.exception_as()))
def _populate_self_namespace(context, template, self_ns=None): def _populate_self_namespace(context, template, self_ns=None):
if self_ns is None: if self_ns is None:
@@ -750,12 +791,12 @@ def _render(template, callable_, args, data, as_unicode=False):
output of the given template and template callable.""" output of the given template and template callable."""
if as_unicode: if as_unicode:
buf = util.FastEncodingBuffer(unicode=True) buf = util.FastEncodingBuffer(as_unicode=True)
elif template.bytestring_passthrough: elif template.bytestring_passthrough:
buf = util.StringIO() buf = compat.StringIO()
else: else:
buf = util.FastEncodingBuffer( buf = util.FastEncodingBuffer(
unicode=as_unicode, as_unicode=as_unicode,
encoding=template.output_encoding, encoding=template.output_encoding,
errors=template.encoding_errors) errors=template.encoding_errors)
context = Context(buf, **data) context = Context(buf, **data)
@@ -767,7 +808,7 @@ def _render(template, callable_, args, data, as_unicode=False):
return context._pop_buffer().getvalue() return context._pop_buffer().getvalue()
def _kwargs_for_callable(callable_, data): def _kwargs_for_callable(callable_, data):
argspec = util.inspect_func_args(callable_) argspec = compat.inspect_func_args(callable_)
# for normal pages, **pageargs is usually present # for normal pages, **pageargs is usually present
if argspec[2]: if argspec[2]:
return data return data
@@ -781,7 +822,7 @@ def _kwargs_for_callable(callable_, data):
return kwargs return kwargs
def _kwargs_for_include(callable_, data, **kwargs): def _kwargs_for_include(callable_, data, **kwargs):
argspec = util.inspect_func_args(callable_) argspec = compat.inspect_func_args(callable_)
namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None] namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]
for arg in namedargs: for arg in namedargs:
if arg != 'context' and arg in data and arg not in kwargs: if arg != 'context' and arg in data and arg not in kwargs:
@@ -815,8 +856,8 @@ def _exec_template(callable_, context, args=None, kwargs=None):
error = None error = None
try: try:
callable_(context, *args, **kwargs) callable_(context, *args, **kwargs)
except Exception, e: except Exception:
_render_error(template, context, e) _render_error(template, context, compat.exception_as())
except: except:
e = sys.exc_info()[0] e = sys.exc_info()[0]
_render_error(template, context, e) _render_error(template, context, e)
@@ -831,7 +872,7 @@ def _render_error(template, context, error):
else: else:
error_template = exceptions.html_error_template() error_template = exceptions.html_error_template()
if context._outputting_as_unicode: if context._outputting_as_unicode:
context._buffer_stack[:] = [util.FastEncodingBuffer(unicode=True)] context._buffer_stack[:] = [util.FastEncodingBuffer(as_unicode=True)]
else: else:
context._buffer_stack[:] = [util.FastEncodingBuffer( context._buffer_stack[:] = [util.FastEncodingBuffer(
error_template.output_encoding, error_template.output_encoding,
+58 -22
View File
@@ -1,5 +1,5 @@
# mako/template.py # mako/template.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -8,8 +8,15 @@
template strings, as well as template runtime operations.""" template strings, as well as template runtime operations."""
from mako.lexer import Lexer from mako.lexer import Lexer
from mako import runtime, util, exceptions, codegen, cache from mako import runtime, util, exceptions, codegen, cache, compat
import os, re, shutil, stat, sys, tempfile, types, weakref import os
import re
import shutil
import stat
import sys
import tempfile
import types
import weakref
class Template(object): class Template(object):
@@ -112,6 +119,15 @@ class Template(object):
preamble of all generated Python modules. See the example preamble of all generated Python modules. See the example
in :ref:`filtering_default_filters`. in :ref:`filtering_default_filters`.
:param future_imports: String list of names to import from `__future__`.
These will be concatenated into a comma-separated string and inserted
into the beginning of the template, e.g. ``futures_imports=['FOO',
'BAR']`` results in ``from __future__ import FOO, BAR``. If you're
interested in using features like the new division operator, you must
use future_imports to convey that to the renderer, as otherwise the
import will not appear as the first executed statement in the generated
code and will therefore not have the desired effect.
:param input_encoding: Encoding of the template's source code. Can :param input_encoding: Encoding of the template's source code. Can
be used in lieu of the coding comment. See be used in lieu of the coding comment. See
:ref:`usage_unicode` as well as :ref:`unicode_toplevel` for :ref:`usage_unicode` as well as :ref:`unicode_toplevel` for
@@ -154,7 +170,7 @@ class Template(object):
from mako.template import Template from mako.template import Template
mytemplate = Template( mytemplate = Template(
file="index.html", filename="index.html",
module_directory="/path/to/modules", module_directory="/path/to/modules",
module_writer=module_writer module_writer=module_writer
) )
@@ -172,6 +188,12 @@ class Template(object):
result of the callable will be used as the template source result of the callable will be used as the template source
code. code.
:param lexer_cls: A :class:`.Lexer` class used to parse
the template. The :class:`.Lexer` class is used by
default.
.. versionadded:: 0.7.4
:param strict_undefined: Replaces the automatic usage of :param strict_undefined: Replaces the automatic usage of
``UNDEFINED`` for any undeclared variables not located in ``UNDEFINED`` for any undeclared variables not located in
the :class:`.Context` with an immediate raise of the :class:`.Context` with an immediate raise of
@@ -190,6 +212,8 @@ class Template(object):
""" """
lexer_cls = Lexer
def __init__(self, def __init__(self,
text=None, text=None,
filename=None, filename=None,
@@ -215,8 +239,10 @@ class Template(object):
buffer_filters=(), buffer_filters=(),
strict_undefined=False, strict_undefined=False,
imports=None, imports=None,
future_imports=None,
enable_loop=True, enable_loop=True,
preprocessor=None): preprocessor=None,
lexer_cls=None):
if uri: if uri:
self.module_id = re.sub(r'\W', "_", uri) self.module_id = re.sub(r'\W', "_", uri)
self.uri = uri self.uri = uri
@@ -248,7 +274,7 @@ class Template(object):
self.strict_undefined = strict_undefined self.strict_undefined = strict_undefined
self.module_writer = module_writer self.module_writer = module_writer
if util.py3k and disable_unicode: if compat.py3k and disable_unicode:
raise exceptions.UnsupportedError( raise exceptions.UnsupportedError(
"Mako for Python 3 does not " "Mako for Python 3 does not "
"support disabling Unicode") "support disabling Unicode")
@@ -257,7 +283,7 @@ class Template(object):
"output_encoding must be set to " "output_encoding must be set to "
"None when disable_unicode is used.") "None when disable_unicode is used.")
if default_filters is None: if default_filters is None:
if util.py3k or self.disable_unicode: if compat.py3k or self.disable_unicode:
self.default_filters = ['str'] self.default_filters = ['str']
else: else:
self.default_filters = ['unicode'] self.default_filters = ['unicode']
@@ -266,8 +292,12 @@ class Template(object):
self.buffer_filters = buffer_filters self.buffer_filters = buffer_filters
self.imports = imports self.imports = imports
self.future_imports = future_imports
self.preprocessor = preprocessor self.preprocessor = preprocessor
if lexer_cls is not None:
self.lexer_cls = lexer_cls
# if plain text, compile code in memory only # if plain text, compile code in memory only
if text is not None: if text is not None:
(code, module) = _compile_text(self, text, filename) (code, module) = _compile_text(self, text, filename)
@@ -307,6 +337,7 @@ class Template(object):
cache_type, cache_dir, cache_url cache_type, cache_dir, cache_url
) )
@util.memoized_property @util.memoized_property
def reserved_names(self): def reserved_names(self):
if self.enable_loop: if self.enable_loop:
@@ -345,7 +376,7 @@ class Template(object):
filename, filename,
path, path,
self.module_writer) self.module_writer)
module = util.load_module(self.module_id, path) module = compat.load_module(self.module_id, path)
del sys.modules[self.module_id] del sys.modules[self.module_id]
if module._magic_number != codegen.MAGIC_NUMBER: if module._magic_number != codegen.MAGIC_NUMBER:
data = util.read_file(filename) data = util.read_file(filename)
@@ -355,7 +386,7 @@ class Template(object):
filename, filename,
path, path,
self.module_writer) self.module_writer)
module = util.load_module(self.module_id, path) module = compat.load_module(self.module_id, path)
del sys.modules[self.module_id] del sys.modules[self.module_id]
ModuleInfo(module, path, self, filename, None, None) ModuleInfo(module, path, self, filename, None, None)
else: else:
@@ -495,7 +526,7 @@ class ModuleTemplate(Template):
self.bytestring_passthrough = bytestring_passthrough or disable_unicode self.bytestring_passthrough = bytestring_passthrough or disable_unicode
self.enable_loop = module._enable_loop self.enable_loop = module._enable_loop
if util.py3k and disable_unicode: if compat.py3k and disable_unicode:
raise exceptions.UnsupportedError( raise exceptions.UnsupportedError(
"Mako for Python 3 does not " "Mako for Python 3 does not "
"support disabling Unicode") "support disabling Unicode")
@@ -570,13 +601,13 @@ class ModuleInfo(object):
if self.module_source is not None: if self.module_source is not None:
return self.module_source return self.module_source
else: else:
return util.read_file(self.module_filename) return util.read_python_file(self.module_filename)
@property @property
def source(self): def source(self):
if self.template_source is not None: if self.template_source is not None:
if self.module._source_encoding and \ if self.module._source_encoding and \
not isinstance(self.template_source, unicode): not isinstance(self.template_source, compat.text_type):
return self.template_source.decode( return self.template_source.decode(
self.module._source_encoding) self.module._source_encoding)
else: else:
@@ -589,11 +620,11 @@ class ModuleInfo(object):
return data return data
def _compile(template, text, filename, generate_magic_comment): def _compile(template, text, filename, generate_magic_comment):
lexer = Lexer(text, lexer = template.lexer_cls(text,
filename, filename,
disable_unicode=template.disable_unicode, disable_unicode=template.disable_unicode,
input_encoding=template.input_encoding, input_encoding=template.input_encoding,
preprocessor=template.preprocessor) preprocessor=template.preprocessor)
node = lexer.parse() node = lexer.parse()
source = codegen.compile(node, source = codegen.compile(node,
template.uri, template.uri,
@@ -601,6 +632,7 @@ def _compile(template, text, filename, generate_magic_comment):
default_filters=template.default_filters, default_filters=template.default_filters,
buffer_filters=template.buffer_filters, buffer_filters=template.buffer_filters,
imports=template.imports, imports=template.imports,
future_imports=template.future_imports,
source_encoding=lexer.encoding, source_encoding=lexer.encoding,
generate_magic_comment=generate_magic_comment, generate_magic_comment=generate_magic_comment,
disable_unicode=template.disable_unicode, disable_unicode=template.disable_unicode,
@@ -615,19 +647,20 @@ def _compile_text(template, text, filename):
generate_magic_comment=template.disable_unicode) generate_magic_comment=template.disable_unicode)
cid = identifier cid = identifier
if not util.py3k and isinstance(cid, unicode): if not compat.py3k and isinstance(cid, compat.text_type):
cid = cid.encode() cid = cid.encode()
module = types.ModuleType(cid) module = types.ModuleType(cid)
code = compile(source, cid, 'exec') code = compile(source, cid, 'exec')
exec code in module.__dict__, module.__dict__
# this exec() works for 2.4->3.3.
exec(code, module.__dict__, module.__dict__)
return (source, module) return (source, module)
def _compile_module_file(template, text, filename, outputpath, module_writer): def _compile_module_file(template, text, filename, outputpath, module_writer):
identifier = template.module_id
source, lexer = _compile(template, text, filename, source, lexer = _compile(template, text, filename,
generate_magic_comment=True) generate_magic_comment=True)
if isinstance(source, unicode): if isinstance(source, compat.text_type):
source = source.encode(lexer.encoding or 'ascii') source = source.encode(lexer.encoding or 'ascii')
if module_writer: if module_writer:
@@ -643,7 +676,10 @@ def _compile_module_file(template, text, filename, outputpath, module_writer):
shutil.move(name, outputpath) shutil.move(name, outputpath)
def _get_module_info_from_callable(callable_): def _get_module_info_from_callable(callable_):
return _get_module_info(callable_.func_globals['__name__']) if compat.py3k:
return _get_module_info(callable_.__globals__['__name__'])
else:
return _get_module_info(callable_.func_globals['__name__'])
def _get_module_info(filename): def _get_module_info(filename):
return ModuleInfo._modules[filename] return ModuleInfo._modules[filename]
+26 -97
View File
@@ -1,41 +1,15 @@
# mako/util.py # mako/util.py
# Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file> # Copyright (C) 2006-2013 the Mako authors and contributors <see AUTHORS file>
# #
# This module is part of Mako and is released under # This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php # the MIT License: http://www.opensource.org/licenses/mit-license.php
import imp import re
import sys
py3k = getattr(sys, 'py3kwarning', False) or sys.version_info >= (3, 0)
py26 = sys.version_info >= (2, 6)
py24 = sys.version_info >= (2, 4) and sys.version_info < (2, 5)
jython = sys.platform.startswith('java')
win32 = sys.platform.startswith('win')
if py3k:
from io import StringIO
else:
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
import codecs, re, weakref, os, time, operator
import collections import collections
import codecs
try: import os
import threading from mako import compat
import thread import operator
except ImportError:
import dummy_threading as threading
import dummy_thread as thread
if win32 or jython:
time_func = time.clock
else:
time_func = time.time
def function_named(fn, name): def function_named(fn, name):
"""Return a function with a given __name__. """Return a function with a given __name__.
@@ -47,34 +21,6 @@ def function_named(fn, name):
fn.__name__ = name fn.__name__ = name
return fn return fn
try:
from functools import partial
except:
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
return newfunc
if py24:
def all(iterable):
for i in iterable:
if not i:
return False
return True
def exception_name(exc):
try:
return exc.__class__.__name__
except AttributeError:
return exc.__name__
else:
all = all
def exception_name(exc):
return exc.__class__.__name__
class PluginLoader(object): class PluginLoader(object):
def __init__(self, group): def __init__(self, group):
@@ -83,7 +29,7 @@ class PluginLoader(object):
def load(self, name): def load(self, name):
if name in self.impls: if name in self.impls:
return self.impls[name]() return self.impls[name]()
else: else:
import pkg_resources import pkg_resources
for impl in pkg_resources.iter_entry_points( for impl in pkg_resources.iter_entry_points(
@@ -92,6 +38,7 @@ class PluginLoader(object):
self.impls[name] = impl.load self.impls[name] = impl.load
return impl.load() return impl.load()
else: else:
from mako import exceptions
raise exceptions.RuntimeException( raise exceptions.RuntimeException(
"Can't load plugin %s %s" % "Can't load plugin %s %s" %
(self.group, name)) (self.group, name))
@@ -112,7 +59,7 @@ def verify_directory(dir):
while not os.path.exists(dir): while not os.path.exists(dir):
try: try:
tries += 1 tries += 1
os.makedirs(dir, 0775) os.makedirs(dir, compat.octal("0775"))
except: except:
if tries > 5: if tries > 5:
raise raise
@@ -180,14 +127,14 @@ class FastEncodingBuffer(object):
"""a very rudimentary buffer that is faster than StringIO, """a very rudimentary buffer that is faster than StringIO,
but doesn't crash on unicode data like cStringIO.""" but doesn't crash on unicode data like cStringIO."""
def __init__(self, encoding=None, errors='strict', unicode=False): def __init__(self, encoding=None, errors='strict', as_unicode=False):
self.data = collections.deque() self.data = collections.deque()
self.encoding = encoding self.encoding = encoding
if unicode: if as_unicode:
self.delim = u'' self.delim = compat.u('')
else: else:
self.delim = '' self.delim = ''
self.unicode = unicode self.as_unicode = as_unicode
self.errors = errors self.errors = errors
self.write = self.data.append self.write = self.data.append
@@ -215,7 +162,7 @@ class LRUCache(dict):
def __init__(self, key, value): def __init__(self, key, value):
self.key = key self.key = key
self.value = value self.value = value
self.timestamp = time_func() self.timestamp = compat.time_func()
def __repr__(self): def __repr__(self):
return repr(self.value) return repr(self.value)
@@ -225,7 +172,7 @@ class LRUCache(dict):
def __getitem__(self, key): def __getitem__(self, key):
item = dict.__getitem__(self, key) item = dict.__getitem__(self, key)
item.timestamp = time_func() item.timestamp = compat.time_func()
return item.value return item.value
def values(self): def values(self):
@@ -300,9 +247,8 @@ def parse_encoding(fp):
if has_bom: if has_bom:
if m: if m:
raise SyntaxError, \ raise SyntaxError("python refuses to compile code with both a UTF8" \
"python refuses to compile code with both a UTF8" \ " byte-order-mark and a magic encoding comment")
" byte-order-mark and a magic encoding comment"
return 'utf_8' return 'utf_8'
elif m: elif m:
return m.group(1) return m.group(1)
@@ -317,7 +263,7 @@ def sorted_dict_repr(d):
Used by the lexer unit test to compare parse trees based on strings. Used by the lexer unit test to compare parse trees based on strings.
""" """
keys = d.keys() keys = list(d.keys())
keys.sort() keys.sort()
return "{" + ", ".join(["%r: %r" % (k, d[k]) for k in keys]) + "}" return "{" + ", ".join(["%r: %r" % (k, d[k]) for k in keys]) + "}"
@@ -397,28 +343,6 @@ mako in baz not in mako""", '<unknown>', 'exec', _ast.PyCF_ONLY_AST)
_ast.NotIn = type(m.body[12].value.ops[1]) _ast.NotIn = type(m.body[12].value.ops[1])
try:
from inspect import CO_VARKEYWORDS, CO_VARARGS
def inspect_func_args(fn):
co = fn.func_code
nargs = co.co_argcount
names = co.co_varnames
args = list(names[:nargs])
varargs = None
if co.co_flags & CO_VARARGS:
varargs = co.co_varnames[nargs]
nargs = nargs + 1
varkw = None
if co.co_flags & CO_VARKEYWORDS:
varkw = co.co_varnames[nargs]
return args, varargs, varkw, fn.func_defaults
except ImportError:
import inspect
def inspect_func_args(fn):
return inspect.getargspec(fn)
def read_file(path, mode='rb'): def read_file(path, mode='rb'):
fp = open(path, mode) fp = open(path, mode)
@@ -428,9 +352,14 @@ def read_file(path, mode='rb'):
finally: finally:
fp.close() fp.close()
def load_module(module_id, path): def read_python_file(path):
fp = open(path, 'rb') fp = open(path, "rb")
try: try:
return imp.load_source(module_id, path, fp) encoding = parse_encoding(fp)
data = fp.read()
if encoding:
data = data.decode(encoding)
return data
finally: finally:
fp.close() fp.close()