From 12856b1297451b26468657218cc3e176b732381f Mon Sep 17 00:00:00 2001 From: Mike Bayer <mike_mp@zzzcomputing.com> Date: Mon, 28 Apr 2014 17:33:15 -0400 Subject: [PATCH] - will now be 1.0 - drop python 2.4, 2.5 support - various pep8ing --- doc/build/changelog.rst | 23 +- mako/__init__.py | 4 +- mako/_ast_util.py | 58 ++-- mako/ast.py | 5 +- mako/codegen.py | 122 ++++---- mako/compat.py | 20 +- mako/exceptions.py | 13 +- mako/lexer.py | 62 ++-- mako/pygen.py | 6 +- mako/pyparser.py | 653 +++++++++------------------------------- mako/runtime.py | 20 +- setup.py | 5 +- test/__init__.py | 5 +- test/test_exceptions.py | 5 +- 14 files changed, 323 insertions(+), 678 deletions(-) diff --git a/doc/build/changelog.rst b/doc/build/changelog.rst index 279a0d5..f43b26d 100644 --- a/doc/build/changelog.rst +++ b/doc/build/changelog.rst @@ -2,13 +2,20 @@ Changelog ========= -0.9 +1.0 === .. changelog:: - :version: 0.9.2 + :version: 1.0.0 :released: + .. change:: + :tags: general + + Compatibility changes; in order to modernize the codebase, Mako + is now dropping support for Python 2.4 and Python 2.5 altogether. + The source base is now targeted at Python 2.6 and forwards. + .. change:: :tags: bug, py3k @@ -23,6 +30,15 @@ Changelog when a "try/except" targeted a tuple of exception types, rather than a single exception. + .. change:: + :tags: feature + :pullreq: bitbucket:5 + + mako-render is now implemented as a setuptools entrypoint script; + a standalone mako.cmd.cmdline() callable is now available, and the + system also uses argparse now instead of optparse. Pull request + courtesy Derek Harland. + .. change:: :tags: feature :pullreq: bitbucket:4 @@ -51,6 +67,9 @@ Changelog template lookup directories. Standard input for templates also works now too. Pull request courtesy Derek Harland. +0.9 +=== + .. changelog:: :version: 0.9.1 :released: Thu Dec 26 2013 diff --git a/mako/__init__.py b/mako/__init__.py index aa99505..52a1c05 100644 --- a/mako/__init__.py +++ b/mako/__init__.py @@ -5,5 +5,7 @@ # the MIT License: http://www.opensource.org/licenses/mit-license.php -__version__ = '0.9.2' +__version__ = '1.0.0' + + diff --git a/mako/_ast_util.py b/mako/_ast_util.py index 788b86a..5c274be 100644 --- a/mako/_ast_util.py +++ b/mako/_ast_util.py @@ -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/mako/ast.py b/mako/ast.py index 8f711b4..44355bc 100644 --- a/mako/ast.py +++ b/mako/ast.py @@ -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,8 +106,8 @@ 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" % diff --git a/mako/codegen.py b/mako/codegen.py index 045d03c..2240ba2 100644 --- a/mako/codegen.py +++ b/mako/codegen.py @@ -187,7 +187,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) @@ -255,7 +255,7 @@ class _GenerateRenderMethod(object): decorator = node.decorator if decorator: self.printer.writeline( - "@runtime._decorate_toplevel(%s)" % decorator) + "@runtime._decorate_toplevel(%s)" % decorator) self.printer.writelines( "def %s(%s):" % (name, ','.join(args)), @@ -267,7 +267,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') @@ -309,9 +309,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,7 +323,7 @@ class _GenerateRenderMethod(object): "_mako_generate_namespaces(context)", "return context.namespaces[(__name__, name)]", None, None - ) + ) self.printer.writeline("def _mako_generate_namespaces(context):") @@ -369,9 +369,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 +381,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,7 +401,7 @@ class _GenerateRenderMethod(object): self.printer.writeline("context['self'].%s = ns" % (node.name)) self.printer.writeline( - "context.namespaces[(__name__, %s)] = ns" % repr(node.name)) + "context.namespaces[(__name__, %s)] = ns" % repr(node.name)) self.printer.write("\n") if not len(namespaces): self.printer.writeline("pass") @@ -466,7 +467,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, @@ -553,7 +554,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 +565,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 +575,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 +628,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 +685,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 +697,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 +710,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,11 +747,10 @@ 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) @@ -822,7 +823,7 @@ class _GenerateRenderMethod(object): "__M_buf.getvalue()", False), None - ) + ) def visitCode(self, node): if not node.ismodule: @@ -835,20 +836,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) 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)" % @@ -941,7 +942,7 @@ 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.writelines( @@ -966,9 +967,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 +1013,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 +1048,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 +1068,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 +1096,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 +1109,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 +1123,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 +1139,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 +1156,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 +1166,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 +1191,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/mako/compat.py b/mako/compat.py index 82b6c05..f782aa9 100644 --- a/mako/compat.py +++ b/mako/compat.py @@ -5,7 +5,6 @@ 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') @@ -100,23 +99,10 @@ 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 +all = all - 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__ +def exception_name(exc): + return exc.__class__.__name__ try: from inspect import CO_VARKEYWORDS, CO_VARARGS diff --git a/mako/exceptions.py b/mako/exceptions.py index b8f97ee..a7bab8c 100644 --- a/mako/exceptions.py +++ b/mako/exceptions.py @@ -28,7 +28,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 +37,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 @@ -260,10 +260,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/mako/lexer.py b/mako/lexer.py index dca88e1..dd46b10 100644 --- a/mako/lexer.py +++ b/mako/lexer.py @@ -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<self.textlength and self.text[cp] != '\n'): - cp -=1 + while (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: </%%%s>" % - match.group(1), - **self.exception_kwargs) + "Closing tag without opening tag: </%%%s>" % + match.group(1), + **self.exception_kwargs) elif self.tag[-1].keyword != match.group(1): raise exceptions.SyntaxException( - "Closing tag </%%%s> does not match tag: <%%%s>" % - (match.group(1), self.tag[-1].keyword), - **self.exception_kwargs) + "Closing tag </%%%s> 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/mako/pygen.py b/mako/pygen.py index cba9464..52e32be 100644 --- a/mako/pygen.py +++ b/mako/pygen.py @@ -80,7 +80,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. @@ -108,7 +108,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 +265,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/mako/pyparser.py b/mako/pyparser.py index db5c295..e1edc5b 100644 --- a/mako/pyparser.py +++ b/mako/pyparser.py @@ -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, '<unknown>', mode) - else: - if isinstance(code, compat.text_type): - code = code.encode('ascii', 'backslashreplace') - return compiler_parse(code, mode) + return _ast_util.parse(code, '<unknown>', mode) except Exception: raise exceptions.SyntaxException( "(%s) %s (%r)" % ( @@ -61,529 +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 - - if compat.py3k: - - # 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.visit(node.type) - 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 + 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 - def visit_For(self, node): + if compat.py3k: - # flip around visit + # ExceptHandler is in Python 2, but this block only works in + # Python 3 (and is required there) - 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_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_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 <modulename> import <name1>, <name2>, " - "...' 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 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 + def visit_Lambda(self, node, *args): + self._visit_function(node, True) - 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 visit_FunctionDef(self, node): + self._add_declared(node.name) + self._visit_function(node, False) - 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) - - def visitClass(self, node, *args): - self._add_declared(node.name) - - def visitAssName(self, node, *args): - self._add_declared(node.name) - - def visitAssign(self, node, *args): - - # 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) - - def visitLambda(self, node, *args): - self._visit_function(node, args) - - def visitFunction(self, node, *args): - self._add_declared(node.name) - self._visit_function(node, args) + yield arg - 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, islambda): - 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" - # 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 - 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 + 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) + 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) - self.visit(node.list, *args) - self.visit(node.assign, *args) - self.visit(node.body, *args) + 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 visitName(self, node, *args): - if node.name not in reserved and node.name \ - not in self.listener.declared_identifiers and node.name \ - not in self.local_ident_stack: - self.listener.undeclared_identifiers.add(node.name) - - 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 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 <modulename> import <name1>, <name2>, " "...' instead.", **self.exception_kwargs) - self._add_declared(mod) + self._add_declared(name.name) - def visit(self, expr): - visitor.walk(expr, self) # , walker=walker()) +class FindTuple(_ast_util.NodeVisitor): - class FindTuple(object): + 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 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) - 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()) +class ParseFunc(_ast_util.NodeVisitor): + def __init__(self, listener, **exception_kwargs): + self.listener = listener + self.exception_kwargs = exception_kwargs - class ParseFunc(object): + def visit_FunctionDef(self, node): + self.listener.funcname = node.name - def __init__(self, listener, **exception_kwargs): - self.listener = listener - self.exception_kwargs = exception_kwargs + argnames = [arg_id(arg) for arg in node.args.args] + if node.args.vararg: + argnames.append(arg_stringname(node.args.vararg)) - def visitFunction(self, node, *args): - self.listener.funcname = node.name - self.listener.argnames = list(node.argnames) - if node.kwargs: - self.listener.kwargnames = [self.listener.argnames.pop()] - else: - self.listener.kwargnames = [] - self.listener.defaults = node.defaults + 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 = [] - self.listener.varargs = node.varargs - self.listener.kwargs = node.kwargs - - def visit(self, expr): - visitor.walk(expr, self) - - - class ExpressionGenerator(object): - - """given an AST node, generates an equivalent literal Python - expression.""" - - 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): + else: + self.listener.kwdefaults = node.args.kw_defaults + self.listener.varargs = node.args.vararg + self.listener.kwargs = node.args.kwarg - def dispatch(self, node, *args): - print('Node:', str(node)) +class ExpressionGenerator(object): - # print "dir:", dir(node) + def __init__(self, astnode): + self.generator = _ast_util.SourceGenerator(' ' * 4) + self.generator.visit(astnode) - return visitor.ASTVisitor.dispatch(self, node, *args) + def value(self): + return ''.join(self.generator.result) diff --git a/mako/runtime.py b/mako/runtime.py index 5a3489b..d7b9681 100644 --- a/mako/runtime.py +++ b/mako/runtime.py @@ -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: @@ -872,7 +867,8 @@ def _render_error(template, context, error): 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/setup.py b/setup.py index 91b5769..bbab08e 100644 --- a/setup.py +++ b/setup.py @@ -9,6 +9,9 @@ v.close() readme = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() +if sys.version_info < (2, 6): + raise Exception("Mako requires Python 2.6 or higher.") + markupsafe_installs = ( sys.version_info >= (2, 6) and sys.version_info < (3, 0) ) or sys.version_info >= (3, 3) @@ -48,7 +51,7 @@ setup(name='Mako', test_suite="nose.collector", zip_safe=False, install_requires=install_requires, - extras_require={'beaker': ['Beaker>=1.1']}, + extras_require={}, entry_points=""" [python.templating.engines] mako = mako.ext.turbogears:TGPlugin diff --git a/test/__init__.py b/test/__init__.py index 91ff54e..cf5f36a 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,7 +1,7 @@ from mako.template import Template import unittest import os -from mako.compat import py3k, py26, py25 +from mako.compat import py3k, py26 from mako import compat from mako.util import update_wrapper import re @@ -111,9 +111,6 @@ def requires_python_2(fn): def requires_python_26_or_greater(fn): return skip_if(lambda: not py26, "Requires Python 2.6 or greater")(fn) -def requires_python_25_or_greater(fn): - return skip_if(lambda: not py25, "Requires Python 2.5 or greater")(fn) - def requires_pygments_14(fn): try: import pygments diff --git a/test/test_exceptions.py b/test/test_exceptions.py index 5160f8e..3330a1f 100644 --- a/test/test_exceptions.py +++ b/test/test_exceptions.py @@ -7,8 +7,7 @@ from mako.lookup import TemplateLookup from mako.compat import u from test.util import result_lines from test import TemplateTest -from test import requires_pygments_14, requires_no_pygments_exceptions, \ - requires_python_25_or_greater +from test import requires_pygments_14, requires_no_pygments_exceptions class ExceptionsTest(TemplateTest): def test_html_error_template(self): @@ -156,7 +155,6 @@ ${u'привет'} html_error = exceptions.html_error_template().render() assert "RuntimeError: test" in str(html_error) - @requires_python_25_or_greater def test_py_utf8_html_error_template(self): try: foo = u('日本') @@ -264,7 +262,6 @@ ${foobar} result_lines(l.get_template("foo.html").render().decode('utf-8')) - @requires_python_25_or_greater def test_custom_tback(self): try: raise RuntimeError("error 1") -- GitLab