From f6f1328721a898816a93ed91ecb0c59ec9d1b669 Mon Sep 17 00:00:00 2001 From: Bas Stottelaar Date: Tue, 27 Jan 2015 22:06:05 +0100 Subject: [PATCH] Upgrade mako to 1.0.1 --- lib/mako/LICENSE | 20 ++ lib/mako/README.rst | 52 +++ lib/mako/__init__.py | 5 +- lib/mako/_ast_util.py | 60 ++-- lib/mako/ast.py | 84 +++-- lib/mako/cache.py | 10 +- lib/mako/cmd.py | 62 ++++ lib/mako/codegen.py | 181 +++++----- lib/mako/compat.py | 39 ++- lib/mako/exceptions.py | 37 +- lib/mako/ext/autohandler.py | 6 +- lib/mako/ext/babelplugin.py | 135 ++------ lib/mako/ext/beaker_cache.py | 21 +- lib/mako/ext/extract.py | 101 ++++++ lib/mako/ext/linguaplugin.py | 38 ++ lib/mako/ext/preprocessors.py | 10 +- lib/mako/ext/pygmentplugin.py | 2 +- lib/mako/ext/turbogears.py | 2 +- lib/mako/filters.py | 9 +- lib/mako/lexer.py | 64 ++-- lib/mako/lookup.py | 2 +- lib/mako/parsetree.py | 14 +- lib/mako/pygen.py | 29 +- lib/mako/pyparser.py | 630 ++++++++-------------------------- lib/mako/runtime.py | 24 +- lib/mako/template.py | 21 +- lib/mako/util.py | 15 +- lib/requests/README.rst | 85 +++++ 28 files changed, 892 insertions(+), 866 deletions(-) create mode 100644 lib/mako/LICENSE create mode 100644 lib/mako/README.rst create mode 100755 lib/mako/cmd.py create mode 100644 lib/mako/ext/extract.py create mode 100644 lib/mako/ext/linguaplugin.py create mode 100644 lib/requests/README.rst diff --git a/lib/mako/LICENSE b/lib/mako/LICENSE new file mode 100644 index 00000000..24eefa1f --- /dev/null +++ b/lib/mako/LICENSE @@ -0,0 +1,20 @@ +This is the MIT license: http://www.opensource.org/licenses/mit-license.php + +Copyright (C) 2006-2015 the Mako authors and contributors . +Mako is a trademark of Michael Bayer. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/mako/README.rst b/lib/mako/README.rst new file mode 100644 index 00000000..8da9d697 --- /dev/null +++ b/lib/mako/README.rst @@ -0,0 +1,52 @@ +========================= +Mako Templates for Python +========================= + +Mako is a template library written in Python. It provides a familiar, non-XML +syntax which compiles into Python modules for maximum performance. Mako's +syntax and API borrows from the best ideas of many others, including Django +templates, Cheetah, Myghty, and Genshi. Conceptually, Mako is an embedded +Python (i.e. Python Server Page) language, which refines the familiar ideas +of componentized layout and inheritance to produce one of the most +straightforward and flexible models available, while also maintaining close +ties to Python calling and scoping semantics. + +Nutshell +======== + +:: + + <%inherit file="base.html"/> + <% + rows = [[v for v in range(0,10)] for row in range(0,10)] + %> + + % for row in rows: + ${makerow(row)} + % endfor +
+ + <%def name="makerow(row)"> + + % for name in row: + ${name}\ + % endfor + + + +Philosophy +=========== + +Python is a great scripting language. Don't reinvent the wheel...your templates can handle it ! + +Documentation +============== + +See documentation for Mako at http://www.makotemplates.org/docs/ + +License +======== + +Mako is licensed under an MIT-style license (see LICENSE). +Other incorporated projects may be licensed under different licenses. +All licenses allow for non-commercial and commercial use. diff --git a/lib/mako/__init__.py b/lib/mako/__init__.py index cb59f3aa..d9638481 100644 --- a/lib/mako/__init__.py +++ b/lib/mako/__init__.py @@ -1,9 +1,8 @@ # mako/__init__.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -__version__ = '0.9.1' - +__version__ = '1.0.1' diff --git a/lib/mako/_ast_util.py b/lib/mako/_ast_util.py index 3b0bd210..efbc4fc2 100644 --- a/lib/mako/_ast_util.py +++ b/lib/mako/_ast_util.py @@ -1,5 +1,5 @@ # mako/_ast_util.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -34,42 +34,42 @@ from _ast import * from mako.compat import arg_stringname BOOLOP_SYMBOLS = { - And: 'and', - Or: 'or' + And: 'and', + Or: 'or' } BINOP_SYMBOLS = { - Add: '+', - Sub: '-', - Mult: '*', - Div: '/', - FloorDiv: '//', - Mod: '%', - LShift: '<<', - RShift: '>>', - BitOr: '|', - BitAnd: '&', - BitXor: '^' + Add: '+', + Sub: '-', + Mult: '*', + Div: '/', + FloorDiv: '//', + Mod: '%', + LShift: '<<', + RShift: '>>', + BitOr: '|', + BitAnd: '&', + BitXor: '^' } CMPOP_SYMBOLS = { - Eq: '==', - Gt: '>', - GtE: '>=', - In: 'in', - Is: 'is', - IsNot: 'is not', - Lt: '<', - LtE: '<=', - NotEq: '!=', - NotIn: 'not in' + Eq: '==', + Gt: '>', + GtE: '>=', + In: 'in', + Is: 'is', + IsNot: 'is not', + Lt: '<', + LtE: '<=', + NotEq: '!=', + NotIn: 'not in' } UNARYOP_SYMBOLS = { - Invert: '~', - Not: 'not', - UAdd: '+', - USub: '-' + Invert: '~', + Not: 'not', + UAdd: '+', + USub: '-' } ALL_SYMBOLS = {} @@ -215,8 +215,8 @@ def get_compile_mode(node): if not isinstance(node, mod): raise TypeError('expected mod node, got %r' % node.__class__.__name__) return { - Expression: 'eval', - Interactive: 'single' + Expression: 'eval', + Interactive: 'single' }.get(node.__class__, 'expr') diff --git a/lib/mako/ast.py b/lib/mako/ast.py index f9ae3e16..65fd84df 100644 --- a/lib/mako/ast.py +++ b/lib/mako/ast.py @@ -1,5 +1,5 @@ # mako/ast.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -8,7 +8,6 @@ code, as well as generating Python from AST nodes""" from mako import exceptions, pyparser, compat -from mako.compat import arg_stringname import re class PythonCode(object): @@ -107,43 +106,70 @@ class FunctionDecl(object): f.visit(expr) if not hasattr(self, 'funcname'): raise exceptions.CompileException( - "Code '%s' is not a function declaration" % code, - **exception_kwargs) + "Code '%s' is not a function declaration" % code, + **exception_kwargs) if not allow_kwargs and self.kwargs: raise exceptions.CompileException( "'**%s' keyword argument not allowed here" % - self.argnames[-1], **exception_kwargs) + self.kwargnames[-1], **exception_kwargs) - def get_argument_expressions(self, include_defaults=True): - """return the argument declarations of this FunctionDecl as a printable - list.""" + def get_argument_expressions(self, as_call=False): + """Return the argument declarations of this FunctionDecl as a printable + list. + + By default the return value is appropriate for writing in a ``def``; + set `as_call` to true to build arguments to be passed to the function + instead (assuming locals with the same names as the arguments exist). + """ namedecls = [] - defaults = [d for d in self.defaults] - kwargs = self.kwargs - varargs = self.varargs - argnames = [f for f in self.argnames] - argnames.reverse() - for arg in argnames: - default = None - if kwargs: - arg = "**" + arg_stringname(arg) - kwargs = False - elif varargs: - arg = "*" + arg_stringname(arg) - varargs = False + + # Build in reverse order, since defaults and slurpy args come last + argnames = self.argnames[::-1] + kwargnames = self.kwargnames[::-1] + defaults = self.defaults[::-1] + kwdefaults = self.kwdefaults[::-1] + + # Named arguments + if self.kwargs: + namedecls.append("**" + kwargnames.pop(0)) + + for name in kwargnames: + # Keyword-only arguments must always be used by name, so even if + # this is a call, print out `foo=foo` + if as_call: + namedecls.append("%s=%s" % (name, name)) + elif kwdefaults: + default = kwdefaults.pop(0) + if default is None: + # The AST always gives kwargs a default, since you can do + # `def foo(*, a=1, b, c=3)` + namedecls.append(name) + else: + namedecls.append("%s=%s" % ( + name, pyparser.ExpressionGenerator(default).value())) else: - default = len(defaults) and defaults.pop() or None - if include_defaults and default: - namedecls.insert(0, "%s=%s" % - (arg, - pyparser.ExpressionGenerator(default).value() - ) - ) + namedecls.append(name) + + # Positional arguments + if self.varargs: + namedecls.append("*" + argnames.pop(0)) + + for name in argnames: + if as_call or not defaults: + namedecls.append(name) else: - namedecls.insert(0, arg) + default = defaults.pop(0) + namedecls.append("%s=%s" % ( + name, pyparser.ExpressionGenerator(default).value())) + + namedecls.reverse() return namedecls + @property + def allargnames(self): + return tuple(self.argnames) + tuple(self.kwargnames) + class FunctionArgs(FunctionDecl): """the argument portion of a function declaration""" diff --git a/lib/mako/cache.py b/lib/mako/cache.py index 44330404..c405c517 100644 --- a/lib/mako/cache.py +++ b/lib/mako/cache.py @@ -1,5 +1,5 @@ # mako/cache.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -88,9 +88,10 @@ class Cache(object): if not self.template.cache_enabled: return creation_function() - return self.impl.get_or_create(key, - creation_function, - **self._get_cache_kw(kw, context)) + return self.impl.get_or_create( + key, + creation_function, + **self._get_cache_kw(kw, context)) def set(self, key, value, **kw): """Place a value in the cache. @@ -178,6 +179,7 @@ class Cache(object): tmpl_kw.setdefault('context', context) return tmpl_kw + class CacheImpl(object): """Provide a cache implementation for use by :class:`.Cache`.""" diff --git a/lib/mako/cmd.py b/lib/mako/cmd.py new file mode 100755 index 00000000..1a9ca566 --- /dev/null +++ b/lib/mako/cmd.py @@ -0,0 +1,62 @@ +# mako/cmd.py +# Copyright (C) 2006-2015 the Mako authors and contributors +# +# This module is part of Mako and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +from argparse import ArgumentParser +from os.path import isfile, dirname +import sys +from mako.template import Template +from mako.lookup import TemplateLookup +from mako import exceptions + +def varsplit(var): + if "=" not in var: + return (var, "") + return var.split("=", 1) + +def _exit(): + sys.stderr.write(exceptions.text_error_template().render()) + sys.exit(1) + +def cmdline(argv=None): + + parser = ArgumentParser("usage: %prog [FILENAME]") + parser.add_argument("--var", default=[], action="append", + help="variable (can be used multiple times, use name=value)") + parser.add_argument("--template-dir", default=[], action="append", + help="Directory to use for template lookup (multiple " + "directories may be provided). If not given then if the " + "template is read from stdin, the value defaults to be " + "the current directory, otherwise it defaults to be the " + "parent directory of the file provided.") + parser.add_argument('input', nargs='?', default='-') + + options = parser.parse_args(argv) + if options.input == '-': + lookup_dirs = options.template_dir or ["."] + lookup = TemplateLookup(lookup_dirs) + try: + template = Template(sys.stdin.read(), lookup=lookup) + except: + _exit() + else: + filename = options.input + if not isfile(filename): + raise SystemExit("error: can't find %s" % filename) + lookup_dirs = options.template_dir or [dirname(filename)] + lookup = TemplateLookup(lookup_dirs) + try: + template = Template(filename=filename, lookup=lookup) + except: + _exit() + + kw = dict([varsplit(var) for var in options.var]) + try: + print(template.render(**kw)) + except: + _exit() + + +if __name__ == "__main__": + cmdline() diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py index 2779a6d3..4b0bda86 100644 --- a/lib/mako/codegen.py +++ b/lib/mako/codegen.py @@ -1,5 +1,5 @@ # mako/codegen.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -14,7 +14,7 @@ from mako import util, ast, parsetree, filters, exceptions from mako import compat -MAGIC_NUMBER = 9 +MAGIC_NUMBER = 10 # names which are hardwired into the # template and are not accessed via the @@ -99,7 +99,6 @@ class _GenerateRenderMethod(object): """ def __init__(self, printer, compiler, node): self.printer = printer - self.last_source_line = -1 self.compiler = compiler self.node = node self.identifier_stack = [None] @@ -146,6 +145,26 @@ class _GenerateRenderMethod(object): for node in defs: _GenerateRenderMethod(printer, compiler, node) + if not self.in_def: + self.write_metadata_struct() + + def write_metadata_struct(self): + self.printer.source_map[self.printer.lineno] = \ + max(self.printer.source_map) + struct = { + "filename": self.compiler.filename, + "uri": self.compiler.uri, + "source_encoding": self.compiler.source_encoding, + "line_map": self.printer.source_map, + } + self.printer.writelines( + '"""', + '__M_BEGIN_METADATA', + compat.json.dumps(struct), + '__M_END_METADATA\n' + '"""' + ) + @property def identifiers(self): return self.identifier_stack[-1] @@ -187,7 +206,7 @@ class _GenerateRenderMethod(object): # module-level names, python code if self.compiler.generate_magic_comment and \ - self.compiler.source_encoding: + self.compiler.source_encoding: self.printer.writeline("# -*- coding:%s -*-" % self.compiler.source_encoding) @@ -232,7 +251,7 @@ class _GenerateRenderMethod(object): [n.name for n in main_identifiers.topleveldefs.values()] ) - self.printer.write("\n\n") + self.printer.write_blanks(2) if len(module_code): self.write_module_code(module_code) @@ -255,8 +274,9 @@ class _GenerateRenderMethod(object): decorator = node.decorator if decorator: self.printer.writeline( - "@runtime._decorate_toplevel(%s)" % decorator) + "@runtime._decorate_toplevel(%s)" % decorator) + self.printer.start_source(node.lineno) self.printer.writelines( "def %s(%s):" % (name, ','.join(args)), # push new frame, assign current frame to __M_caller @@ -267,7 +287,7 @@ class _GenerateRenderMethod(object): self.printer.writeline("context._push_buffer()") self.identifier_stack.append( - self.compiler.identifiers.branch(self.node)) + self.compiler.identifiers.branch(self.node)) if (not self.in_def or self.node.is_block) and '**pageargs' in args: self.identifier_stack[-1].argument_declared.add('pageargs') @@ -288,7 +308,7 @@ class _GenerateRenderMethod(object): self.write_def_finish(self.node, buffered, filtered, cached) self.printer.writeline(None) - self.printer.write("\n\n") + self.printer.write_blanks(2) if cached: self.write_cache_decorator( node, name, @@ -299,7 +319,7 @@ class _GenerateRenderMethod(object): """write module-level template code, i.e. that which is enclosed in <%! %> tags in the template.""" for n in module_code: - self.write_source_comment(n) + self.printer.start_source(n.lineno) self.printer.write_indented_block(n.text) def write_inherit(self, node): @@ -309,9 +329,9 @@ class _GenerateRenderMethod(object): "def _mako_inherit(template, context):", "_mako_generate_namespaces(context)", "return runtime._inherit_from(context, %s, _template_uri)" % - (node.parsed_attributes['file']), + (node.parsed_attributes['file']), None - ) + ) def write_namespaces(self, namespaces): """write the module-level namespace-generating callable.""" @@ -323,14 +343,14 @@ class _GenerateRenderMethod(object): "_mako_generate_namespaces(context)", "return context.namespaces[(__name__, name)]", None, None - ) + ) self.printer.writeline("def _mako_generate_namespaces(context):") for node in namespaces.values(): if 'import' in node.attributes: self.compiler.has_ns_imports = True - self.write_source_comment(node) + self.printer.start_source(node.lineno) if len(node.nodes): self.printer.writeline("def make_namespace():") export = [] @@ -369,9 +389,9 @@ class _GenerateRenderMethod(object): " templateuri=%s, callables=%s, " " calling_uri=_template_uri)" % ( - node.name, - node.parsed_attributes.get('file', 'None'), - callable_name, + node.name, + node.parsed_attributes.get('file', 'None'), + callable_name, ) ) elif 'module' in node.parsed_attributes: @@ -381,9 +401,10 @@ class _GenerateRenderMethod(object): " callables=%s, calling_uri=_template_uri," " module=%s)" % ( - node.name, - callable_name, - node.parsed_attributes.get('module', 'None') + node.name, + callable_name, + node.parsed_attributes.get( + 'module', 'None') ) ) else: @@ -400,8 +421,8 @@ class _GenerateRenderMethod(object): self.printer.writeline("context['self'].%s = ns" % (node.name)) self.printer.writeline( - "context.namespaces[(__name__, %s)] = ns" % repr(node.name)) - self.printer.write("\n") + "context.namespaces[(__name__, %s)] = ns" % repr(node.name)) + self.printer.write_blanks(1) if not len(namespaces): self.printer.writeline("pass") self.printer.writeline(None) @@ -466,7 +487,7 @@ class _GenerateRenderMethod(object): for ident, ns in self.compiler.namespaces.items(): if 'import' in ns.attributes: self.printer.writeline( - "_mako_get_namespace(context, %r)."\ + "_mako_get_namespace(context, %r)." "_populate(_import_ns, %r)" % ( ident, @@ -532,18 +553,11 @@ class _GenerateRenderMethod(object): self.printer.writeline("__M_writer = context.writer()") - def write_source_comment(self, node): - """write a source comment containing the line number of the - corresponding template line.""" - if self.last_source_line != node.lineno: - self.printer.writeline("# SOURCE LINE %d" % node.lineno) - self.last_source_line = node.lineno - def write_def_decl(self, node, identifiers): """write a locally-available callable referencing a top-level def""" funcname = node.funcname namedecls = node.get_argument_expressions() - nameargs = node.get_argument_expressions(include_defaults=False) + nameargs = node.get_argument_expressions(as_call=True) if not self.in_def and ( len(self.identifiers.locally_assigned) > 0 or @@ -553,7 +567,7 @@ class _GenerateRenderMethod(object): nameargs.insert(0, 'context') self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls))) self.printer.writeline( - "return render_%s(%s)" % (funcname, ",".join(nameargs))) + "return render_%s(%s)" % (funcname, ",".join(nameargs))) self.printer.writeline(None) def write_inline_def(self, node, identifiers, nested): @@ -564,9 +578,9 @@ class _GenerateRenderMethod(object): decorator = node.decorator if decorator: self.printer.writeline( - "@runtime._decorate_inline(context, %s)" % decorator) + "@runtime._decorate_inline(context, %s)" % decorator) self.printer.writeline( - "def %s(%s):" % (node.funcname, ",".join(namedecls))) + "def %s(%s):" % (node.funcname, ",".join(namedecls))) filtered = len(node.filter_args.args) > 0 buffered = eval(node.attributes.get('buffered', 'False')) cached = eval(node.attributes.get('cached', 'False')) @@ -574,11 +588,11 @@ class _GenerateRenderMethod(object): # push new frame, assign current frame to __M_caller "__M_caller = context.caller_stack._push_frame()", "try:" - ) + ) if buffered or filtered or cached: self.printer.writelines( "context._push_buffer()", - ) + ) identifiers = identifiers.branch(node, nested=nested) @@ -627,8 +641,8 @@ class _GenerateRenderMethod(object): ) else: self.printer.writelines( - "finally:", - "__M_buf, __M_writer = context._pop_buffer_and_writer()" + "finally:", + "__M_buf, __M_writer = context._pop_buffer_and_writer()" ) if callstack: @@ -684,7 +698,7 @@ class _GenerateRenderMethod(object): # form "arg1, arg2, arg3=arg3, arg4=arg4", etc. pass_args = [ - '=' in a and "%s=%s" % ((a.split('=')[0],)*2) or a + "%s=%s" % ((a.split('=')[0],) * 2) if '=' in a else a for a in args ] @@ -696,11 +710,11 @@ class _GenerateRenderMethod(object): if buffered: s = "context.get('local')."\ "cache._ctx_get_or_create("\ - "%s, lambda:__M_%s(%s), context, %s__M_defname=%r)" % \ - (cachekey, name, ','.join(pass_args), - ''.join(["%s=%s, " % (k, v) - for k, v in cache_args.items()]), - name + "%s, lambda:__M_%s(%s), context, %s__M_defname=%r)" % ( + cachekey, name, ','.join(pass_args), + ''.join(["%s=%s, " % (k, v) + for k, v in cache_args.items()]), + name ) # apply buffer_filters s = self.create_filter_callable(self.compiler.buffer_filters, s, @@ -709,12 +723,13 @@ class _GenerateRenderMethod(object): else: self.printer.writelines( "__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))" % - (cachekey, name, ','.join(pass_args), - ''.join(["%s=%s, " % (k, v) + ( + cachekey, name, ','.join(pass_args), + ''.join(["%s=%s, " % (k, v) for k, v in cache_args.items()]), - name, + name, ), "return ''", None @@ -745,18 +760,17 @@ class _GenerateRenderMethod(object): continue m = re.match(r'(.+?)(\(.*\))', e) if m: - (ident, fargs) = m.group(1,2) + ident, fargs = m.group(1, 2) f = locate_encode(ident) e = f + fargs else: - x = e e = locate_encode(e) assert e is not None target = "%s(%s)" % (e, target) return target def visitExpression(self, node): - self.write_source_comment(node) + self.printer.start_source(node.lineno) if len(node.escapes) or \ ( self.compiler.pagetag is not None and @@ -778,7 +792,7 @@ class _GenerateRenderMethod(object): self.printer.writeline("loop = __M_loop._exit()") self.printer.writeline(None) else: - self.write_source_comment(node) + self.printer.start_source(node.lineno) if self.compiler.enable_loop and node.keyword == 'for': text = mangle_mako_loop(node, self.printer) else: @@ -800,7 +814,7 @@ class _GenerateRenderMethod(object): self.printer.writeline("pass") def visitText(self, node): - self.write_source_comment(node) + self.printer.start_source(node.lineno) self.printer.writeline("__M_writer(%s)" % repr(node.content)) def visitTextTag(self, node): @@ -822,11 +836,11 @@ class _GenerateRenderMethod(object): "__M_buf.getvalue()", False), None - ) + ) def visitCode(self, node): if not node.ismodule: - self.write_source_comment(node) + self.printer.start_source(node.lineno) self.printer.write_indented_block(node.text) if not self.in_def and len(self.identifiers.locally_assigned) > 0: @@ -835,20 +849,20 @@ class _GenerateRenderMethod(object): # which is used for def calls within the same template, # to simulate "enclosing scope" self.printer.writeline( - '__M_locals_builtin_stored = __M_locals_builtin()') + '__M_locals_builtin_stored = __M_locals_builtin()') self.printer.writeline( - '__M_locals.update(__M_dict_builtin([(__M_key,' - ' __M_locals_builtin_stored[__M_key]) for __M_key in' - ' [%s] if __M_key in __M_locals_builtin_stored]))' % - ','.join([repr(x) for x in node.declared_identifiers()])) + '__M_locals.update(__M_dict_builtin([(__M_key,' + ' __M_locals_builtin_stored[__M_key]) for __M_key in' + ' [%s] if __M_key in __M_locals_builtin_stored]))' % + ','.join([repr(x) for x in node.declared_identifiers()])) def visitIncludeTag(self, node): - self.write_source_comment(node) + self.printer.start_source(node.lineno) args = node.attributes.get('args') if args: self.printer.writeline( - "runtime._include_file(context, %s, _template_uri, %s)" % - (node.parsed_attributes['file'], args)) + "runtime._include_file(context, %s, _template_uri, %s)" % + (node.parsed_attributes['file'], args)) else: self.printer.writeline( "runtime._include_file(context, %s, _template_uri)" % @@ -864,7 +878,7 @@ class _GenerateRenderMethod(object): if node.is_anonymous: self.printer.writeline("%s()" % node.funcname) else: - nameargs = node.get_argument_expressions(include_defaults=False) + nameargs = node.get_argument_expressions(as_call=True) nameargs += ['**pageargs'] self.printer.writeline("if 'parent' not in context._data or " "not hasattr(context._data['parent'], '%s'):" @@ -941,9 +955,9 @@ class _GenerateRenderMethod(object): # push on caller for nested call "context.caller_stack.nextcaller = " "runtime.Namespace('caller', context, " - "callables=ccall(__M_caller))", + "callables=ccall(__M_caller))", "try:") - self.write_source_comment(node) + self.printer.start_source(node.lineno) self.printer.writelines( "__M_writer(%s)" % self.create_filter_callable( [], node.expression, True), @@ -966,9 +980,9 @@ class _Identifiers(object): # things that have already been declared # in an enclosing namespace (i.e. names we can just use) self.declared = set(parent.declared).\ - union([c.name for c in parent.closuredefs.values()]).\ - union(parent.locally_declared).\ - union(parent.argument_declared) + union([c.name for c in parent.closuredefs.values()]).\ + union(parent.locally_declared).\ + union(parent.argument_declared) # if these identifiers correspond to a "nested" # scope, it means whatever the parent identifiers @@ -1012,7 +1026,7 @@ class _Identifiers(object): node.accept_visitor(self) illegal_names = self.compiler.reserved_names.intersection( - self.locally_declared) + self.locally_declared) if illegal_names: raise exceptions.NameConflictError( "Reserved words declared in template: %s" % @@ -1047,7 +1061,7 @@ class _Identifiers(object): for ident in node.undeclared_identifiers(): 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) for ident in node.declared_identifiers(): self.locally_declared.add(ident) @@ -1067,7 +1081,7 @@ class _Identifiers(object): if not node.ismodule: self.check_declared(node) self.locally_assigned = self.locally_assigned.union( - node.declared_identifiers()) + node.declared_identifiers()) def visitNamespaceTag(self, node): # only traverse into the sub-elements of a @@ -1095,8 +1109,8 @@ class _Identifiers(object): self._check_name_exists(self.closuredefs, node) for ident in node.undeclared_identifiers(): - if ident != 'context' and\ - ident not in self.declared.union(self.locally_declared): + if ident != 'context' and \ + ident not in self.declared.union(self.locally_declared): self.undeclared.add(ident) # visit defs only one level deep @@ -1108,8 +1122,7 @@ class _Identifiers(object): n.accept_visitor(self) def visitBlockTag(self, node): - if node is not self.node and \ - not node.is_anonymous: + if node is not self.node and not node.is_anonymous: if isinstance(self.node, parsetree.DefTag): raise exceptions.CompileException( @@ -1123,7 +1136,7 @@ class _Identifiers(object): for ident in node.undeclared_identifiers(): 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) if not node.is_anonymous: @@ -1139,7 +1152,7 @@ class _Identifiers(object): def visitTextTag(self, node): for ident in node.undeclared_identifiers(): 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) def visitIncludeTag(self, node): @@ -1156,8 +1169,9 @@ class _Identifiers(object): def visitCallTag(self, node): if node is self.node: for ident in node.undeclared_identifiers(): - if ident != 'context' and\ - ident not in self.declared.union(self.locally_declared): + if ident != 'context' and \ + ident not in self.declared.union( + self.locally_declared): self.undeclared.add(ident) for ident in node.declared_identifiers(): self.argument_declared.add(ident) @@ -1165,15 +1179,16 @@ class _Identifiers(object): n.accept_visitor(self) else: for ident in node.undeclared_identifiers(): - if ident != 'context' and\ - ident not in self.declared.union(self.locally_declared): + if ident != 'context' and \ + ident not in self.declared.union( + self.locally_declared): self.undeclared.add(ident) _FOR_LOOP = re.compile( r'^for\s+((?:\(?)\s*[A-Za-z_][A-Za-z_0-9]*' r'(?:\s*,\s*(?:[A-Za-z_][A-Za-z0-9_]*),??)*\s*(?:\)?))\s+in\s+(.*):' - ) +) def mangle_mako_loop(node, printer): """converts a for loop into a context manager wrapped around a for loop @@ -1189,7 +1204,7 @@ def mangle_mako_loop(node, printer): 'loop = __M_loop._enter(%s)' % match.group(2), 'try:' #'with __M_loop(%s) as loop:' % match.group(2) - ) + ) text = 'for %s in loop:' % match.group(1) else: raise SyntaxError("Couldn't apply loop context: %s" % node.text) diff --git a/lib/mako/compat.py b/lib/mako/compat.py index 31da8bd3..fe277bbf 100644 --- a/lib/mako/compat.py +++ b/lib/mako/compat.py @@ -3,8 +3,8 @@ import time py3k = sys.version_info >= (3, 0) py33 = sys.version_info >= (3, 3) +py2k = sys.version_info < (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') @@ -23,6 +23,9 @@ if py3k: def u(s): return s + def b(s): + return s.encode("latin-1") + def octal(lit): return eval("0o" + lit) @@ -44,6 +47,9 @@ else: def u(s): return unicode(s, "utf-8") + def b(s): + return s + def octal(lit): return eval("0" + lit) @@ -62,6 +68,18 @@ else: fp.close() +if py3k: + def reraise(tp, value, tb=None, cause=None): + if cause is not None: + value.__cause__ = cause + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value +else: + exec("def reraise(tp, value, tb=None, cause=None):\n" + " raise tp, value, tb\n") + + def exception_as(): return sys.exc_info()[1] @@ -93,23 +111,12 @@ except: 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 +all = all +import json - def exception_name(exc): - return exc.__class__.__name__ +def exception_name(exc): + return exc.__class__.__name__ try: from inspect import CO_VARKEYWORDS, CO_VARARGS diff --git a/lib/mako/exceptions.py b/lib/mako/exceptions.py index dcf81a61..c531f211 100644 --- a/lib/mako/exceptions.py +++ b/lib/mako/exceptions.py @@ -1,5 +1,5 @@ # mako/exceptions.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -8,7 +8,6 @@ import traceback import sys -import re from mako import util, compat class MakoException(Exception): @@ -28,7 +27,7 @@ class CompileException(MakoException): def __init__(self, message, source, lineno, pos, filename): MakoException.__init__(self, message + _format_filepos(lineno, pos, filename)) - self.lineno =lineno + self.lineno = lineno self.pos = pos self.filename = filename self.source = source @@ -37,7 +36,7 @@ class SyntaxException(MakoException): def __init__(self, message, source, lineno, pos, filename): MakoException.__init__(self, message + _format_filepos(lineno, pos, filename)) - self.lineno =lineno + self.lineno = lineno self.pos = pos self.filename = filename self.source = source @@ -77,7 +76,6 @@ class RichTraceback(object): self.records = self._init(traceback) if isinstance(self.error, (CompileException, SyntaxException)): - import mako.template self.source = self.error.source self.lineno = self.error.lineno self._has_source = True @@ -167,19 +165,19 @@ class RichTraceback(object): None, None, None, None)) continue - template_ln = module_ln = 1 - line_map = {} - for line in module_source.split("\n"): - match = re.match(r'\s*# SOURCE LINE (\d+)', line) - if match: - template_ln = int(match.group(1)) - module_ln += 1 - line_map[module_ln] = template_ln + template_ln = 1 + + source_map = mako.template.ModuleInfo.\ + get_module_source_metadata( + module_source, full_line_map=True) + line_map = source_map['full_line_map'] + template_lines = [line for line in template_source.split("\n")] mods[filename] = (line_map, template_lines) - template_ln = line_map[lineno] + template_ln = line_map[lineno - 1] + if template_ln <= len(template_lines): template_line = template_lines[template_ln - 1] else: @@ -188,7 +186,7 @@ class RichTraceback(object): line, template_filename, template_ln, template_line, template_source)) if not self.source: - for l in range(len(new_trcback)-1, 0, -1): + for l in range(len(new_trcback) - 1, 0, -1): if new_trcback[l][5]: self.source = new_trcback[l][7] self.lineno = new_trcback[l][5] @@ -260,10 +258,11 @@ def html_error_template(): filenames, line numbers and code for that of the originating source template, as applicable. - The template's default ``encoding_errors`` value is ``'htmlentityreplace'``. The - template has two options. With the ``full`` option disabled, only a section of - an HTML document is returned. With the ``css`` option disabled, the default - stylesheet won't be included. + The template's default ``encoding_errors`` value is + ``'htmlentityreplace'``. The template has two options. With the + ``full`` option disabled, only a section of an HTML document is + returned. With the ``css`` option disabled, the default stylesheet + won't be included. """ import mako.template diff --git a/lib/mako/ext/autohandler.py b/lib/mako/ext/autohandler.py index 7bd1c71f..8deaae19 100644 --- a/lib/mako/ext/autohandler.py +++ b/lib/mako/ext/autohandler.py @@ -1,5 +1,5 @@ # ext/autohandler.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -48,7 +48,7 @@ def autohandler(template, context, name='autohandler'): if len(tokens) == 1: break tokens[-2:] = [name] - + if not lookup.filesystem_checks: return lookup._uri_cache.setdefault( (autohandler, _template_uri, name), None) @@ -62,4 +62,4 @@ def _file_exists(lookup, path): return True else: return False - + diff --git a/lib/mako/ext/babelplugin.py b/lib/mako/ext/babelplugin.py index 538c0484..ead70811 100644 --- a/lib/mako/ext/babelplugin.py +++ b/lib/mako/ext/babelplugin.py @@ -1,14 +1,36 @@ # ext/babelplugin.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php """gettext message extraction via Babel: http://babel.edgewall.org/""" from babel.messages.extract import extract_python -from mako.compat import StringIO -from mako import compat -from mako import lexer, parsetree +from mako.ext.extract import MessageExtractor + + +class BabelMakoExtractor(MessageExtractor): + def __init__(self, keywords, comment_tags, options): + self.keywords = keywords + self.options = options + self.config = { + 'comment-tags': u' '.join(comment_tags), + 'encoding': options.get('input_encoding', + options.get('encoding', None)), + } + super(BabelMakoExtractor, self).__init__() + + def __call__(self, fileobj): + return self.process_file(fileobj) + + def process_python(self, code, code_lineno, translator_strings): + comment_tags = self.config['comment-tags'] + for lineno, funcname, messages, python_translator_comments \ + in extract_python(code, + self.keywords, comment_tags, self.options): + yield (code_lineno + (lineno - 1), funcname, messages, + translator_strings + python_translator_comments) + def extract(fileobj, keywords, comment_tags, options): """Extract messages from Mako templates. @@ -22,105 +44,6 @@ def extract(fileobj, keywords, comment_tags, options): :return: an iterator over ``(lineno, funcname, message, comments)`` tuples :rtype: ``iterator`` """ - encoding = options.get('input_encoding', options.get('encoding', None)) - - template_node = lexer.Lexer(fileobj.read(), - input_encoding=encoding).parse() - for extracted in extract_nodes(template_node.get_children(), - keywords, comment_tags, options): - yield extracted - -def extract_nodes(nodes, keywords, comment_tags, options): - """Extract messages from Mako's lexer node objects - - :param nodes: an iterable of Mako parsetree.Node objects to extract from - :param keywords: a list of keywords (i.e. function names) that should be - recognized as translation functions - :param comment_tags: a list of translator tags to search for and include - in the results - :param options: a dictionary of additional options (optional) - :return: an iterator over ``(lineno, funcname, message, comments)`` tuples - :rtype: ``iterator`` - """ - translator_comments = [] - in_translator_comments = False - - for node in nodes: - child_nodes = None - if in_translator_comments and isinstance(node, parsetree.Text) and \ - not node.content.strip(): - # Ignore whitespace within translator comments - continue - - if isinstance(node, parsetree.Comment): - value = node.text.strip() - if in_translator_comments: - translator_comments.extend(_split_comment(node.lineno, value)) - continue - for comment_tag in comment_tags: - if value.startswith(comment_tag): - in_translator_comments = True - translator_comments.extend(_split_comment(node.lineno, - value)) - continue - - if isinstance(node, parsetree.DefTag): - code = node.function_decl.code - child_nodes = node.nodes - elif isinstance(node, parsetree.BlockTag): - code = node.body_decl.code - child_nodes = node.nodes - elif isinstance(node, parsetree.CallTag): - code = node.code.code - child_nodes = node.nodes - elif isinstance(node, parsetree.PageTag): - code = node.body_decl.code - elif isinstance(node, parsetree.CallNamespaceTag): - code = node.expression - child_nodes = node.nodes - elif isinstance(node, parsetree.ControlLine): - if node.isend: - in_translator_comments = False - continue - code = node.text - elif isinstance(node, parsetree.Code): - in_translator_comments = False - code = node.code.code - elif isinstance(node, parsetree.Expression): - code = node.code.code - else: - continue - - # Comments don't apply unless they immediately preceed the message - if translator_comments and \ - translator_comments[-1][0] < node.lineno - 1: - translator_comments = [] - - translator_strings = [comment[1] for comment in translator_comments] - - if isinstance(code, compat.text_type): - code = code.encode('ascii', 'backslashreplace') - - used_translator_comments = False - code = compat.byte_buffer(code) - for lineno, funcname, messages, python_translator_comments \ - in extract_python(code, keywords, comment_tags, options): - yield (node.lineno + (lineno - 1), funcname, messages, - translator_strings + python_translator_comments) - used_translator_comments = True - - if used_translator_comments: - translator_comments = [] - in_translator_comments = False - - if child_nodes: - for extracted in extract_nodes(child_nodes, keywords, comment_tags, - options): - yield extracted - - -def _split_comment(lineno, comment): - """Return the multiline comment at lineno split into a list of comment line - numbers and the accompanying comment line""" - return [(lineno + index, line) for index, line in - enumerate(comment.splitlines())] + extractor = BabelMakoExtractor(keywords, comment_tags, options) + for message in extractor(fileobj): + yield message diff --git a/lib/mako/ext/beaker_cache.py b/lib/mako/ext/beaker_cache.py index c9fbf0a5..40ef7745 100644 --- a/lib/mako/ext/beaker_cache.py +++ b/lib/mako/ext/beaker_cache.py @@ -4,7 +4,16 @@ from mako import exceptions from mako.cache import CacheImpl +try: + from beaker import cache as beaker_cache +except: + has_beaker = False +else: + has_beaker = True + _beaker_cache = None + + class BeakerCacheImpl(CacheImpl): """A :class:`.CacheImpl` provided for the Beaker caching system. @@ -15,15 +24,11 @@ class BeakerCacheImpl(CacheImpl): """ def __init__(self, cache): + if not has_beaker: + raise exceptions.RuntimeException( + "Can't initialize Beaker plugin; Beaker is not installed.") global _beaker_cache if _beaker_cache is None: - try: - from beaker import cache as beaker_cache - except ImportError: - raise exceptions.RuntimeException( - "the Beaker package is required to use cache " - "functionality.") - if 'manager' in cache.template.cache_args: _beaker_cache = cache.template.cache_args['manager'] else: @@ -48,7 +53,7 @@ class BeakerCacheImpl(CacheImpl): cache = _beaker_cache.get_cache_region(self.cache.id, region, **kw) else: cache = _beaker_cache.get_cache(self.cache.id, **kw) - cache_args = {'starttime':self.cache.starttime} + cache_args = {'starttime': self.cache.starttime} if expiretime: cache_args['expiretime'] = expiretime return cache, cache_args diff --git a/lib/mako/ext/extract.py b/lib/mako/ext/extract.py new file mode 100644 index 00000000..5ce51757 --- /dev/null +++ b/lib/mako/ext/extract.py @@ -0,0 +1,101 @@ +import re +from mako import compat +from mako import lexer +from mako import parsetree + + +class MessageExtractor(object): + def process_file(self, fileobj): + template_node = lexer.Lexer( + fileobj.read(), + input_encoding=self.config['encoding']).parse() + for extracted in self.extract_nodes(template_node.get_children()): + yield extracted + + def extract_nodes(self, nodes): + translator_comments = [] + in_translator_comments = False + comment_tags = list( + filter(None, re.split(r'\s+', self.config['comment-tags']))) + + for node in nodes: + child_nodes = None + if in_translator_comments and \ + isinstance(node, parsetree.Text) and \ + not node.content.strip(): + # Ignore whitespace within translator comments + continue + + if isinstance(node, parsetree.Comment): + value = node.text.strip() + if in_translator_comments: + translator_comments.extend( + self._split_comment(node.lineno, value)) + continue + for comment_tag in comment_tags: + if value.startswith(comment_tag): + in_translator_comments = True + translator_comments.extend( + self._split_comment(node.lineno, value)) + continue + + if isinstance(node, parsetree.DefTag): + code = node.function_decl.code + child_nodes = node.nodes + elif isinstance(node, parsetree.BlockTag): + code = node.body_decl.code + child_nodes = node.nodes + elif isinstance(node, parsetree.CallTag): + code = node.code.code + child_nodes = node.nodes + elif isinstance(node, parsetree.PageTag): + code = node.body_decl.code + elif isinstance(node, parsetree.CallNamespaceTag): + code = node.expression + child_nodes = node.nodes + elif isinstance(node, parsetree.ControlLine): + if node.isend: + in_translator_comments = False + continue + code = node.text + elif isinstance(node, parsetree.Code): + in_translator_comments = False + code = node.code.code + elif isinstance(node, parsetree.Expression): + code = node.code.code + else: + continue + + # Comments don't apply unless they immediately preceed the message + if translator_comments and \ + translator_comments[-1][0] < node.lineno - 1: + translator_comments = [] + + translator_strings = [ + comment[1] for comment in translator_comments] + + if isinstance(code, compat.text_type): + code = code.encode('ascii', 'backslashreplace') + + used_translator_comments = False + code = compat.byte_buffer(code) + + for message in self.process_python( + code, node.lineno, translator_strings): + yield message + used_translator_comments = True + + if used_translator_comments: + translator_comments = [] + in_translator_comments = False + + if child_nodes: + for extracted in self.extract_nodes(child_nodes): + yield extracted + + @staticmethod + def _split_comment(lineno, comment): + """Return the multiline comment at lineno split into a list of + comment line numbers and the accompanying comment line""" + return [(lineno + index, line) for index, line in + enumerate(comment.splitlines())] diff --git a/lib/mako/ext/linguaplugin.py b/lib/mako/ext/linguaplugin.py new file mode 100644 index 00000000..a809072c --- /dev/null +++ b/lib/mako/ext/linguaplugin.py @@ -0,0 +1,38 @@ +import io +from lingua.extractors import Extractor +from lingua.extractors import Message +from lingua.extractors import get_extractor +from mako.ext.extract import MessageExtractor +from mako import compat + + +class LinguaMakoExtractor(Extractor, MessageExtractor): + '''Mako templates''' + extensions = ['.mako'] + default_config = { + 'encoding': 'utf-8', + 'comment-tags': '', + } + + def __call__(self, filename, options, fileobj=None): + self.options = options + self.filename = filename + self.python_extractor = get_extractor('x.py') + if fileobj is None: + fileobj = open(filename, 'rb') + return self.process_file(fileobj) + + def process_python(self, code, code_lineno, translator_strings): + source = code.getvalue().strip() + if source.endswith(compat.b(':')): + source += compat.b(' pass') + code = io.BytesIO(source) + for msg in self.python_extractor( + self.filename, self.options, code, code_lineno): + if translator_strings: + msg = Message(msg.msgctxt, msg.msgid, msg.msgid_plural, + msg.flags, + compat.u(' ').join( + translator_strings + [msg.comment]), + msg.tcomment, msg.location) + yield msg diff --git a/lib/mako/ext/preprocessors.py b/lib/mako/ext/preprocessors.py index acd32e55..c24893bd 100644 --- a/lib/mako/ext/preprocessors.py +++ b/lib/mako/ext/preprocessors.py @@ -1,20 +1,20 @@ # ext/preprocessors.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -"""preprocessing functions, used with the 'preprocessor' +"""preprocessing functions, used with the 'preprocessor' argument on Template, TemplateLookup""" import re def convert_comments(text): """preprocess old style comments. - + example: - + from mako.ext.preprocessors import convert_comments - t = Template(..., preprocessor=preprocess_comments)""" + t = Template(..., preprocessor=convert_comments)""" return re.sub(r'(?<=\n)\s*#[^#]', "##", text) diff --git a/lib/mako/ext/pygmentplugin.py b/lib/mako/ext/pygmentplugin.py index 040a25cf..3adcfb8c 100644 --- a/lib/mako/ext/pygmentplugin.py +++ b/lib/mako/ext/pygmentplugin.py @@ -1,5 +1,5 @@ # ext/pygmentplugin.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php diff --git a/lib/mako/ext/turbogears.py b/lib/mako/ext/turbogears.py index a4179d05..d3976d97 100644 --- a/lib/mako/ext/turbogears.py +++ b/lib/mako/ext/turbogears.py @@ -1,5 +1,5 @@ # ext/turbogears.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php diff --git a/lib/mako/filters.py b/lib/mako/filters.py index 77aaf756..d79ce238 100644 --- a/lib/mako/filters.py +++ b/lib/mako/filters.py @@ -1,5 +1,5 @@ # mako/filters.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -48,6 +48,10 @@ def url_escape(string): string = string.encode("utf8") return quote_plus(string) +def legacy_url_escape(string): + # convert into a list of octets + return quote_plus(string) + def url_unescape(string): text = unquote_plus(string) if not is_ascii_str(text): @@ -64,7 +68,7 @@ class Decode(object): if isinstance(x, compat.text_type): return x elif not isinstance(x, compat.binary_type): - return compat.text_type(str(x), encoding=key) + return decode(str(x)) else: return compat.text_type(x, encoding=key) return decode @@ -193,4 +197,5 @@ if compat.py3k: NON_UNICODE_ESCAPES = DEFAULT_ESCAPES.copy() NON_UNICODE_ESCAPES['h'] = 'filters.legacy_html_escape' +NON_UNICODE_ESCAPES['u'] = 'filters.legacy_url_escape' diff --git a/lib/mako/lexer.py b/lib/mako/lexer.py index 42b9ecdc..1dda3982 100644 --- a/lib/mako/lexer.py +++ b/lib/mako/lexer.py @@ -1,5 +1,5 @@ # mako/lexer.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -44,10 +44,10 @@ class Lexer(object): @property def exception_kwargs(self): - return {'source':self.text, - 'lineno':self.matched_lineno, - 'pos':self.matched_charpos, - 'filename':self.filename} + return {'source': self.text, + 'lineno': self.matched_lineno, + 'pos': self.matched_charpos, + 'filename': self.filename} def match(self, regexp, flags=None): """compile the given regexp, cache the reg, and call match_reg().""" @@ -83,8 +83,8 @@ class Lexer(object): self.matched_lineno = self.lineno lines = re.findall(r"\n", self.text[mp:self.match_position]) cp = mp - 1 - while (cp >= 0 and cp= 0 and cp < self.textlength and self.text[cp] != '\n'): + cp -= 1 self.matched_charpos = mp - cp self.lineno += len(lines) #print "MATCHED:", match.group(0), "LINE START:", @@ -111,8 +111,8 @@ class Lexer(object): brace_level -= 1 continue return \ - self.text[startpos:\ - self.match_position-len(match.group(1))],\ + self.text[startpos: + self.match_position - len(match.group(1))],\ match.group(1) match = self.match(r"(.*?)(?=\"|\'|#|%s)" % text_re, re.S) if match: @@ -162,9 +162,9 @@ class Lexer(object): elif self.control_line and \ not self.control_line[-1].is_ternary(node.keyword): raise exceptions.SyntaxException( - "Keyword '%s' not a legal ternary for keyword '%s'" % - (node.keyword, self.control_line[-1].keyword), - **self.exception_kwargs) + "Keyword '%s' not a legal ternary for keyword '%s'" % + (node.keyword, self.control_line[-1].keyword), + **self.exception_kwargs) _coding_re = re.compile(r'#.*coding[:=]\s*([-\w.]+).*\r?\n') @@ -201,10 +201,10 @@ class Lexer(object): text = text.decode(parsed_encoding) except UnicodeDecodeError: raise exceptions.CompileException( - "Unicode decode operation of encoding '%s' failed" % - parsed_encoding, - text.decode('utf-8', 'ignore'), - 0, 0, filename) + "Unicode decode operation of encoding '%s' failed" % + parsed_encoding, + text.decode('utf-8', 'ignore'), + 0, 0, filename) return parsed_encoding, text @@ -254,11 +254,11 @@ class Lexer(object): **self.exception_kwargs) if len(self.control_line): raise exceptions.SyntaxException( - "Unterminated control keyword: '%s'" % - self.control_line[-1].keyword, - self.text, - self.control_line[-1].lineno, - self.control_line[-1].pos, self.filename) + "Unterminated control keyword: '%s'" % + self.control_line[-1].keyword, + self.text, + self.control_line[-1].lineno, + self.control_line[-1].pos, self.filename) return self.template def match_tag_start(self): @@ -311,14 +311,14 @@ class Lexer(object): if match: if not len(self.tag): raise exceptions.SyntaxException( - "Closing tag without opening tag: " % - match.group(1), - **self.exception_kwargs) + "Closing tag without opening tag: " % + match.group(1), + **self.exception_kwargs) elif self.tag[-1].keyword != match.group(1): raise exceptions.SyntaxException( - "Closing tag does not match tag: <%%%s>" % - (match.group(1), self.tag[-1].keyword), - **self.exception_kwargs) + "Closing tag does not match tag: <%%%s>" % + (match.group(1), self.tag[-1].keyword), + **self.exception_kwargs) self.tag.pop() return True else: @@ -370,9 +370,9 @@ class Lexer(object): # compiler.parse() not complain about indentation text = adjust_whitespace(text) + "\n" self.append_node( - parsetree.Code, - text, - match.group(1)=='!', lineno=line, pos=pos) + parsetree.Code, + text, + match.group(1) == '!', lineno=line, pos=pos) return True else: return False @@ -397,8 +397,8 @@ class Lexer(object): def match_control_line(self): match = self.match( - r"(?<=^)[\t ]*(%(?!%)|##)[\t ]*((?:(?:\\r?\n)|[^\r\n])*)" - r"(?:\r?\n|\Z)", re.M) + r"(?<=^)[\t ]*(%(?!%)|##)[\t ]*((?:(?:\\r?\n)|[^\r\n])*)" + r"(?:\r?\n|\Z)", re.M) if match: operator = match.group(1) text = match.group(2) diff --git a/lib/mako/lookup.py b/lib/mako/lookup.py index 3edd1016..2af54119 100644 --- a/lib/mako/lookup.py +++ b/lib/mako/lookup.py @@ -1,5 +1,5 @@ # mako/lookup.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php diff --git a/lib/mako/parsetree.py b/lib/mako/parsetree.py index 1c04c269..49ec4e06 100644 --- a/lib/mako/parsetree.py +++ b/lib/mako/parsetree.py @@ -1,5 +1,5 @@ # mako/parsetree.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -437,7 +437,7 @@ class DefTag(Tag): return self.function_decl.get_argument_expressions(**kw) def declared_identifiers(self): - return self.function_decl.argnames + return self.function_decl.allargnames def undeclared_identifiers(self): res = [] @@ -451,7 +451,7 @@ class DefTag(Tag): ).union( self.expression_undeclared_identifiers ).difference( - self.function_decl.argnames + self.function_decl.allargnames ) class BlockTag(Tag): @@ -502,7 +502,7 @@ class BlockTag(Tag): return self.body_decl.get_argument_expressions(**kw) def declared_identifiers(self): - return self.body_decl.argnames + return self.body_decl.allargnames def undeclared_identifiers(self): return (self.filter_args.\ @@ -524,7 +524,7 @@ class CallTag(Tag): **self.exception_kwargs) def declared_identifiers(self): - return self.code.declared_identifiers.union(self.body_decl.argnames) + return self.code.declared_identifiers.union(self.body_decl.allargnames) def undeclared_identifiers(self): return self.code.undeclared_identifiers.\ @@ -554,7 +554,7 @@ class CallNamespaceTag(Tag): **self.exception_kwargs) def declared_identifiers(self): - return self.code.declared_identifiers.union(self.body_decl.argnames) + return self.code.declared_identifiers.union(self.body_decl.allargnames) def undeclared_identifiers(self): return self.code.undeclared_identifiers.\ @@ -589,6 +589,6 @@ class PageTag(Tag): **self.exception_kwargs) def declared_identifiers(self): - return self.body_decl.argnames + return self.body_decl.allargnames diff --git a/lib/mako/pygen.py b/lib/mako/pygen.py index ed7ccc7b..5ba5125a 100644 --- a/lib/mako/pygen.py +++ b/lib/mako/pygen.py @@ -1,5 +1,5 @@ # mako/pygen.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -26,6 +26,9 @@ class PythonPrinter(object): # the stream we are writing to self.stream = stream + # current line number + self.lineno = 1 + # a list of lines that represents a buffered "block" of code, # which can be later printed relative to an indent level self.line_buffer = [] @@ -34,8 +37,20 @@ class PythonPrinter(object): self._reset_multi_line_flags() - def write(self, text): - self.stream.write(text) + # mapping of generated python lines to template + # source lines + self.source_map = {} + + def _update_lineno(self, num): + self.lineno += num + + def start_source(self, lineno): + if self.lineno not in self.source_map: + self.source_map[self.lineno] = lineno + + def write_blanks(self, num): + self.stream.write("\n" * num) + self._update_lineno(num) def write_indented_block(self, block): """print a line or lines of python which already contain indentation. @@ -45,6 +60,7 @@ class PythonPrinter(object): self.in_indent_lines = False for l in re.split(r'\r?\n', block): self.line_buffer.append(l) + self._update_lineno(1) def writelines(self, *lines): """print a series of lines of python.""" @@ -80,7 +96,7 @@ class PythonPrinter(object): ): if self.indent > 0: - self.indent -=1 + self.indent -= 1 # if the indent_detail stack is empty, the user # probably put extra closures - the resulting # module wont compile. @@ -94,6 +110,7 @@ class PythonPrinter(object): # write the line self.stream.write(self._indent_line(line) + "\n") + self._update_lineno(len(line.split("\n"))) # see if this line should increase the indentation level. # note that a line can both decrase (before printing) and @@ -108,7 +125,7 @@ class PythonPrinter(object): if match: # its a "compound" keyword, so we will check for "unindentors" indentor = match.group(1) - self.indent +=1 + self.indent += 1 self.indent_detail.append(indentor) else: indentor = None @@ -265,7 +282,7 @@ def adjust_whitespace(text): return start_state - def _indent_line(line, stripspace = ''): + def _indent_line(line, stripspace=''): return re.sub(r"^%s" % stripspace, '', line) lines = [] diff --git a/lib/mako/pyparser.py b/lib/mako/pyparser.py index 75301cc3..bfa46a9f 100644 --- a/lib/mako/pyparser.py +++ b/lib/mako/pyparser.py @@ -1,5 +1,5 @@ # mako/pyparser.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -11,7 +11,7 @@ module is used. """ from mako import exceptions, util, compat -from mako.compat import StringIO, arg_stringname +from mako.compat import arg_stringname import operator if compat.py3k: @@ -29,29 +29,16 @@ else: # the "id" attribute on a function node arg_id = operator.attrgetter('id') - -try: - import _ast - util.restore__ast(_ast) - from mako import _ast_util -except ImportError: - _ast = None - from compiler import parse as compiler_parse - from compiler import visitor - +import _ast +util.restore__ast(_ast) +from mako import _ast_util def parse(code, mode='exec', **exception_kwargs): """Parse an expression into AST""" - try: - if _ast: - return _ast_util.parse(code, '', mode) - else: - if isinstance(code, compat.text_type): - code = code.encode('ascii', 'backslashreplace') - return compiler_parse(code, mode) + return _ast_util.parse(code, '', mode) except Exception: raise exceptions.SyntaxException( "(%s) %s (%r)" % ( @@ -61,512 +48,185 @@ def parse(code, mode='exec', **exception_kwargs): ), **exception_kwargs) -if _ast: - class FindIdentifiers(_ast_util.NodeVisitor): +class FindIdentifiers(_ast_util.NodeVisitor): - def __init__(self, listener, **exception_kwargs): - self.in_function = False - self.in_assign_targets = False - self.local_ident_stack = set() - self.listener = listener - self.exception_kwargs = exception_kwargs + def __init__(self, listener, **exception_kwargs): + self.in_function = False + self.in_assign_targets = False + self.local_ident_stack = set() + self.listener = listener + self.exception_kwargs = exception_kwargs - def _add_declared(self, name): - if not self.in_function: - self.listener.declared_identifiers.add(name) - else: - self.local_ident_stack.add(name) + def _add_declared(self, name): + if not self.in_function: + self.listener.declared_identifiers.add(name) + else: + self.local_ident_stack.add(name) - def visit_ClassDef(self, node): - self._add_declared(node.name) + def visit_ClassDef(self, node): + self._add_declared(node.name) - def visit_Assign(self, node): + def visit_Assign(self, node): - # flip around the visiting of Assign so the expression gets - # evaluated first, in the case of a clause like "x=x+5" (x - # is undeclared) + # flip around the visiting of Assign so the expression gets + # evaluated first, in the case of a clause like "x=x+5" (x + # is undeclared) - self.visit(node.value) - in_a = self.in_assign_targets - self.in_assign_targets = True - for n in node.targets: - self.visit(n) - self.in_assign_targets = in_a + self.visit(node.value) + in_a = self.in_assign_targets + self.in_assign_targets = True + for n in node.targets: + self.visit(n) + self.in_assign_targets = in_a - if compat.py3k: + if compat.py3k: - # ExceptHandler is in Python 2, but this block only works in - # Python 3 (and is required there) + # ExceptHandler is in Python 2, but this block only works in + # Python 3 (and is required there) - def visit_ExceptHandler(self, node): - if node.name is not None: - self._add_declared(node.name) - if node.type is not None: - self.listener.undeclared_identifiers.add(node.type.id) - for statement in node.body: - self.visit(statement) - - def visit_Lambda(self, node, *args): - self._visit_function(node, True) - - def visit_FunctionDef(self, node): - self._add_declared(node.name) - 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): - - # push function state onto stack. dont log any more - # identifiers as "declared" until outside of the function, - # but keep logging identifiers as "undeclared". track - # argument names in each function header so they arent - # counted as "undeclared" - - inf = self.in_function - self.in_function = True - - local_ident_stack = self.local_ident_stack - self.local_ident_stack = local_ident_stack.union([ - arg_id(arg) for arg in self._expand_tuples(node.args.args) - ]) - if islambda: - self.visit(node.body) - else: - for n in node.body: - self.visit(n) - self.in_function = inf - self.local_ident_stack = local_ident_stack - - def visit_For(self, node): - - # flip around visit - - self.visit(node.iter) - self.visit(node.target) + def visit_ExceptHandler(self, node): + if node.name is not None: + self._add_declared(node.name) + if node.type is not None: + self.visit(node.type) for statement in node.body: self.visit(statement) - for statement in node.orelse: - self.visit(statement) - def visit_Name(self, node): - if isinstance(node.ctx, _ast.Store): - # this is eqiuvalent to visit_AssName in - # compiler - self._add_declared(node.id) - elif node.id not in reserved and node.id \ - not in self.listener.declared_identifiers and node.id \ - not in self.local_ident_stack: - self.listener.undeclared_identifiers.add(node.id) + def visit_Lambda(self, node, *args): + self._visit_function(node, True) - def visit_Import(self, node): - for name in node.names: - if name.asname is not None: - self._add_declared(name.asname) - else: - self._add_declared(name.name.split('.')[0]) + def visit_FunctionDef(self, node): + self._add_declared(node.name) + self._visit_function(node, False) - def visit_ImportFrom(self, node): - for name in node.names: - if name.asname is not None: - self._add_declared(name.asname) - else: - if name.name == '*': - raise exceptions.CompileException( - "'import *' is not supported, since all identifier " - "names must be explicitly declared. Please use the " - "form 'from import , , " - "...' instead.", **self.exception_kwargs) - self._add_declared(name.name) - - - class FindTuple(_ast_util.NodeVisitor): - - def __init__(self, listener, code_factory, **exception_kwargs): - self.listener = listener - self.exception_kwargs = exception_kwargs - self.code_factory = code_factory - - def visit_Tuple(self, node): - for n in node.elts: - p = self.code_factory(n, **self.exception_kwargs) - self.listener.codeargs.append(p) - self.listener.args.append(ExpressionGenerator(n).value()) - self.listener.declared_identifiers = \ - self.listener.declared_identifiers.union( - p.declared_identifiers) - self.listener.undeclared_identifiers = \ - self.listener.undeclared_identifiers.union( - p.undeclared_identifiers) - - - class ParseFunc(_ast_util.NodeVisitor): - - def __init__(self, listener, **exception_kwargs): - self.listener = listener - self.exception_kwargs = exception_kwargs - - def visit_FunctionDef(self, node): - self.listener.funcname = node.name - argnames = [arg_id(arg) for arg in node.args.args] - if node.args.vararg: - argnames.append(arg_stringname(node.args.vararg)) - if node.args.kwarg: - argnames.append(arg_stringname(node.args.kwarg)) - self.listener.argnames = argnames - self.listener.defaults = node.args.defaults # ast - self.listener.varargs = node.args.vararg - self.listener.kwargs = node.args.kwarg - - class ExpressionGenerator(object): - - def __init__(self, astnode): - self.generator = _ast_util.SourceGenerator(' ' * 4) - self.generator.visit(astnode) - - def value(self): - return ''.join(self.generator.result) -else: - class FindIdentifiers(object): - - def __init__(self, listener, **exception_kwargs): - self.in_function = False - self.local_ident_stack = set() - self.listener = listener - self.exception_kwargs = exception_kwargs - - def _add_declared(self, name): - if not self.in_function: - self.listener.declared_identifiers.add(name) + def _expand_tuples(self, args): + for arg in args: + if isinstance(arg, _ast.Tuple): + for n in arg.elts: + yield n else: - self.local_ident_stack.add(name) + yield arg - def visitClass(self, node, *args): - self._add_declared(node.name) + def _visit_function(self, node, islambda): - def visitAssName(self, node, *args): - self._add_declared(node.name) + # push function state onto stack. dont log any more + # identifiers as "declared" until outside of the function, + # but keep logging identifiers as "undeclared". track + # argument names in each function header so they arent + # counted as "undeclared" - def visitAssign(self, node, *args): + inf = self.in_function + self.in_function = True - # flip around the visiting of Assign so the expression gets - # evaluated first, in the case of a clause like "x=x+5" (x - # is undeclared) - self.visit(node.expr, *args) - for n in node.nodes: - self.visit(n, *args) + local_ident_stack = self.local_ident_stack + self.local_ident_stack = local_ident_stack.union([ + arg_id(arg) for arg in self._expand_tuples(node.args.args) + ]) + if islambda: + self.visit(node.body) + else: + for n in node.body: + self.visit(n) + self.in_function = inf + self.local_ident_stack = local_ident_stack - def visitLambda(self, node, *args): - self._visit_function(node, args) + def visit_For(self, node): - def visitFunction(self, node, *args): - self._add_declared(node.name) - self._visit_function(node, args) + # flip around visit - def _expand_tuples(self, args): - for arg in args: - if isinstance(arg, tuple): - for n in arg: - yield n - else: - yield arg + self.visit(node.iter) + self.visit(node.target) + for statement in node.body: + self.visit(statement) + for statement in node.orelse: + self.visit(statement) - def _visit_function(self, node, args): - - # push function state onto stack. dont log any more - # identifiers as "declared" until outside of the function, - # but keep logging identifiers as "undeclared". track - # argument names in each function header so they arent - # counted as "undeclared" - - inf = self.in_function - self.in_function = True - - local_ident_stack = self.local_ident_stack - self.local_ident_stack = local_ident_stack.union([ - arg for arg in self._expand_tuples(node.argnames) - ]) - - for n in node.getChildNodes(): - self.visit(n, *args) - self.in_function = inf - self.local_ident_stack = local_ident_stack - - def visitFor(self, node, *args): - - # flip around visit - - self.visit(node.list, *args) - self.visit(node.assign, *args) - self.visit(node.body, *args) - - def visitName(self, node, *args): - if node.name not in reserved and node.name \ - not in self.listener.declared_identifiers and node.name \ + def visit_Name(self, node): + if isinstance(node.ctx, _ast.Store): + # this is eqiuvalent to visit_AssName in + # compiler + self._add_declared(node.id) + elif node.id not in reserved and node.id \ + not in self.listener.declared_identifiers and node.id \ not in self.local_ident_stack: - self.listener.undeclared_identifiers.add(node.name) + self.listener.undeclared_identifiers.add(node.id) - def visitImport(self, node, *args): - for mod, alias in node.names: - if alias is not None: - self._add_declared(alias) - else: - self._add_declared(mod.split('.')[0]) + def visit_Import(self, node): + for name in node.names: + if name.asname is not None: + self._add_declared(name.asname) + else: + self._add_declared(name.name.split('.')[0]) - def visitFrom(self, node, *args): - for mod, alias in node.names: - if alias is not None: - self._add_declared(alias) - else: - if mod == '*': - raise exceptions.CompileException( + def visit_ImportFrom(self, node): + for name in node.names: + if name.asname is not None: + self._add_declared(name.asname) + else: + if name.name == '*': + raise exceptions.CompileException( "'import *' is not supported, since all identifier " "names must be explicitly declared. Please use the " "form 'from import , , " "...' instead.", **self.exception_kwargs) - self._add_declared(mod) - - def visit(self, expr): - visitor.walk(expr, self) # , walker=walker()) + self._add_declared(name.name) - class FindTuple(object): +class FindTuple(_ast_util.NodeVisitor): - def __init__(self, listener, code_factory, **exception_kwargs): - self.listener = listener - self.exception_kwargs = exception_kwargs - self.code_factory = code_factory + def __init__(self, listener, code_factory, **exception_kwargs): + self.listener = listener + self.exception_kwargs = exception_kwargs + self.code_factory = code_factory - def visitTuple(self, node, *args): - for n in node.nodes: - p = self.code_factory(n, **self.exception_kwargs) - self.listener.codeargs.append(p) - self.listener.args.append(ExpressionGenerator(n).value()) - self.listener.declared_identifiers = \ - self.listener.declared_identifiers.union( - p.declared_identifiers) - self.listener.undeclared_identifiers = \ - self.listener.undeclared_identifiers.union( - p.undeclared_identifiers) - - def visit(self, expr): - visitor.walk(expr, self) # , walker=walker()) + def visit_Tuple(self, node): + for n in node.elts: + p = self.code_factory(n, **self.exception_kwargs) + self.listener.codeargs.append(p) + self.listener.args.append(ExpressionGenerator(n).value()) + self.listener.declared_identifiers = \ + self.listener.declared_identifiers.union( + p.declared_identifiers) + self.listener.undeclared_identifiers = \ + self.listener.undeclared_identifiers.union( + p.undeclared_identifiers) - class ParseFunc(object): +class ParseFunc(_ast_util.NodeVisitor): - def __init__(self, listener, **exception_kwargs): - self.listener = listener - self.exception_kwargs = exception_kwargs + def __init__(self, listener, **exception_kwargs): + self.listener = listener + self.exception_kwargs = exception_kwargs - def visitFunction(self, node, *args): - self.listener.funcname = node.name - self.listener.argnames = node.argnames - self.listener.defaults = node.defaults - self.listener.varargs = node.varargs - self.listener.kwargs = node.kwargs + def visit_FunctionDef(self, node): + self.listener.funcname = node.name - def visit(self, expr): - visitor.walk(expr, self) + argnames = [arg_id(arg) for arg in node.args.args] + if node.args.vararg: + argnames.append(arg_stringname(node.args.vararg)) + if compat.py2k: + # kw-only args don't exist in Python 2 + kwargnames = [] + else: + kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs] + if node.args.kwarg: + kwargnames.append(arg_stringname(node.args.kwarg)) + self.listener.argnames = argnames + self.listener.defaults = node.args.defaults # ast + self.listener.kwargnames = kwargnames + if compat.py2k: + self.listener.kwdefaults = [] + else: + self.listener.kwdefaults = node.args.kw_defaults + self.listener.varargs = node.args.vararg + self.listener.kwargs = node.args.kwarg - class ExpressionGenerator(object): +class ExpressionGenerator(object): - """given an AST node, generates an equivalent literal Python - expression.""" + def __init__(self, astnode): + self.generator = _ast_util.SourceGenerator(' ' * 4) + self.generator.visit(astnode) - def __init__(self, astnode): - self.buf = StringIO() - visitor.walk(astnode, self) # , walker=walker()) - - def value(self): - return self.buf.getvalue() - - def operator(self, op, node, *args): - self.buf.write('(') - self.visit(node.left, *args) - self.buf.write(' %s ' % op) - self.visit(node.right, *args) - self.buf.write(')') - - def booleanop(self, op, node, *args): - self.visit(node.nodes[0]) - for n in node.nodes[1:]: - self.buf.write(' ' + op + ' ') - self.visit(n, *args) - - def visitConst(self, node, *args): - self.buf.write(repr(node.value)) - - def visitAssName(self, node, *args): - - # TODO: figure out OP_ASSIGN, other OP_s - - self.buf.write(node.name) - - def visitName(self, node, *args): - self.buf.write(node.name) - - def visitMul(self, node, *args): - self.operator('*', node, *args) - - def visitAnd(self, node, *args): - self.booleanop('and', node, *args) - - def visitOr(self, node, *args): - self.booleanop('or', node, *args) - - def visitBitand(self, node, *args): - self.booleanop('&', node, *args) - - def visitBitor(self, node, *args): - self.booleanop('|', node, *args) - - def visitBitxor(self, node, *args): - self.booleanop('^', node, *args) - - def visitAdd(self, node, *args): - self.operator('+', node, *args) - - def visitGetattr(self, node, *args): - self.visit(node.expr, *args) - self.buf.write('.%s' % node.attrname) - - def visitSub(self, node, *args): - self.operator('-', node, *args) - - def visitNot(self, node, *args): - self.buf.write('not ') - self.visit(node.expr) - - def visitDiv(self, node, *args): - self.operator('/', node, *args) - - def visitFloorDiv(self, node, *args): - self.operator('//', node, *args) - - def visitSubscript(self, node, *args): - self.visit(node.expr) - self.buf.write('[') - [self.visit(x) for x in node.subs] - self.buf.write(']') - - def visitUnarySub(self, node, *args): - self.buf.write('-') - self.visit(node.expr) - - def visitUnaryAdd(self, node, *args): - self.buf.write('-') - self.visit(node.expr) - - def visitSlice(self, node, *args): - self.visit(node.expr) - self.buf.write('[') - if node.lower is not None: - self.visit(node.lower) - self.buf.write(':') - if node.upper is not None: - self.visit(node.upper) - self.buf.write(']') - - def visitDict(self, node): - self.buf.write('{') - c = node.getChildren() - for i in range(0, len(c), 2): - self.visit(c[i]) - self.buf.write(': ') - self.visit(c[i + 1]) - if i < len(c) - 2: - self.buf.write(', ') - self.buf.write('}') - - def visitTuple(self, node): - self.buf.write('(') - c = node.getChildren() - for i in range(0, len(c)): - self.visit(c[i]) - if i < len(c) - 1: - self.buf.write(', ') - self.buf.write(')') - - def visitList(self, node): - self.buf.write('[') - c = node.getChildren() - for i in range(0, len(c)): - self.visit(c[i]) - if i < len(c) - 1: - self.buf.write(', ') - self.buf.write(']') - - def visitListComp(self, node): - self.buf.write('[') - self.visit(node.expr) - self.buf.write(' ') - for n in node.quals: - self.visit(n) - self.buf.write(']') - - def visitListCompFor(self, node): - self.buf.write(' for ') - self.visit(node.assign) - self.buf.write(' in ') - self.visit(node.list) - for n in node.ifs: - self.visit(n) - - def visitListCompIf(self, node): - self.buf.write(' if ') - self.visit(node.test) - - def visitCompare(self, node): - self.visit(node.expr) - for tup in node.ops: - self.buf.write(tup[0]) - self.visit(tup[1]) - - def visitCallFunc(self, node, *args): - self.visit(node.node) - self.buf.write('(') - if len(node.args): - self.visit(node.args[0]) - for a in node.args[1:]: - self.buf.write(', ') - self.visit(a) - 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): - - def dispatch(self, node, *args): - print('Node:', str(node)) - - # print "dir:", dir(node) - - return visitor.ASTVisitor.dispatch(self, node, *args) + def value(self): + return ''.join(self.generator.result) diff --git a/lib/mako/runtime.py b/lib/mako/runtime.py index f94c109d..6b6a35a9 100644 --- a/lib/mako/runtime.py +++ b/lib/mako/runtime.py @@ -1,5 +1,5 @@ # mako/runtime.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -9,9 +9,7 @@ Namespace, and various helper functions.""" from mako import exceptions, util, compat from mako.compat import compat_builtins -import inspect import sys -import collections class Context(object): @@ -132,9 +130,7 @@ class Context(object): def get(self, key, default=None): """Return a value from this :class:`.Context`.""" - return self._data.get(key, - compat_builtins.__dict__.get(key, default) - ) + return self._data.get(key, compat_builtins.__dict__.get(key, default)) def write(self, string): """Write a string to this :class:`.Context` object's @@ -474,8 +470,8 @@ class Namespace(object): def get_template(self, uri): """Return a :class:`.Template` from the given ``uri``. - The ``uri`` resolution is relative to the ``uri`` of this :class:`.Namespace` - object's :class:`.Template`. + The ``uri`` resolution is relative to the ``uri`` of this + :class:`.Namespace` object's :class:`.Template`. """ return _lookup_template(self.context, uri, self._templateuri) @@ -673,7 +669,7 @@ def supports_caller(func): """ - def wrap_stackframe(context, *args, **kwargs): + def wrap_stackframe(context, *args, **kwargs): context.caller_stack._push_frame() try: return func(context, *args, **kwargs) @@ -691,8 +687,8 @@ def capture(context, callable_, *args, **kwargs): if not compat.callable(callable_): raise exceptions.RuntimeException( - "capture() function expects a callable as " - "its argument (i.e. capture(func, *args, **kwargs))" + "capture() function expects a callable as " + "its argument (i.e. capture(func, *args, **kwargs))" ) context._push_buffer() try: @@ -853,7 +849,6 @@ def _exec_template(callable_, context, args=None, kwargs=None): template = context._with_template if template is not None and \ (template.format_exceptions or template.error_handler): - error = None try: callable_(context, *args, **kwargs) except Exception: @@ -868,11 +863,12 @@ def _render_error(template, context, error): if template.error_handler: result = template.error_handler(context, error) if not result: - raise error + compat.reraise(*sys.exc_info()) else: error_template = exceptions.html_error_template() if context._outputting_as_unicode: - context._buffer_stack[:] = [util.FastEncodingBuffer(as_unicode=True)] + context._buffer_stack[:] = [ + util.FastEncodingBuffer(as_unicode=True)] else: context._buffer_stack[:] = [util.FastEncodingBuffer( error_template.output_encoding, diff --git a/lib/mako/template.py b/lib/mako/template.py index 3a7b7f0b..fb610628 100644 --- a/lib/mako/template.py +++ b/lib/mako/template.py @@ -1,5 +1,5 @@ # mako/template.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -596,6 +596,25 @@ class ModuleInfo(object): if module_filename: self._modules[module_filename] = self + @classmethod + def get_module_source_metadata(cls, module_source, full_line_map=False): + source_map = re.search( + r"__M_BEGIN_METADATA(.+?)__M_END_METADATA", + module_source, re.S).group(1) + source_map = compat.json.loads(source_map) + source_map['line_map'] = dict((int(k), int(v)) + for k, v in source_map['line_map'].items()) + if full_line_map: + f_line_map = source_map['full_line_map'] = [] + line_map = source_map['line_map'] + + curr_templ_line = 1 + for mod_line in range(1, max(line_map)): + if mod_line in line_map: + curr_templ_line = line_map[mod_line] + f_line_map.append(curr_templ_line) + return source_map + @property def code(self): if self.module_source is not None: diff --git a/lib/mako/util.py b/lib/mako/util.py index 0a9e14e3..cba2ab79 100644 --- a/lib/mako/util.py +++ b/lib/mako/util.py @@ -1,5 +1,5 @@ # mako/util.py -# Copyright (C) 2006-2013 the Mako authors and contributors +# Copyright (C) 2006-2015 the Mako authors and contributors # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -11,15 +11,10 @@ import os from mako import compat import operator -def function_named(fn, name): - """Return a function with a given __name__. - - Will assign to __name__ and return the original function if possible on - the Python implementation, otherwise a new function will be constructed. - - """ - fn.__name__ = name - return fn +def update_wrapper(decorated, fn): + decorated.__wrapped__ = fn + decorated.__name__ = fn.__name__ + return decorated class PluginLoader(object): diff --git a/lib/requests/README.rst b/lib/requests/README.rst new file mode 100644 index 00000000..e9f63ef8 --- /dev/null +++ b/lib/requests/README.rst @@ -0,0 +1,85 @@ +Requests: HTTP for Humans +========================= + +.. image:: https://badge.fury.io/py/requests.png + :target: http://badge.fury.io/py/requests + +.. image:: https://pypip.in/d/requests/badge.png + :target: https://crate.io/packages/requests/ + + +Requests is an Apache2 Licensed HTTP library, written in Python, for human +beings. + +Most existing Python modules for sending HTTP requests are extremely +verbose and cumbersome. Python's builtin urllib2 module provides most of +the HTTP capabilities you should need, but the api is thoroughly broken. +It requires an enormous amount of work (even method overrides) to +perform the simplest of tasks. + +Things shouldn't be this way. Not in Python. + +.. code-block:: python + + >>> r = requests.get('https://api.github.com', auth=('user', 'pass')) + >>> r.status_code + 204 + >>> r.headers['content-type'] + 'application/json' + >>> r.text + ... + +See `the same code, without Requests `_. + +Requests allow you to send HTTP/1.1 requests. You can add headers, form data, +multipart files, and parameters with simple Python dictionaries, and access the +response data in the same way. It's powered by httplib and `urllib3 +`_, but it does all the hard work and crazy +hacks for you. + + +Features +-------- + +- International Domains and URLs +- Keep-Alive & Connection Pooling +- Sessions with Cookie Persistence +- Browser-style SSL Verification +- Basic/Digest Authentication +- Elegant Key/Value Cookies +- Automatic Decompression +- Unicode Response Bodies +- Multipart File Uploads +- Connection Timeouts +- Thread-safety +- HTTP(S) proxy support + + +Installation +------------ + +To install Requests, simply: + +.. code-block:: bash + + $ pip install requests + + +Documentation +------------- + +Documentation is available at http://docs.python-requests.org/. + + +Contribute +---------- + +#. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug. There is a `Contributor Friendly`_ tag for issues that should be ideal for people who are not very familiar with the codebase yet. +#. If you feel uncomfortable or uncertain about an issue or your changes, feel free to email @sigmavirus24 and he will happily help you via email, Skype, remote pairing or whatever you are comfortable with. +#. Fork `the repository`_ on GitHub to start making your changes to the **master** branch (or branch off of it). +#. Write a test which shows that the bug was fixed or that the feature works as expected. +#. Send a pull request and bug the maintainer until it gets merged and published. :) Make sure to add yourself to AUTHORS_. + +.. _`the repository`: http://github.com/kennethreitz/requests +.. _AUTHORS: https://github.com/kennethreitz/requests/blob/master/AUTHORS.rst +.. _Contributor Friendly: https://github.com/kennethreitz/requests/issues?direction=desc&labels=Contributor+Friendly&page=1&sort=updated&state=open