diff options
author | Magnus Hagander | 2008-11-03 13:40:19 +0000 |
---|---|---|
committer | Magnus Hagander | 2008-11-03 13:40:19 +0000 |
commit | 66dec2b79bd6eaebc31c752ddd5cbc616f1b1600 (patch) | |
tree | 5bd7d1fec12403d84980997c0fa5954f22df0e02 | |
parent | 80a496e63cc6f72ca61a0d2cecc1ac254f62ed19 (diff) |
Switch to using the Django template engine, since we'll
be using Django for some of the other stuff later, and it
sucks to have more than one template engine...
git-svn-id: file:///Users/dpage/pgweb/svn-repo/trunk@2258 8f5c7a92-453e-0410-a47f-ad33c8a6b003
-rwxr-xr-x | planet/generator.py | 16 | ||||
-rw-r--r-- | planet/mako/__init__.py | 6 | ||||
-rw-r--r-- | planet/mako/_ast_util.py | 863 | ||||
-rw-r--r-- | planet/mako/ast.py | 125 | ||||
-rw-r--r-- | planet/mako/cache.py | 55 | ||||
-rw-r--r-- | planet/mako/codegen.py | 697 | ||||
-rw-r--r-- | planet/mako/exceptions.py | 252 | ||||
-rw-r--r-- | planet/mako/filters.py | 170 | ||||
-rw-r--r-- | planet/mako/lexer.py | 328 | ||||
-rw-r--r-- | planet/mako/lookup.py | 136 | ||||
-rw-r--r-- | planet/mako/parsetree.py | 293 | ||||
-rw-r--r-- | planet/mako/pygen.py | 267 | ||||
-rw-r--r-- | planet/mako/pyparser.py | 377 | ||||
-rw-r--r-- | planet/mako/runtime.py | 389 | ||||
-rw-r--r-- | planet/mako/template.py | 274 | ||||
-rw-r--r-- | planet/mako/util.py | 256 | ||||
-rw-r--r-- | planet/template/base.tmpl | 2 | ||||
-rw-r--r-- | planet/template/index.tmpl | 27 | ||||
-rw-r--r-- | planet/template/policy.tmpl | 4 |
19 files changed, 26 insertions, 4511 deletions
diff --git a/planet/generator.py b/planet/generator.py index c3229805..a895d2bb 100755 --- a/planet/generator.py +++ b/planet/generator.py @@ -16,8 +16,9 @@ import os.path import sys import tidy import urllib -from mako.template import Template -from mako.lookup import TemplateLookup +from django.template import Context +from django.template.loader import get_template +from django.conf import settings from HTMLParser import HTMLParser from planethtml import * @@ -36,6 +37,9 @@ class Generator: self.feeds = [] self.staticfiles = ['policy'] + settings.configure( + TEMPLATE_DIRS=('template',), + ) def Generate(self): rss = PyRSS2Gen.RSS2( @@ -71,10 +75,12 @@ class Generator: self.UpdateStaticFile(staticfile) def WriteFromTemplate(self, templatename, outputname): - lookup = TemplateLookup(directories=['template'], output_encoding='utf-8', input_encoding='utf-8') - tmpl = lookup.get_template(templatename) + tmpl = get_template(templatename) f = open(outputname, "w") - f.write(tmpl.render_unicode(feeds=self.feeds, posts=self.items).encode('utf-8')) + f.write(tmpl.render(Context({ + 'feeds': self.feeds, + 'posts': self.items, + })).encode('utf-8')) f.close() def UpdateStaticFile(self, filename): diff --git a/planet/mako/__init__.py b/planet/mako/__init__.py deleted file mode 100644 index adced7f4..00000000 --- a/planet/mako/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# __init__.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - diff --git a/planet/mako/_ast_util.py b/planet/mako/_ast_util.py deleted file mode 100644 index a9efa2fa..00000000 --- a/planet/mako/_ast_util.py +++ /dev/null @@ -1,863 +0,0 @@ -# -*- coding: utf-8 -*- -""" - ast - ~~~ - - The `ast` module helps Python applications to process trees of the Python - abstract syntax grammar. The abstract syntax itself might change with - each Python release; this module helps to find out programmatically what - the current grammar looks like and allows modifications of it. - - An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as - a flag to the `compile()` builtin function or by using the `parse()` - function from this module. The result will be a tree of objects whose - classes all inherit from `ast.AST`. - - A modified abstract syntax tree can be compiled into a Python code object - using the built-in `compile()` function. - - Additionally various helper functions are provided that make working with - the trees simpler. The main intention of the helper functions and this - module in general is to provide an easy to use interface for libraries - that work tightly with the python syntax (template engines for example). - - - :copyright: Copyright 2008 by Armin Ronacher. - :license: Python License. -""" -import sys -from _ast import * - -if sys.platform.startswith('java'): - import array - - ast_list = array.ArrayType - get_symbol_key = lambda op: op - - def get_class_name(t): - result = t.__class__.__name__ - if result in ("expr_contextType", - "boolopType", - "unaryopType", - "cmpopType", - "operatorType"): - result = str(t) - if result == "AugLoad": - result = "Load" - elif result == "AugStore": - result = "Store" - else: - result = result.split(".")[-1] - if result.endswith("Type"): - result = result[:-4] - return result - -else: - ast_list = list - get_symbol_key = type - get_class_name = lambda node: node.__class__.__name__ - - -BOOLOP_SYMBOLS = { - And: 'and', - Or: 'or' -} - -BINOP_SYMBOLS = { - 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' -} - -UNARYOP_SYMBOLS = { - Invert: '~', - Not: 'not', - UAdd: '+', - USub: '-' -} - -ALL_SYMBOLS = {} -ALL_SYMBOLS.update(BOOLOP_SYMBOLS) -ALL_SYMBOLS.update(BINOP_SYMBOLS) -ALL_SYMBOLS.update(CMPOP_SYMBOLS) -ALL_SYMBOLS.update(UNARYOP_SYMBOLS) - - -def parse(expr, filename='<unknown>', mode='exec'): - """Parse an expression into an AST node.""" - return compile(expr, filename, mode, PyCF_ONLY_AST) - - -def to_source(node, indent_with=' ' * 4): - """ - This function can convert a node tree back into python sourcecode. This - is useful for debugging purposes, especially if you're dealing with custom - asts not generated by python itself. - - It could be that the sourcecode is evaluable when the AST itself is not - compilable / evaluable. The reason for this is that the AST contains some - more data than regular sourcecode does, which is dropped during - conversion. - - Each level of indentation is replaced with `indent_with`. Per default this - parameter is equal to four spaces as suggested by PEP 8, but it might be - adjusted to match the application's styleguide. - """ - generator = SourceGenerator(indent_with) - generator.visit(node) - return ''.join(generator.result) - - -def dump(node): - """ - A very verbose representation of the node passed. This is useful for - debugging purposes. - """ - def _format(node): - if isinstance(node, AST): - return '%s(%s)' % (node.__class__.__name__, - ', '.join('%s=%s' % (a, _format(b)) - for a, b in iter_fields(node))) - elif isinstance(node, ast_list): - return '[%s]' % ', '.join(_format(x) for x in node) - return repr(node) - if not isinstance(node, AST): - raise TypeError('expected AST, got %r' % node.__class__.__name__) - return _format(node) - - -def copy_location(new_node, old_node): - """ - Copy the source location hint (`lineno` and `col_offset`) from the - old to the new node if possible and return the new one. - """ - for attr in 'lineno', 'col_offset': - if attr in old_node._attributes and attr in new_node._attributes \ - and hasattr(old_node, attr): - setattr(new_node, attr, getattr(old_node, attr)) - return new_node - - -def fix_missing_locations(node): - """ - Some nodes require a line number and the column offset. Without that - information the compiler will abort the compilation. Because it can be - a dull task to add appropriate line numbers and column offsets when - adding new nodes this function can help. It copies the line number and - column offset of the parent node to the child nodes without this - information. - - Unlike `copy_location` this works recursive and won't touch nodes that - already have a location information. - """ - def _fix(node, lineno, col_offset): - if 'lineno' in node._attributes: - if not hasattr(node, 'lineno'): - node.lineno = lineno - else: - lineno = node.lineno - if 'col_offset' in node._attributes: - if not hasattr(node, 'col_offset'): - node.col_offset = col_offset - else: - col_offset = node.col_offset - for child in iter_child_nodes(node): - _fix(child, lineno, col_offset) - _fix(node, 1, 0) - return node - - -def increment_lineno(node, n=1): - """ - Increment the line numbers of all nodes by `n` if they have line number - attributes. This is useful to "move code" to a different location in a - file. - """ - for node in zip((node,), walk(node)): - if 'lineno' in node._attributes: - node.lineno = getattr(node, 'lineno', 0) + n - - -def iter_fields(node): - """Iterate over all fields of a node, only yielding existing fields.""" - if not hasattr(node, '_fields') or not node._fields: - return - for field in node._fields: - try: - yield field, getattr(node, field) - except AttributeError: - pass - - -def get_fields(node): - """Like `iter_fiels` but returns a dict.""" - return dict(iter_fields(node)) - - -def iter_child_nodes(node): - """Iterate over all child nodes or a node.""" - for name, field in iter_fields(node): - if isinstance(field, AST): - yield field - elif isinstance(field, ast_list): - for item in field: - if isinstance(item, AST): - yield item - - -def get_child_nodes(node): - """Like `iter_child_nodes` but returns a list.""" - return list(iter_child_nodes(node)) - - -def get_compile_mode(node): - """ - Get the mode for `compile` of a given node. If the node is not a `mod` - node (`Expression`, `Module` etc.) a `TypeError` is thrown. - """ - if not isinstance(node, mod): - raise TypeError('expected mod node, got %r' % node.__class__.__name__) - return { - Expression: 'eval', - Interactive: 'single' - }.get(node.__class__, 'expr') - - -def get_docstring(node): - """ - Return the docstring for the given node or `None` if no docstring can be - found. If the node provided does not accept docstrings a `TypeError` - will be raised. - """ - if not isinstance(node, (FunctionDef, ClassDef, Module)): - raise TypeError("%r can't have docstrings" % node.__class__.__name__) - if node.body and isinstance(node.body[0], Str): - return node.body[0].s - - -def walk(node): - """ - Iterate over all nodes. This is useful if you only want to modify nodes in - place and don't care about the context or the order the nodes are returned. - """ - from collections import deque - todo = deque([node]) - while todo: - node = todo.popleft() - todo.extend(iter_child_nodes(node)) - yield node - - -class NodeVisitor(object): - """ - Walks the abstract syntax tree and call visitor functions for every node - found. The visitor functions may return values which will be forwarded - by the `visit` method. - - Per default the visitor functions for the nodes are ``'visit_'`` + - class name of the node. So a `TryFinally` node visit function would - be `visit_TryFinally`. This behavior can be changed by overriding - the `get_visitor` function. If no visitor function exists for a node - (return value `None`) the `generic_visit` visitor is used instead. - - Don't use the `NodeVisitor` if you want to apply changes to nodes during - traversing. For this a special visitor exists (`NodeTransformer`) that - allows modifications. - """ - - def get_visitor(self, node): - """ - Return the visitor function for this node or `None` if no visitor - exists for this node. In that case the generic visit function is - used instead. - """ - method = 'visit_' + get_class_name(node) - return getattr(self, method, None) - - def visit(self, node): - """Visit a node.""" - f = self.get_visitor(node) - if f is not None: - return f(node) - return self.generic_visit(node) - - def generic_visit(self, node): - """Called if no explicit visitor function exists for a node.""" - for field, value in iter_fields(node): - if isinstance(value, ast_list): - for item in value: - if isinstance(item, AST): - self.visit(item) - elif isinstance(value, AST): - self.visit(value) - - -class NodeTransformer(NodeVisitor): - """ - Walks the abstract syntax tree and allows modifications of nodes. - - The `NodeTransformer` will walk the AST and use the return value of the - visitor functions to replace or remove the old node. If the return - value of the visitor function is `None` the node will be removed - from the previous location otherwise it's replaced with the return - value. The return value may be the original node in which case no - replacement takes place. - - Here an example transformer that rewrites all `foo` to `data['foo']`:: - - class RewriteName(NodeTransformer): - - def visit_Name(self, node): - return copy_location(Subscript( - value=Name(id='data', ctx=Load()), - slice=Index(value=Str(s=node.id)), - ctx=node.ctx - ), node) - - Keep in mind that if the node you're operating on has child nodes - you must either transform the child nodes yourself or call the generic - visit function for the node first. - - Nodes that were part of a collection of statements (that applies to - all statement nodes) may also return a list of nodes rather than just - a single node. - - Usually you use the transformer like this:: - - node = YourTransformer().visit(node) - """ - - def generic_visit(self, node): - for field, old_value in iter_fields(node): - old_value = getattr(node, field, None) - if isinstance(old_value, ast_list): - new_values = [] - for value in old_value: - if isinstance(value, AST): - value = self.visit(value) - if value is None: - continue - elif not isinstance(value, AST): - new_values.extend(value) - continue - new_values.append(value) - old_value[:] = new_values - elif isinstance(old_value, AST): - new_node = self.visit(old_value) - if new_node is None: - delattr(node, field) - else: - setattr(node, field, new_node) - return node - - -class SourceGenerator(NodeVisitor): - """ - This visitor is able to transform a well formed syntax tree into python - sourcecode. For more details have a look at the docstring of the - `node_to_source` function. - """ - - def __init__(self, indent_with): - self.result = [] - self.indent_with = indent_with - self.indentation = 0 - self.new_lines = 0 - - def write(self, x): - if self.new_lines: - if self.result: - self.result.append('\n' * self.new_lines) - self.result.append(self.indent_with * self.indentation) - self.new_lines = 0 - self.result.append(x) - - def newline(self, n=1): - self.new_lines = max(self.new_lines, n) - - def body(self, statements): - self.new_line = True - self.indentation += 1 - for stmt in statements: - self.visit(stmt) - self.indentation -= 1 - - def body_or_else(self, node): - self.body(node.body) - if node.orelse: - self.newline() - self.write('else:') - self.body(node.orelse) - - def signature(self, node): - want_comma = [] - def write_comma(): - if want_comma: - self.write(', ') - else: - want_comma.append(True) - - padding = [None] * (len(node.args) - len(node.defaults)) - for arg, default in zip(node.args, padding + node.defaults): - write_comma() - self.visit(arg) - if default is not None: - self.write('=') - self.visit(default) - if node.vararg is not None: - write_comma() - self.write('*' + node.vararg) - if node.kwarg is not None: - write_comma() - self.write('**' + node.kwarg) - - def decorators(self, node): - for decorator in node.decorator_list: - self.newline() - self.write('@') - self.visit(decorator) - - # Statements - - def visit_Assign(self, node): - self.newline() - for idx, target in enumerate(node.targets): - if idx: - self.write(', ') - self.visit(target) - self.write(' = ') - self.visit(node.value) - - def visit_AugAssign(self, node): - self.newline() - self.visit(node.target) - self.write(BINOP_SYMBOLS[get_symbol_key(node.op)] + '=') - self.visit(node.value) - - def visit_ImportFrom(self, node): - self.newline() - self.write('from %s%s import ' % ('.' * node.level, node.module)) - for idx, item in enumerate(node.names): - if idx: - self.write(', ') - self.write(item) - - def visit_Import(self, node): - self.newline() - for item in node.names: - self.write('import ') - self.visit(item) - - def visit_Expr(self, node): - self.newline() - self.generic_visit(node) - - def visit_FunctionDef(self, node): - self.newline(n=2) - self.decorators(node) - self.newline() - self.write('def %s(' % node.name) - self.signature(node.args) - self.write('):') - self.body(node.body) - - def visit_ClassDef(self, node): - have_args = [] - def paren_or_comma(): - if have_args: - self.write(', ') - else: - have_args.append(True) - self.write('(') - - self.newline(n=3) - self.decorators(node) - self.newline() - self.write('class %s' % node.name) - for base in node.bases: - paren_or_comma() - self.visit(base) - # XXX: the if here is used to keep this module compatible - # with python 2.6. - if hasattr(node, 'keywords'): - for keyword in node.keywords: - paren_or_comma() - self.write(keyword.arg + '=') - self.visit(keyword.value) - if node.starargs is not None: - paren_or_comma() - self.write('*') - self.visit(node.starargs) - if node.kwargs is not None: - paren_or_comma() - self.write('**') - self.visit(node.kwargs) - self.write(have_args and '):' or ':') - self.body(node.body) - - def visit_If(self, node): - self.newline() - self.write('if ') - self.visit(node.test) - self.write(':') - self.body(node.body) - while True: - else_ = node.orelse - if len(else_) == 1 and isinstance(else_[0], If): - node = else_[0] - self.newline() - self.write('elif ') - self.visit(node.test) - self.write(':') - self.body(node.body) - else: - self.newline() - self.write('else:') - self.body(else_) - break - - def visit_For(self, node): - self.newline() - self.write('for ') - self.visit(node.target) - self.write(' in ') - self.visit(node.iter) - self.write(':') - self.body_or_else(node) - - def visit_While(self, node): - self.newline() - self.write('while ') - self.visit(node.test) - self.write(':') - self.body_or_else(node) - - def visit_With(self, node): - self.newline() - self.write('with ') - self.visit(node.context_expr) - if node.optional_vars is not None: - self.write(' as ') - self.visit(node.optional_vars) - self.write(':') - self.body(node.body) - - def visit_Pass(self, node): - self.newline() - self.write('pass') - - def visit_Print(self, node): - # XXX: python 2.6 only - self.newline() - self.write('print ') - want_comma = False - if node.dest is not None: - self.write(' >> ') - self.visit(node.dest) - want_comma = True - for value in node.values: - if want_comma: - self.write(', ') - self.visit(value) - want_comma = True - if not node.nl: - self.write(',') - - def visit_Delete(self, node): - self.newline() - self.write('del ') - for idx, target in enumerate(node): - if idx: - self.write(', ') - self.visit(target) - - def visit_TryExcept(self, node): - self.newline() - self.write('try:') - self.body(node.body) - for handler in node.handlers: - self.visit(handler) - - def visit_TryFinally(self, node): - self.newline() - self.write('try:') - self.body(node.body) - self.newline() - self.write('finally:') - self.body(node.finalbody) - - def visit_Global(self, node): - self.newline() - self.write('global ' + ', '.join(node.names)) - - def visit_Nonlocal(self, node): - self.newline() - self.write('nonlocal ' + ', '.join(node.names)) - - def visit_Return(self, node): - self.newline() - self.write('return ') - self.visit(node.value) - - def visit_Break(self, node): - self.newline() - self.write('break') - - def visit_Continue(self, node): - self.newline() - self.write('continue') - - def visit_Raise(self, node): - # XXX: Python 2.6 / 3.0 compatibility - self.newline() - self.write('raise') - if hasattr(node, 'exc') and node.exc is not None: - self.write(' ') - self.visit(node.exc) - if node.cause is not None: - self.write(' from ') - self.visit(node.cause) - elif hasattr(node, 'type') and node.type is not None: - self.visit(node.type) - if node.inst is not None: - self.write(', ') - self.visit(node.inst) - if node.tback is not None: - self.write(', ') - self.visit(node.tback) - - # Expressions - - def visit_Attribute(self, node): - self.visit(node.value) - self.write('.' + node.attr) - - def visit_Call(self, node): - want_comma = [] - def write_comma(): - if want_comma: - self.write(', ') - else: - want_comma.append(True) - - self.visit(node.func) - self.write('(') - for arg in node.args: - write_comma() - self.visit(arg) - for keyword in node.keywords: - write_comma() - self.write(keyword.arg + '=') - self.visit(keyword.value) - if node.starargs is not None: - write_comma() - self.write('*') - self.visit(node.starargs) - if node.kwargs is not None: - write_comma() - self.write('**') - self.visit(node.kwargs) - self.write(')') - - def visit_Name(self, node): - self.write(node.id) - - def visit_Str(self, node): - self.write(repr(node.s)) - - def visit_Bytes(self, node): - self.write(repr(node.s)) - - def visit_Num(self, node): - self.write(repr(node.n)) - - def visit_Tuple(self, node): - self.write('(') - idx = -1 - for idx, item in enumerate(node.elts): - if idx: - self.write(', ') - self.visit(item) - self.write(idx and ')' or ',)') - - def sequence_visit(left, right): - def visit(self, node): - self.write(left) - for idx, item in enumerate(node.elts): - if idx: - self.write(', ') - self.visit(item) - self.write(right) - return visit - - visit_List = sequence_visit('[', ']') - visit_Set = sequence_visit('{', '}') - del sequence_visit - - def visit_Dict(self, node): - self.write('{') - for idx, (key, value) in enumerate(zip(node.keys, node.values)): - if idx: - self.write(', ') - self.visit(key) - self.write(': ') - self.visit(value) - self.write('}') - - def visit_BinOp(self, node): - self.write('(') - self.visit(node.left) - self.write(' %s ' % BINOP_SYMBOLS[get_symbol_key(node.op)]) - self.visit(node.right) - self.write(')') - - def visit_BoolOp(self, node): - self.write('(') - for idx, value in enumerate(node.values): - if idx: - self.write(' %s ' % BOOLOP_SYMBOLS[get_symbol_key(node.op)]) - self.visit(value) - self.write(')') - - def visit_Compare(self, node): - self.write('(') - self.visit(node.left) - for op, right in zip(node.ops, node.comparators): - self.write(' %s ' % CMPOP_SYMBOLS[get_symbol_key(op)]) - self.visit(right) - self.write(')') - - def visit_UnaryOp(self, node): - self.write('(') - op = UNARYOP_SYMBOLS[get_symbol_key(node.op)] - self.write(op) - if op == 'not': - self.write(' ') - self.visit(node.operand) - self.write(')') - - def visit_Subscript(self, node): - self.visit(node.value) - self.write('[') - self.visit(node.slice) - self.write(']') - - def visit_Slice(self, node): - if node.lower is not None: - self.visit(node.lower) - self.write(':') - if node.upper is not None: - self.visit(node.upper) - if node.step is not None: - self.write(':') - if not (isinstance(node.step, Name) and node.step.id == 'None'): - self.visit(node.step) - - def visit_ExtSlice(self, node): - for idx, item in node.dims: - if idx: - self.write(', ') - self.visit(item) - - def visit_Yield(self, node): - self.write('yield ') - self.visit(node.value) - - def visit_Lambda(self, node): - self.write('lambda ') - self.signature(node.args) - self.write(': ') - self.visit(node.body) - - def visit_Ellipsis(self, node): - self.write('Ellipsis') - - def generator_visit(left, right): - def visit(self, node): - self.write(left) - self.visit(node.elt) - for comprehension in node.generators: - self.visit(comprehension) - self.write(right) - return visit - - visit_ListComp = generator_visit('[', ']') - visit_GeneratorExp = generator_visit('(', ')') - visit_SetComp = generator_visit('{', '}') - del generator_visit - - def visit_DictComp(self, node): - self.write('{') - self.visit(node.key) - self.write(': ') - self.visit(node.value) - for comprehension in node.generators: - self.visit(comprehension) - self.write('}') - - def visit_IfExp(self, node): - self.visit(node.body) - self.write(' if ') - self.visit(node.test) - self.write(' else ') - self.visit(node.orelse) - - def visit_Starred(self, node): - self.write('*') - self.visit(node.value) - - def visit_Repr(self, node): - # XXX: python 2.6 only - self.write('`') - self.visit(node.value) - self.write('`') - - # Helper Nodes - - def visit_alias(self, node): - self.write(node.name) - if node.asname is not None: - self.write(' as ' + node.asname) - - def visit_comprehension(self, node): - self.write(' for ') - self.visit(node.target) - self.write(' in ') - self.visit(node.iter) - if node.ifs: - for if_ in node.ifs: - self.write(' if ') - self.visit(if_) - - def visit_excepthandler(self, node): - self.newline() - self.write('except') - if node.type is not None: - self.write(' ') - self.visit(node.type) - if node.name is not None: - self.write(' as ') - self.visit(node.name) - self.write(':') - self.body(node.body) diff --git a/planet/mako/ast.py b/planet/mako/ast.py deleted file mode 100644 index 6d4ef029..00000000 --- a/planet/mako/ast.py +++ /dev/null @@ -1,125 +0,0 @@ -# ast.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""utilities for analyzing expressions and blocks of Python code, as well as generating Python from AST nodes""" - -from mako import exceptions, pyparser, util -import re - -class PythonCode(object): - """represents information about a string containing Python code""" - def __init__(self, code, **exception_kwargs): - self.code = code - - # represents all identifiers which are assigned to at some point in the code - self.declared_identifiers = util.Set() - - # represents all identifiers which are referenced before their assignment, if any - self.undeclared_identifiers = util.Set() - - # note that an identifier can be in both the undeclared and declared lists. - - # using AST to parse instead of using code.co_varnames, code.co_names has several advantages: - # - we can locate an identifier as "undeclared" even if its declared later in the same block of code - # - AST is less likely to break with version changes (for example, the behavior of co_names changed a little bit - # in python version 2.5) - if isinstance(code, basestring): - expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs) - else: - expr = code - - f = pyparser.FindIdentifiers(self, **exception_kwargs) - f.visit(expr) - -class ArgumentList(object): - """parses a fragment of code as a comma-separated list of expressions""" - def __init__(self, code, **exception_kwargs): - self.codeargs = [] - self.args = [] - self.declared_identifiers = util.Set() - self.undeclared_identifiers = util.Set() - if isinstance(code, basestring): - if re.match(r"\S", code) and not re.match(r",\s*$", code): - # if theres text and no trailing comma, insure its parsed - # as a tuple by adding a trailing comma - code += "," - expr = pyparser.parse(code, "exec", **exception_kwargs) - else: - expr = code - - f = pyparser.FindTuple(self, PythonCode, **exception_kwargs) - f.visit(expr) - -class PythonFragment(PythonCode): - """extends PythonCode to provide identifier lookups in partial control statements - - e.g. - for x in 5: - elif y==9: - except (MyException, e): - etc. - """ - def __init__(self, code, **exception_kwargs): - m = re.match(r'^(\w+)(?:\s+(.*?))?:\s*(#|$)', code.strip(), re.S) - if not m: - raise exceptions.CompileException("Fragment '%s' is not a partial control statement" % code, **exception_kwargs) - if m.group(3): - code = code[:m.start(3)] - (keyword, expr) = m.group(1,2) - if keyword in ['for','if', 'while']: - code = code + "pass" - elif keyword == 'try': - code = code + "pass\nexcept:pass" - elif keyword == 'elif' or keyword == 'else': - code = "if False:pass\n" + code + "pass" - elif keyword == 'except': - code = "try:pass\n" + code + "pass" - else: - raise exceptions.CompileException("Unsupported control keyword: '%s'" % keyword, **exception_kwargs) - super(PythonFragment, self).__init__(code, **exception_kwargs) - - -class FunctionDecl(object): - """function declaration""" - def __init__(self, code, allow_kwargs=True, **exception_kwargs): - self.code = code - expr = pyparser.parse(code, "exec", **exception_kwargs) - - f = pyparser.ParseFunc(self, **exception_kwargs) - f.visit(expr) - if not hasattr(self, 'funcname'): - raise exceptions.CompileException("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) - - def get_argument_expressions(self, include_defaults=True): - """return the argument declarations of this FunctionDecl as a printable list.""" - 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 - kwargs = False - elif varargs: - arg = "*" + arg - varargs = False - else: - default = len(defaults) and defaults.pop() or None - if include_defaults and default: - namedecls.insert(0, "%s=%s" % (arg, pyparser.ExpressionGenerator(default).value())) - else: - namedecls.insert(0, arg) - return namedecls - -class FunctionArgs(FunctionDecl): - """the argument portion of a function declaration""" - def __init__(self, code, **kwargs): - super(FunctionArgs, self).__init__("def ANON(%s):pass" % code, **kwargs) diff --git a/planet/mako/cache.py b/planet/mako/cache.py deleted file mode 100644 index 43f73178..00000000 --- a/planet/mako/cache.py +++ /dev/null @@ -1,55 +0,0 @@ -from mako import exceptions - -try: - from beaker import cache - cache = cache.CacheManager() -except ImportError: - cache = None - -class Cache(object): - def __init__(self, id, starttime): - self.id = id - self.starttime = starttime - self.def_regions = {} - - def put(self, key, value, **kwargs): - defname = kwargs.pop('defname', None) - expiretime = kwargs.pop('expiretime', None) - createfunc = kwargs.pop('createfunc', None) - - self._get_cache(defname, **kwargs).put_value(key, starttime=self.starttime, expiretime=expiretime) - - def get(self, key, **kwargs): - defname = kwargs.pop('defname', None) - expiretime = kwargs.pop('expiretime', None) - createfunc = kwargs.pop('createfunc', None) - - return self._get_cache(defname, **kwargs).get_value(key, starttime=self.starttime, expiretime=expiretime, createfunc=createfunc) - - def invalidate(self, key, **kwargs): - defname = kwargs.pop('defname', None) - expiretime = kwargs.pop('expiretime', None) - createfunc = kwargs.pop('createfunc', None) - - self._get_cache(defname, **kwargs).remove_value(key, starttime=self.starttime, expiretime=expiretime) - - def invalidate_body(self): - self.invalidate('render_body', defname='render_body') - - def invalidate_def(self, name): - self.invalidate('render_%s' % name, defname='render_%s' % name) - - def invalidate_closure(self, name): - self.invalidate(name, defname=name) - - def _get_cache(self, defname, type=None, **kw): - if not cache: - raise exceptions.RuntimeException("the Beaker package is required to use cache functionality.") - if type == 'memcached': - type = 'ext:memcached' - if not type: - (type, kw) = self.def_regions.get(defname, ('memory', {})) - else: - self.def_regions[defname] = (type, kw) - return cache.get_cache(self.id, type=type, **kw) -
\ No newline at end of file diff --git a/planet/mako/codegen.py b/planet/mako/codegen.py deleted file mode 100644 index 0f1be999..00000000 --- a/planet/mako/codegen.py +++ /dev/null @@ -1,697 +0,0 @@ -# codegen.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""provides functionality for rendering a parsetree constructing into module source code.""" - -import time -import re -from mako.pygen import PythonPrinter -from mako import util, ast, parsetree, filters - -MAGIC_NUMBER = 5 - - -def compile(node, uri, filename=None, default_filters=None, buffer_filters=None, imports=None, source_encoding=None, generate_unicode=True): - """generate module source code given a parsetree node, uri, and optional source filename""" - - buf = util.FastEncodingBuffer(unicode=generate_unicode) - - printer = PythonPrinter(buf) - _GenerateRenderMethod(printer, _CompileContext(uri, filename, default_filters, buffer_filters, imports, source_encoding, generate_unicode), node) - return buf.getvalue() - -class _CompileContext(object): - def __init__(self, uri, filename, default_filters, buffer_filters, imports, source_encoding, generate_unicode): - self.uri = uri - self.filename = filename - self.default_filters = default_filters - self.buffer_filters = buffer_filters - self.imports = imports - self.source_encoding = source_encoding - self.generate_unicode = generate_unicode - -class _GenerateRenderMethod(object): - """a template visitor object which generates the full module source for a template.""" - def __init__(self, printer, compiler, node): - self.printer = printer - self.last_source_line = -1 - self.compiler = compiler - self.node = node - self.identifier_stack = [None] - - self.in_def = isinstance(node, parsetree.DefTag) - - if self.in_def: - name = "render_" + node.name - args = node.function_decl.get_argument_expressions() - filtered = len(node.filter_args.args) > 0 - buffered = eval(node.attributes.get('buffered', 'False')) - cached = eval(node.attributes.get('cached', 'False')) - defs = None - pagetag = None - else: - defs = self.write_toplevel() - pagetag = self.compiler.pagetag - name = "render_body" - if pagetag is not None: - args = pagetag.body_decl.get_argument_expressions() - if not pagetag.body_decl.kwargs: - args += ['**pageargs'] - cached = eval(pagetag.attributes.get('cached', 'False')) - else: - args = ['**pageargs'] - cached = False - buffered = filtered = False - if args is None: - args = ['context'] - else: - args = [a for a in ['context'] + args] - - self.write_render_callable(pagetag or node, name, args, buffered, filtered, cached) - - if defs is not None: - for node in defs: - _GenerateRenderMethod(printer, compiler, node) - - identifiers = property(lambda self:self.identifier_stack[-1]) - - def write_toplevel(self): - """traverse a template structure for module-level directives and generate the - start of module-level code.""" - inherit = [] - namespaces = {} - module_code = [] - encoding =[None] - - self.compiler.pagetag = None - - class FindTopLevel(object): - def visitInheritTag(s, node): - inherit.append(node) - def visitNamespaceTag(s, node): - namespaces[node.name] = node - def visitPageTag(s, node): - self.compiler.pagetag = node - def visitCode(s, node): - if node.ismodule: - module_code.append(node) - - f = FindTopLevel() - for n in self.node.nodes: - n.accept_visitor(f) - - self.compiler.namespaces = namespaces - - module_ident = util.Set() - for n in module_code: - module_ident = module_ident.union(n.declared_identifiers()) - - module_identifiers = _Identifiers() - module_identifiers.declared = module_ident - - # module-level names, python code - if not self.compiler.generate_unicode and self.compiler.source_encoding: - self.printer.writeline("# -*- encoding:%s -*-" % self.compiler.source_encoding) - - self.printer.writeline("from mako import runtime, filters, cache") - self.printer.writeline("UNDEFINED = runtime.UNDEFINED") - self.printer.writeline("__M_dict_builtin = dict") - self.printer.writeline("__M_locals_builtin = locals") - self.printer.writeline("_magic_number = %s" % repr(MAGIC_NUMBER)) - self.printer.writeline("_modified_time = %s" % repr(time.time())) - self.printer.writeline("_template_filename=%s" % repr(self.compiler.filename)) - self.printer.writeline("_template_uri=%s" % repr(self.compiler.uri)) - self.printer.writeline("_template_cache=cache.Cache(__name__, _modified_time)") - self.printer.writeline("_source_encoding=%s" % repr(self.compiler.source_encoding)) - if self.compiler.imports: - buf = '' - for imp in self.compiler.imports: - buf += imp + "\n" - self.printer.writeline(imp) - impcode = ast.PythonCode(buf, source='', lineno=0, pos=0, filename='template defined imports') - else: - impcode = None - - main_identifiers = module_identifiers.branch(self.node) - module_identifiers.topleveldefs = module_identifiers.topleveldefs.union(main_identifiers.topleveldefs) - [module_identifiers.declared.add(x) for x in ["UNDEFINED"]] - if impcode: - [module_identifiers.declared.add(x) for x in impcode.declared_identifiers] - - self.compiler.identifiers = module_identifiers - self.printer.writeline("_exports = %s" % repr([n.name for n in main_identifiers.topleveldefs.values()])) - self.printer.write("\n\n") - - if len(module_code): - self.write_module_code(module_code) - - if len(inherit): - self.write_namespaces(namespaces) - self.write_inherit(inherit[-1]) - elif len(namespaces): - self.write_namespaces(namespaces) - - return main_identifiers.topleveldefs.values() - - def write_render_callable(self, node, name, args, buffered, filtered, cached): - """write a top-level render callable. - - this could be the main render() method or that of a top-level def.""" - self.printer.writelines( - "def %s(%s):" % (name, ','.join(args)), - "context.caller_stack._push_frame()", - "try:" - ) - if buffered or filtered or cached: - self.printer.writeline("context._push_buffer()") - - self.identifier_stack.append(self.compiler.identifiers.branch(self.node)) - if not self.in_def and '**pageargs' in args: - self.identifier_stack[-1].argument_declared.add('pageargs') - - if not self.in_def and (len(self.identifiers.locally_assigned) > 0 or len(self.identifiers.argument_declared)>0): - self.printer.writeline("__M_locals = __M_dict_builtin(%s)" % ','.join(["%s=%s" % (x, x) for x in self.identifiers.argument_declared])) - - self.write_variable_declares(self.identifiers, toplevel=True) - - for n in self.node.nodes: - n.accept_visitor(self) - - self.write_def_finish(self.node, buffered, filtered, cached) - self.printer.writeline(None) - self.printer.write("\n\n") - if cached: - self.write_cache_decorator(node, name, args, buffered, self.identifiers, toplevel=True) - - def write_module_code(self, module_code): - """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.write_indented_block(n.text) - - def write_inherit(self, node): - """write the module-level inheritance-determination callable.""" - self.printer.writelines( - "def _mako_inherit(template, context):", - "_mako_generate_namespaces(context)", - "return runtime._inherit_from(context, %s, _template_uri)" % (node.parsed_attributes['file']), - None - ) - - def write_namespaces(self, namespaces): - """write the module-level namespace-generating callable.""" - self.printer.writelines( - "def _mako_get_namespace(context, name):", - "try:", - "return context.namespaces[(__name__, name)]", - "except KeyError:", - "_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 node.attributes.has_key('import'): - self.compiler.has_ns_imports = True - self.write_source_comment(node) - if len(node.nodes): - self.printer.writeline("def make_namespace():") - export = [] - identifiers = self.compiler.identifiers.branch(node) - class NSDefVisitor(object): - def visitDefTag(s, node): - self.write_inline_def(node, identifiers, nested=False) - export.append(node.name) - vis = NSDefVisitor() - for n in node.nodes: - n.accept_visitor(vis) - self.printer.writeline("return [%s]" % (','.join(export))) - self.printer.writeline(None) - callable_name = "make_namespace()" - else: - callable_name = "None" - self.printer.writeline("ns = runtime.Namespace(%s, context._clean_inheritance_tokens(), templateuri=%s, callables=%s, calling_uri=_template_uri, module=%s)" % (repr(node.name), node.parsed_attributes.get('file', 'None'), callable_name, node.parsed_attributes.get('module', 'None'))) - if eval(node.attributes.get('inheritable', "False")): - self.printer.writeline("context['self'].%s = ns" % (node.name)) - self.printer.writeline("context.namespaces[(__name__, %s)] = ns" % repr(node.name)) - self.printer.write("\n") - if not len(namespaces): - self.printer.writeline("pass") - self.printer.writeline(None) - - def write_variable_declares(self, identifiers, toplevel=False, limit=None): - """write variable declarations at the top of a function. - - the variable declarations are in the form of callable definitions for defs and/or - name lookup within the function's context argument. the names declared are based on the - names that are referenced in the function body, which don't otherwise have any explicit - assignment operation. names that are assigned within the body are assumed to be - locally-scoped variables and are not separately declared. - - for def callable definitions, if the def is a top-level callable then a - 'stub' callable is generated which wraps the current Context into a closure. if the def - is not top-level, it is fully rendered as a local closure.""" - - # collection of all defs available to us in this scope - comp_idents = dict([(c.name, c) for c in identifiers.defs]) - to_write = util.Set() - - # write "context.get()" for all variables we are going to need that arent in the namespace yet - to_write = to_write.union(identifiers.undeclared) - - # write closure functions for closures that we define right here - to_write = to_write.union(util.Set([c.name for c in identifiers.closuredefs.values()])) - - # remove identifiers that are declared in the argument signature of the callable - to_write = to_write.difference(identifiers.argument_declared) - - # remove identifiers that we are going to assign to. in this way we mimic Python's behavior, - # i.e. assignment to a variable within a block means that variable is now a "locally declared" var, - # which cannot be referenced beforehand. - to_write = to_write.difference(identifiers.locally_declared) - - # if a limiting set was sent, constraint to those items in that list - # (this is used for the caching decorator) - if limit is not None: - to_write = to_write.intersection(limit) - - if toplevel and getattr(self.compiler, 'has_ns_imports', False): - self.printer.writeline("_import_ns = {}") - self.compiler.has_imports = True - for ident, ns in self.compiler.namespaces.iteritems(): - if ns.attributes.has_key('import'): - self.printer.writeline("_mako_get_namespace(context, %s)._populate(_import_ns, %s)" % (repr(ident), repr(re.split(r'\s*,\s*', ns.attributes['import'])))) - - for ident in to_write: - if ident in comp_idents: - comp = comp_idents[ident] - if comp.is_root(): - self.write_def_decl(comp, identifiers) - else: - self.write_inline_def(comp, identifiers, nested=True) - elif ident in self.compiler.namespaces: - self.printer.writeline("%s = _mako_get_namespace(context, %s)" % (ident, repr(ident))) - else: - if getattr(self.compiler, 'has_ns_imports', False): - self.printer.writeline("%s = _import_ns.get(%s, context.get(%s, UNDEFINED))" % (ident, repr(ident), repr(ident))) - else: - self.printer.writeline("%s = context.get(%s, UNDEFINED)" % (ident, repr(ident))) - - 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.function_decl.funcname - namedecls = node.function_decl.get_argument_expressions() - nameargs = node.function_decl.get_argument_expressions(include_defaults=False) - if not self.in_def and (len(self.identifiers.locally_assigned) > 0 or len(self.identifiers.argument_declared) > 0): - nameargs.insert(0, 'context.locals_(__M_locals)') - else: - nameargs.insert(0, 'context') - self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls))) - self.printer.writeline("return render_%s(%s)" % (funcname, ",".join(nameargs))) - self.printer.writeline(None) - - def write_inline_def(self, node, identifiers, nested): - """write a locally-available def callable inside an enclosing def.""" - namedecls = node.function_decl.get_argument_expressions() - self.printer.writeline("def %s(%s):" % (node.name, ",".join(namedecls))) - filtered = len(node.filter_args.args) > 0 - buffered = eval(node.attributes.get('buffered', 'False')) - cached = eval(node.attributes.get('cached', 'False')) - self.printer.writelines( - "context.caller_stack._push_frame()", - "try:" - ) - if buffered or filtered or cached: - self.printer.writelines( - "context._push_buffer()", - ) - - identifiers = identifiers.branch(node, nested=nested) - - self.write_variable_declares(identifiers) - - self.identifier_stack.append(identifiers) - for n in node.nodes: - n.accept_visitor(self) - self.identifier_stack.pop() - - self.write_def_finish(node, buffered, filtered, cached) - self.printer.writeline(None) - if cached: - self.write_cache_decorator(node, node.name, namedecls, False, identifiers, inline=True, toplevel=False) - - def write_def_finish(self, node, buffered, filtered, cached, callstack=True): - """write the end section of a rendering function, either outermost or inline. - - this takes into account if the rendering function was filtered, buffered, etc. - and closes the corresponding try: block if any, and writes code to retrieve captured content, - apply filters, send proper return value.""" - if not buffered and not cached and not filtered: - self.printer.writeline("return ''") - if callstack: - self.printer.writelines( - "finally:", - "context.caller_stack._pop_frame()", - None - ) - - if buffered or filtered or cached: - if buffered or cached: - # in a caching scenario, don't try to get a writer - # from the context after popping; assume the caching - # implemenation might be using a context with no - # extra buffers - self.printer.writelines( - "finally:", - "__M_buf = context._pop_buffer()" - ) - else: - self.printer.writelines( - "finally:", - "__M_buf, __M_writer = context._pop_buffer_and_writer()" - ) - - if callstack: - self.printer.writeline("context.caller_stack._pop_frame()") - - s = "__M_buf.getvalue()" - if filtered: - s = self.create_filter_callable(node.filter_args.args, s, False) - self.printer.writeline(None) - if buffered and not cached: - s = self.create_filter_callable(self.compiler.buffer_filters, s, False) - if buffered or cached: - self.printer.writeline("return %s" % s) - else: - self.printer.writelines( - "__M_writer(%s)" % s, - "return ''" - ) - - def write_cache_decorator(self, node_or_pagetag, name, args, buffered, identifiers, inline=False, toplevel=False): - """write a post-function decorator to replace a rendering callable with a cached version of itself.""" - self.printer.writeline("__M_%s = %s" % (name, name)) - cachekey = node_or_pagetag.parsed_attributes.get('cache_key', repr(name)) - cacheargs = {} - for arg in (('cache_type', 'type'), ('cache_dir', 'data_dir'), ('cache_timeout', 'expiretime'), ('cache_url', 'url')): - val = node_or_pagetag.parsed_attributes.get(arg[0], None) - if val is not None: - if arg[1] == 'expiretime': - cacheargs[arg[1]] = int(eval(val)) - else: - cacheargs[arg[1]] = val - else: - if self.compiler.pagetag is not None: - val = self.compiler.pagetag.parsed_attributes.get(arg[0], None) - if val is not None: - if arg[1] == 'expiretime': - cacheargs[arg[1]] == int(eval(val)) - else: - cacheargs[arg[1]] = val - - self.printer.writeline("def %s(%s):" % (name, ','.join(args))) - - # form "arg1, arg2, arg3=arg3, arg4=arg4", etc. - pass_args = [ '=' in a and "%s=%s" % ((a.split('=')[0],)*2) or a for a in args] - - self.write_variable_declares(identifiers, toplevel=toplevel, limit=node_or_pagetag.undeclared_identifiers()) - if buffered: - s = "context.get('local').get_cached(%s, defname=%r, %screatefunc=lambda:__M_%s(%s))" % (cachekey, name, ''.join(["%s=%s, " % (k,v) for k, v in cacheargs.iteritems()]), name, ','.join(pass_args)) - # apply buffer_filters - s = self.create_filter_callable(self.compiler.buffer_filters, s, False) - self.printer.writelines("return " + s,None) - else: - self.printer.writelines( - "__M_writer(context.get('local').get_cached(%s, defname=%r, %screatefunc=lambda:__M_%s(%s)))" % (cachekey, name, ''.join(["%s=%s, " % (k,v) for k, v in cacheargs.iteritems()]), name, ','.join(pass_args)), - "return ''", - None - ) - - def create_filter_callable(self, args, target, is_expression): - """write a filter-applying expression based on the filters present in the given - filter names, adjusting for the global 'default' filter aliases as needed.""" - def locate_encode(name): - if re.match(r'decode\..+', name): - return "filters." + name - else: - return filters.DEFAULT_ESCAPES.get(name, name) - - if 'n' not in args: - if is_expression: - if self.compiler.pagetag: - args = self.compiler.pagetag.filter_args.args + args - if self.compiler.default_filters: - args = self.compiler.default_filters + args - for e in args: - # if filter given as a function, get just the identifier portion - if e == 'n': - continue - m = re.match(r'(.+?)(\(.*\))', e) - if m: - (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) - if len(node.escapes) or (self.compiler.pagetag is not None and len(self.compiler.pagetag.filter_args.args)) or len(self.compiler.default_filters): - s = self.create_filter_callable(node.escapes_code.args, "%s" % node.text, True) - self.printer.writeline("__M_writer(%s)" % s) - else: - self.printer.writeline("__M_writer(%s)" % node.text) - - def visitControlLine(self, node): - if node.isend: - self.printer.writeline(None) - else: - self.write_source_comment(node) - self.printer.writeline(node.text) - def visitText(self, node): - self.write_source_comment(node) - self.printer.writeline("__M_writer(%s)" % repr(node.content)) - def visitTextTag(self, node): - filtered = len(node.filter_args.args) > 0 - if filtered: - self.printer.writelines( - "__M_writer = context._push_writer()", - "try:", - ) - for n in node.nodes: - n.accept_visitor(self) - if filtered: - self.printer.writelines( - "finally:", - "__M_buf, __M_writer = context._pop_buffer_and_writer()", - "__M_writer(%s)" % self.create_filter_callable(node.filter_args.args, "__M_buf.getvalue()", False), - None - ) - - def visitCode(self, node): - if not node.ismodule: - self.write_source_comment(node) - self.printer.write_indented_block(node.text) - - if not self.in_def and len(self.identifiers.locally_assigned) > 0: - # if we are the "template" def, fudge locally declared/modified variables into the "__M_locals" dictionary, - # which is used for def calls within the same template, to simulate "enclosing scope" - self.printer.writeline('__M_locals.update(__M_dict_builtin([(__M_key, __M_locals_builtin()[__M_key]) for __M_key in [%s] if __M_key in __M_locals_builtin()]))' % ','.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)) - else: - self.printer.writeline("runtime._include_file(context, %s, _template_uri)" % (node.parsed_attributes['file'])) - - def visitNamespaceTag(self, node): - pass - - def visitDefTag(self, node): - pass - - def visitCallTag(self, node): - self.printer.writeline("def ccall(caller):") - export = ['body'] - callable_identifiers = self.identifiers.branch(node, nested=True) - body_identifiers = callable_identifiers.branch(node, nested=False) - # we want the 'caller' passed to ccall to be used for the body() function, - # but for other non-body() <%def>s within <%call> we want the current caller off the call stack (if any) - body_identifiers.add_declared('caller') - - self.identifier_stack.append(body_identifiers) - class DefVisitor(object): - def visitDefTag(s, node): - self.write_inline_def(node, callable_identifiers, nested=False) - export.append(node.name) - # remove defs that are within the <%call> from the "closuredefs" defined - # in the body, so they dont render twice - if node.name in body_identifiers.closuredefs: - del body_identifiers.closuredefs[node.name] - - vis = DefVisitor() - for n in node.nodes: - n.accept_visitor(vis) - self.identifier_stack.pop() - - bodyargs = node.body_decl.get_argument_expressions() - self.printer.writeline("def body(%s):" % ','.join(bodyargs)) - # TODO: figure out best way to specify buffering/nonbuffering (at call time would be better) - buffered = False - if buffered: - self.printer.writelines( - "context._push_buffer()", - "try:" - ) - self.write_variable_declares(body_identifiers) - self.identifier_stack.append(body_identifiers) - - for n in node.nodes: - n.accept_visitor(self) - self.identifier_stack.pop() - - self.write_def_finish(node, buffered, False, False, callstack=False) - self.printer.writelines( - None, - "return [%s]" % (','.join(export)), - None - ) - - self.printer.writelines( - # get local reference to current caller, if any - "caller = context.caller_stack._get_caller()", - # push on caller for nested call - "context.caller_stack.nextcaller = runtime.Namespace('caller', context, callables=ccall(caller))", - "try:") - self.write_source_comment(node) - self.printer.writelines( - "__M_writer(%s)" % self.create_filter_callable([], node.attributes['expr'], True), - "finally:", - "context.caller_stack.nextcaller = None", - None - ) - -class _Identifiers(object): - """tracks the status of identifier names as template code is rendered.""" - def __init__(self, node=None, parent=None, nested=False): - if parent is not None: - # things that have already been declared in an enclosing namespace (i.e. names we can just use) - self.declared = util.Set(parent.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 had as undeclared will have been declared by that parent, - # and therefore we have them in our scope. - if nested: - self.declared = self.declared.union(parent.undeclared) - - # top level defs that are available - self.topleveldefs = util.SetLikeDict(**parent.topleveldefs) - else: - self.declared = util.Set() - self.topleveldefs = util.SetLikeDict() - - # things within this level that are referenced before they are declared (e.g. assigned to) - self.undeclared = util.Set() - - # things that are declared locally. some of these things could be in the "undeclared" - # list as well if they are referenced before declared - self.locally_declared = util.Set() - - # assignments made in explicit python blocks. these will be propigated to - # the context of local def calls. - self.locally_assigned = util.Set() - - # things that are declared in the argument signature of the def callable - self.argument_declared = util.Set() - - # closure defs that are defined in this level - self.closuredefs = util.SetLikeDict() - - self.node = node - - if node is not None: - node.accept_visitor(self) - - def branch(self, node, **kwargs): - """create a new Identifiers for a new Node, with this Identifiers as the parent.""" - return _Identifiers(node, self, **kwargs) - - defs = property(lambda self:util.Set(self.topleveldefs.union(self.closuredefs).values())) - - def __repr__(self): - return "Identifiers(declared=%s, locally_declared=%s, undeclared=%s, topleveldefs=%s, closuredefs=%s, argumenetdeclared=%s)" % (repr(list(self.declared)), repr(list(self.locally_declared)), repr(list(self.undeclared)), repr([c.name for c in self.topleveldefs.values()]), repr([c.name for c in self.closuredefs.values()]), repr(self.argument_declared)) - - def check_declared(self, node): - """update the state of this Identifiers with the undeclared and declared identifiers of the given node.""" - for ident in node.undeclared_identifiers(): - if ident != 'context' and ident not in self.declared.union(self.locally_declared): - self.undeclared.add(ident) - for ident in node.declared_identifiers(): - self.locally_declared.add(ident) - - def add_declared(self, ident): - self.declared.add(ident) - if ident in self.undeclared: - self.undeclared.remove(ident) - - def visitExpression(self, node): - self.check_declared(node) - def visitControlLine(self, node): - self.check_declared(node) - def visitCode(self, node): - if not node.ismodule: - self.check_declared(node) - self.locally_assigned = self.locally_assigned.union(node.declared_identifiers()) - def visitDefTag(self, node): - if node.is_root(): - self.topleveldefs[node.name] = node - elif node is not self.node: - self.closuredefs[node.name] = node - for ident in node.undeclared_identifiers(): - if ident != 'context' and ident not in self.declared.union(self.locally_declared): - self.undeclared.add(ident) - # visit defs only one level deep - if node is self.node: - for ident in node.declared_identifiers(): - self.argument_declared.add(ident) - for n in node.nodes: - n.accept_visitor(self) - def visitIncludeTag(self, node): - self.check_declared(node) - def visitPageTag(self, node): - for ident in node.declared_identifiers(): - self.argument_declared.add(ident) - self.check_declared(node) - - 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): - self.undeclared.add(ident) - for ident in node.declared_identifiers(): - self.argument_declared.add(ident) - for n in node.nodes: - n.accept_visitor(self) - else: - for ident in node.undeclared_identifiers(): - if ident != 'context' and ident not in self.declared.union(self.locally_declared): - self.undeclared.add(ident) - diff --git a/planet/mako/exceptions.py b/planet/mako/exceptions.py deleted file mode 100644 index 3f4e3a77..00000000 --- a/planet/mako/exceptions.py +++ /dev/null @@ -1,252 +0,0 @@ -# exceptions.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""exception classes""" - -import traceback, sys, re -from mako import util - -class MakoException(Exception): - pass - -class RuntimeException(MakoException): - pass - -def _format_filepos(lineno, pos, filename): - if filename is None: - return " at line: %d char: %d" % (lineno, pos) - else: - return " in file '%s' at line: %d char: %d" % (filename, lineno, pos) -class CompileException(MakoException): - def __init__(self, message, source, lineno, pos, filename): - MakoException.__init__(self, message + _format_filepos(lineno, pos, filename)) - self.lineno =lineno - self.pos = pos - self.filename = filename - self.source = source - -class SyntaxException(MakoException): - def __init__(self, message, source, lineno, pos, filename): - MakoException.__init__(self, message + _format_filepos(lineno, pos, filename)) - self.lineno =lineno - self.pos = pos - self.filename = filename - self.source = source - -class TemplateLookupException(MakoException): - pass - -class TopLevelLookupException(TemplateLookupException): - pass - -class RichTraceback(object): - """pulls the current exception from the sys traceback and extracts Mako-specific - template information. - - Usage: - - RichTraceback() - - Properties: - - error - the exception instance. - source - source code of the file where the error occured. if the error occured within a compiled template, - this is the template source. - lineno - line number where the error occured. if the error occured within a compiled template, the line number - is adjusted to that of the template source - records - a list of 8-tuples containing the original python traceback elements, plus the - filename, line number, source line, and full template source for the traceline mapped back to its originating source - template, if any for that traceline (else the fields are None). - reverse_records - the list of records in reverse - traceback - a list of 4-tuples, in the same format as a regular python traceback, with template-corresponding - traceback records replacing the originals - reverse_traceback - the traceback list in reverse - """ - def __init__(self): - (self.source, self.lineno) = ("", 0) - (t, self.error, self.records) = self._init() - if self.error is None: - self.error = t - if isinstance(self.error, CompileException) or isinstance(self.error, SyntaxException): - import mako.template - self.source = self.error.source - self.lineno = self.error.lineno - self._has_source = True - self.reverse_records = [r for r in self.records] - self.reverse_records.reverse() - def _get_reformatted_records(self, records): - for rec in records: - if rec[6] is not None: - yield (rec[4], rec[5], rec[2], rec[6]) - else: - yield tuple(rec[0:4]) - traceback = property(lambda self:self._get_reformatted_records(self.records), doc=""" - return a list of 4-tuple traceback records (i.e. normal python format) - with template-corresponding lines remapped to the originating template - """) - reverse_traceback = property(lambda self:self._get_reformatted_records(self.reverse_records), doc=""" - return the same data as traceback, except in reverse order - """) - def _init(self): - """format a traceback from sys.exc_info() into 7-item tuples, containing - the regular four traceback tuple items, plus the original template - filename, the line number adjusted relative to the template source, and - code line from that line number of the template.""" - import mako.template - mods = {} - (type, value, trcback) = sys.exc_info() - rawrecords = traceback.extract_tb(trcback) - new_trcback = [] - for filename, lineno, function, line in rawrecords: - try: - (line_map, template_lines) = mods[filename] - except KeyError: - try: - info = mako.template._get_module_info(filename) - module_source = info.code - template_source = info.source - template_filename = info.template_filename or filename - except KeyError: - new_trcback.append((filename, lineno, function, line, 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)) - else: - template_ln += 1 - module_ln += 1 - line_map[module_ln] = template_ln - template_lines = [line for line in template_source.split("\n")] - mods[filename] = (line_map, template_lines) - - template_ln = line_map[lineno] - if template_ln <= len(template_lines): - template_line = template_lines[template_ln - 1] - else: - template_line = None - new_trcback.append((filename, lineno, function, line, template_filename, template_ln, template_line, template_source)) - if not self.source: - 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] - break - else: - try: - # A normal .py file (not a Template) - fp = open(new_trcback[-1][0]) - encoding = util.parse_encoding(fp) - fp.seek(0) - self.source = fp.read() - fp.close() - if encoding: - self.source = self.source.decode(encoding) - except IOError: - self.source = '' - self.lineno = new_trcback[-1][1] - return (type, value, new_trcback) - - -def text_error_template(lookup=None): - """provides a template that renders a stack trace in a similar format to the Python interpreter, - substituting source template filenames, line numbers and code for that of the originating - source template, as applicable.""" - import mako.template - return mako.template.Template(r""" -<%! - from mako.exceptions import RichTraceback -%>\ -<% - tback = RichTraceback() -%>\ -Traceback (most recent call last): -% for (filename, lineno, function, line) in tback.traceback: - File "${filename}", line ${lineno}, in ${function or '?'} - ${line | unicode.strip} -% endfor -${str(tback.error.__class__.__name__)}: ${str(tback.error)} -""") - -def html_error_template(): - """provides a template that renders a stack trace in an HTML format, providing an excerpt of - code as well as substituting source 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.""" - import mako.template - return mako.template.Template(r""" -<%! - from mako.exceptions import RichTraceback -%> -<%page args="full=True, css=True"/> -% if full: -<html> -<head> - <title>Mako Runtime Error</title> -% endif -% if css: - <style> - body { font-family:verdana; margin:10px 30px 10px 30px;} - .stacktrace { margin:5px 5px 5px 5px; } - .highlight { padding:0px 10px 0px 10px; background-color:#9F9FDF; } - .nonhighlight { padding:0px; background-color:#DFDFDF; } - .sample { padding:10px; margin:10px 10px 10px 10px; font-family:monospace; } - .sampleline { padding:0px 10px 0px 10px; } - .sourceline { margin:5px 5px 10px 5px; font-family:monospace;} - .location { font-size:80%; } - </style> -% endif -% if full: -</head> -<body> -% endif - -<h2>Error !</h2> -<% - tback = RichTraceback() - src = tback.source - line = tback.lineno - if src: - lines = src.split('\n') - else: - lines = None -%> -<h3>${str(tback.error.__class__.__name__)}: ${str(tback.error)}</h3> - -% if lines: - <div class="sample"> - <div class="nonhighlight"> -% for index in range(max(0, line-4),min(len(lines), line+5)): - % if index + 1 == line: -<div class="highlight">${index + 1} ${lines[index] | h}</div> - % else: -<div class="sampleline">${index + 1} ${lines[index] | h}</div> - % endif -% endfor - </div> - </div> -% endif - -<div class="stacktrace"> -% for (filename, lineno, function, line) in tback.reverse_traceback: - <div class="location">${filename}, line ${lineno}:</div> - <div class="sourceline">${line | h}</div> -% endfor -</div> - -% if full: -</body> -</html> -% endif -""", output_encoding=sys.getdefaultencoding(), encoding_errors='htmlentityreplace') diff --git a/planet/mako/filters.py b/planet/mako/filters.py deleted file mode 100644 index f2522415..00000000 --- a/planet/mako/filters.py +++ /dev/null @@ -1,170 +0,0 @@ -# filters.py -# Copyright (C) 2006, 2007, 2008 Geoffrey T. Dairiki <[email protected]> and Michael Bayer <[email protected]> -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - - -import re, cgi, urllib, htmlentitydefs, codecs -from StringIO import StringIO - -xml_escapes = { - '&' : '&', - '>' : '>', - '<' : '<', - '"' : '"', # also " in html-only - "'" : ''' # also ' in html-only -} -# XXX: " is valid in HTML and XML -# ' is not valid HTML, but is valid XML - -def html_escape(string): - return cgi.escape(string, True) - -def xml_escape(string): - return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string) - -def url_escape(string): - # convert into a list of octets - string = string.encode("utf8") - return urllib.quote_plus(string) - -def url_unescape(string): - text = urllib.unquote_plus(string) - if not is_ascii_str(text): - text = text.decode("utf8") - return text - -def trim(string): - return string.strip() - - -class Decode(object): - def __getattr__(self, key): - def decode(x): - if isinstance(x, unicode): - return x - elif not isinstance(x, str): - return unicode(str(x), encoding=key) - else: - return unicode(x, encoding=key) - return decode -decode = Decode() - - -_ASCII_re = re.compile(r'\A[\x00-\x7f]*\Z') - -def is_ascii_str(text): - return isinstance(text, str) and _ASCII_re.match(text) - -################################################################ - -class XMLEntityEscaper(object): - def __init__(self, codepoint2name, name2codepoint): - self.codepoint2entity = dict([(c, u'&%s;' % n) - for c,n in codepoint2name.iteritems()]) - self.name2codepoint = name2codepoint - - def escape_entities(self, text): - """Replace characters with their character entity references. - - Only characters corresponding to a named entity are replaced. - """ - return unicode(text).translate(self.codepoint2entity) - - def __escape(self, m): - codepoint = ord(m.group()) - try: - return self.codepoint2entity[codepoint] - except (KeyError, IndexError): - return '&#x%X;' % codepoint - - - __escapable = re.compile(r'["&<>]|[^\x00-\x7f]') - - def escape(self, text): - """Replace characters with their character references. - - Replace characters by their named entity references. - Non-ASCII characters, if they do not have a named entity reference, - are replaced by numerical character references. - - The return value is guaranteed to be ASCII. - """ - return self.__escapable.sub(self.__escape, unicode(text) - ).encode('ascii') - - # XXX: This regexp will not match all valid XML entity names__. - # (It punts on details involving involving CombiningChars and Extenders.) - # - # .. __: http://www.w3.org/TR/2000/REC-xml-20001006#NT-EntityRef - __characterrefs = re.compile(r'''& (?: - \#(\d+) - | \#x([\da-f]+) - | ( (?!\d) [:\w] [-.:\w]+ ) - ) ;''', - re.X | re.UNICODE) - - def __unescape(self, m): - dval, hval, name = m.groups() - if dval: - codepoint = int(dval) - elif hval: - codepoint = int(hval, 16) - else: - codepoint = self.name2codepoint.get(name, 0xfffd) - # U+FFFD = "REPLACEMENT CHARACTER" - if codepoint < 128: - return chr(codepoint) - return unichr(codepoint) - - def unescape(self, text): - """Unescape character references. - - All character references (both entity references and numerical - character references) are unescaped. - """ - return self.__characterrefs.sub(self.__unescape, text) - - -_html_entities_escaper = XMLEntityEscaper(htmlentitydefs.codepoint2name, - htmlentitydefs.name2codepoint) - -html_entities_escape = _html_entities_escaper.escape_entities -html_entities_unescape = _html_entities_escaper.unescape - - -def htmlentityreplace_errors(ex): - """An encoding error handler. - - This python `codecs`_ error handler replaces unencodable - characters with HTML entities, or, if no HTML entity exists for - the character, XML character references. - - >>> u'The cost was \u20ac12.'.encode('latin1', 'htmlentityreplace') - 'The cost was €12.' - """ - if isinstance(ex, UnicodeEncodeError): - # Handle encoding errors - bad_text = ex.object[ex.start:ex.end] - text = _html_entities_escaper.escape(bad_text) - return (unicode(text), ex.end) - raise ex - -codecs.register_error('htmlentityreplace', htmlentityreplace_errors) - - -# TODO: options to make this dynamic per-compilation will be added in a later release -DEFAULT_ESCAPES = { - 'x':'filters.xml_escape', - 'h':'filters.html_escape', - 'u':'filters.url_escape', - 'trim':'filters.trim', - 'entity':'filters.html_entities_escape', - 'unicode':'unicode', - 'decode':'decode', - 'str':'str', - 'n':'n' -} - - diff --git a/planet/mako/lexer.py b/planet/mako/lexer.py deleted file mode 100644 index 785461c9..00000000 --- a/planet/mako/lexer.py +++ /dev/null @@ -1,328 +0,0 @@ -# lexer.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""provides the Lexer class for parsing template strings into parse trees.""" - -import re, codecs -from mako import parsetree, exceptions -from mako.pygen import adjust_whitespace - -_regexp_cache = {} - -class Lexer(object): - def __init__(self, text, filename=None, disable_unicode=False, input_encoding=None, preprocessor=None): - self.text = text - self.filename = filename - self.template = parsetree.TemplateNode(self.filename) - self.matched_lineno = 1 - self.matched_charpos = 0 - self.lineno = 1 - self.match_position = 0 - self.tag = [] - self.control_line = [] - self.disable_unicode = disable_unicode - self.encoding = input_encoding - if preprocessor is None: - self.preprocessor = [] - elif not hasattr(preprocessor, '__iter__'): - self.preprocessor = [preprocessor] - else: - self.preprocessor = preprocessor - - exception_kwargs = property(lambda self:{'source':self.text, 'lineno':self.matched_lineno, 'pos':self.matched_charpos, 'filename':self.filename}) - - def match(self, regexp, flags=None): - """match the given regular expression string and flags to the current text position. - - if a match occurs, update the current text and line position.""" - mp = self.match_position - try: - reg = _regexp_cache[(regexp, flags)] - except KeyError: - if flags: - reg = re.compile(regexp, flags) - else: - reg = re.compile(regexp) - _regexp_cache[(regexp, flags)] = reg - - match = reg.match(self.text, self.match_position) - if match: - (start, end) = match.span() - if end == start: - self.match_position = end + 1 - else: - self.match_position = end - 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 - self.matched_charpos = mp - cp - self.lineno += len(lines) - #print "MATCHED:", match.group(0), "LINE START:", self.matched_lineno, "LINE END:", self.lineno - #print "MATCH:", regexp, "\n", self.text[mp : mp + 15], (match and "TRUE" or "FALSE") - return match - - def parse_until_text(self, *text): - startpos = self.match_position - while True: - match = self.match(r'#.*\n') - if match: - continue - match = self.match(r'(\"\"\"|\'\'\'|\"|\')') - if match: - m = self.match(r'.*?%s' % match.group(1), re.S) - if not m: - raise exceptions.SyntaxException("Unmatched '%s'" % match.group(1), **self.exception_kwargs) - else: - match = self.match(r'(%s)' % r'|'.join(text)) - if match: - return (self.text[startpos:self.match_position-len(match.group(1))], match.group(1)) - else: - match = self.match(r".*?(?=\"|\'|#|%s)" % r'|'.join(text), re.S) - if not match: - raise exceptions.SyntaxException("Expected: %s" % ','.join(text), **self.exception_kwargs) - - def append_node(self, nodecls, *args, **kwargs): - kwargs.setdefault('source', self.text) - kwargs.setdefault('lineno', self.matched_lineno) - kwargs.setdefault('pos', self.matched_charpos) - kwargs['filename'] = self.filename - node = nodecls(*args, **kwargs) - if len(self.tag): - self.tag[-1].nodes.append(node) - else: - self.template.nodes.append(node) - if isinstance(node, parsetree.Tag): - if len(self.tag): - node.parent = self.tag[-1] - self.tag.append(node) - elif isinstance(node, parsetree.ControlLine): - if node.isend: - self.control_line.pop() - elif node.is_primary: - self.control_line.append(node) - elif len(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) - - def escape_code(self, text): - if not self.disable_unicode and self.encoding: - return text.encode('ascii', 'backslashreplace') - else: - return text - - def parse(self): - for preproc in self.preprocessor: - self.text = preproc(self.text) - if not isinstance(self.text, unicode) and self.text.startswith(codecs.BOM_UTF8): - self.text = self.text[len(codecs.BOM_UTF8):] - parsed_encoding = 'utf-8' - me = self.match_encoding() - if me is not None and me != 'utf-8': - raise exceptions.CompileException("Found utf-8 BOM in file, with conflicting magic encoding comment of '%s'" % me, self.text.decode('utf-8', 'ignore'), 0, 0, self.filename) - else: - parsed_encoding = self.match_encoding() - if parsed_encoding: - self.encoding = parsed_encoding - if not self.disable_unicode and not isinstance(self.text, unicode): - if self.encoding: - try: - self.text = self.text.decode(self.encoding) - except UnicodeDecodeError, e: - raise exceptions.CompileException("Unicode decode operation of encoding '%s' failed" % self.encoding, self.text.decode('utf-8', 'ignore'), 0, 0, self.filename) - else: - try: - self.text = self.text.decode() - except UnicodeDecodeError, e: - raise exceptions.CompileException("Could not read template using encoding of 'ascii'. Did you forget a magic encoding comment?", self.text.decode('utf-8', 'ignore'), 0, 0, self.filename) - - self.textlength = len(self.text) - - while (True): - if self.match_position > self.textlength: - break - - if self.match_end(): - break - if self.match_expression(): - continue - if self.match_control_line(): - continue - if self.match_comment(): - continue - if self.match_tag_start(): - continue - if self.match_tag_end(): - continue - if self.match_python_block(): - continue - if self.match_text(): - continue - - if self.match_position > self.textlength: - break - raise exceptions.CompileException("assertion failed") - - if len(self.tag): - raise exceptions.SyntaxException("Unclosed tag: <%%%s>" % self.tag[-1].keyword, **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) - return self.template - - def match_encoding(self): - match = self.match(r'#.*coding[:=]\s*([-\w.]+).*\r?\n') - if match: - return match.group(1) - else: - return None - - def match_tag_start(self): - match = self.match(r''' - \<% # opening tag - - (\w+) # keyword - - ((?:\s+\w+|=|".*?"|'.*?')*) # attrname, = sign, string expression - - \s* # more whitespace - - (/)?> # closing - - ''', - - re.I | re.S | re.X) - - if match: - (keyword, attr, isend) = (match.group(1).lower(), match.group(2), match.group(3)) - self.keyword = keyword - attributes = {} - if attr: - for att in re.findall(r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr): - (key, val1, val2) = att - text = val1 or val2 - text = text.replace('\r\n', '\n') - attributes[key] = self.escape_code(text) - self.append_node(parsetree.Tag, keyword, attributes) - if isend: - self.tag.pop() - else: - if keyword == 'text': - match = self.match(r'(.*?)(?=\</%text>)', re.S) - if not match: - raise exceptions.SyntaxException("Unclosed tag: <%%%s>" % self.tag[-1].keyword, **self.exception_kwargs) - self.append_node(parsetree.Text, match.group(1)) - return self.match_tag_end() - return True - else: - return False - - def match_tag_end(self): - match = self.match(r'\</%[\t ]*(.+?)[\t ]*>') - if match: - if not len(self.tag): - raise exceptions.SyntaxException("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) - self.tag.pop() - return True - else: - return False - - def match_end(self): - match = self.match(r'\Z', re.S) - if match: - string = match.group() - if string: - return string - else: - return True - else: - return False - - def match_text(self): - match = self.match(r""" - (.*?) # anything, followed by: - ( - (?<=\n)(?=[ \t]*(?=%|\#\#)) # an eval or line-based comment preceded by a consumed \n and whitespace - | - (?=\${) # an expression - | - (?=\#\*) # multiline comment - | - (?=</?[%&]) # a substitution or block or call start or end - # - don't consume - | - (\\\r?\n) # an escaped newline - throw away - | - \Z # end of string - )""", re.X | re.S) - - if match: - text = match.group(1) - self.append_node(parsetree.Text, text) - return True - else: - return False - - def match_python_block(self): - match = self.match(r"<%(!)?") - if match: - (line, pos) = (self.matched_lineno, self.matched_charpos) - (text, end) = self.parse_until_text(r'%>') - text = adjust_whitespace(text) + "\n" # the trailing newline helps compiler.parse() not complain about indentation - self.append_node(parsetree.Code, self.escape_code(text), match.group(1)=='!', lineno=line, pos=pos) - return True - else: - return False - - def match_expression(self): - match = self.match(r"\${") - if match: - (line, pos) = (self.matched_lineno, self.matched_charpos) - (text, end) = self.parse_until_text(r'\|', r'}') - if end == '|': - (escapes, end) = self.parse_until_text(r'}') - else: - escapes = "" - text = text.replace('\r\n', '\n') - self.append_node(parsetree.Expression, self.escape_code(text), escapes.strip(), lineno=line, pos=pos) - return True - else: - return False - - def match_control_line(self): - match = self.match(r"(?<=^)[\t ]*(%|##)[\t ]*((?:(?:\\r?\n)|[^\r\n])*)(?:\r?\n|\Z)", re.M) - if match: - operator = match.group(1) - text = match.group(2) - if operator == '%': - m2 = re.match(r'(end)?(\w+)\s*(.*)', text) - if not m2: - raise exceptions.SyntaxException("Invalid control line: '%s'" % text, **self.exception_kwargs) - (isend, keyword) = m2.group(1, 2) - isend = (isend is not None) - - if isend: - if not len(self.control_line): - raise exceptions.SyntaxException("No starting keyword '%s' for '%s'" % (keyword, text), **self.exception_kwargs) - elif self.control_line[-1].keyword != keyword: - raise exceptions.SyntaxException("Keyword '%s' doesn't match keyword '%s'" % (text, self.control_line[-1].keyword), **self.exception_kwargs) - self.append_node(parsetree.ControlLine, keyword, isend, self.escape_code(text)) - else: - self.append_node(parsetree.Comment, text) - return True - else: - return False - - def match_comment(self): - """matches the multiline version of a comment""" - match = self.match(r"<%doc>(.*?)</%doc>", re.S) - if match: - self.append_node(parsetree.Comment, match.group(1)) - return True - else: - return False - diff --git a/planet/mako/lookup.py b/planet/mako/lookup.py deleted file mode 100644 index f720e4c3..00000000 --- a/planet/mako/lookup.py +++ /dev/null @@ -1,136 +0,0 @@ -# lookup.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -import os, stat, posixpath, re -from mako import exceptions, util -from mako.template import Template - -try: - import threading -except: - import dummy_threading as threading - -class TemplateCollection(object): - def has_template(self, uri): - try: - self.get_template(uri) - return True - except exceptions.TemplateLookupException, e: - return False - def get_template(self, uri, relativeto=None): - raise NotImplementedError() - def filename_to_uri(self, uri, filename): - """convert the given filename to a uri relative to this TemplateCollection.""" - return uri - - def adjust_uri(self, uri, filename): - """adjust the given uri based on the calling filename. - - when this method is called from the runtime, the 'filename' parameter - is taken directly to the 'filename' attribute of the calling - template. Therefore a custom TemplateCollection subclass can place any string - identifier desired in the "filename" parameter of the Template objects it constructs - and have them come back here.""" - return uri - -class TemplateLookup(TemplateCollection): - def __init__(self, directories=None, module_directory=None, filesystem_checks=True, collection_size=-1, format_exceptions=False, - error_handler=None, disable_unicode=False, output_encoding=None, encoding_errors='strict', cache_type=None, cache_dir=None, cache_url=None, - modulename_callable=None, default_filters=None, buffer_filters=[], imports=None, input_encoding=None, preprocessor=None): - if isinstance(directories, basestring): - directories = [directories] - self.directories = [posixpath.normpath(d) for d in directories or []] - self.module_directory = module_directory - self.modulename_callable = modulename_callable - self.filesystem_checks = filesystem_checks - self.collection_size = collection_size - self.template_args = {'format_exceptions':format_exceptions, 'error_handler':error_handler, 'disable_unicode':disable_unicode, 'output_encoding':output_encoding, 'encoding_errors':encoding_errors, 'input_encoding':input_encoding, 'module_directory':module_directory, 'cache_type':cache_type, 'cache_dir':cache_dir or module_directory, 'cache_url':cache_url, 'default_filters':default_filters, 'buffer_filters':buffer_filters, 'imports':imports, 'preprocessor':preprocessor} - if collection_size == -1: - self.__collection = {} - self._uri_cache = {} - else: - self.__collection = util.LRUCache(collection_size) - self._uri_cache = util.LRUCache(collection_size) - self._mutex = threading.Lock() - - def get_template(self, uri): - try: - if self.filesystem_checks: - return self.__check(uri, self.__collection[uri]) - else: - return self.__collection[uri] - except KeyError: - u = re.sub(r'^\/+', '', uri) - for dir in self.directories: - srcfile = posixpath.normpath(posixpath.join(dir, u)) - if os.path.exists(srcfile): - return self.__load(srcfile, uri) - else: - raise exceptions.TopLevelLookupException("Cant locate template for uri '%s'" % uri) - - def adjust_uri(self, uri, relativeto): - """adjust the given uri based on the calling filename.""" - - if uri[0] != '/': - if relativeto is not None: - return posixpath.join(posixpath.dirname(relativeto), uri) - else: - return '/' + uri - else: - return uri - - - def filename_to_uri(self, filename): - try: - return self._uri_cache[filename] - except KeyError: - value = self.__relativeize(filename) - self._uri_cache[filename] = value - return value - - def __relativeize(self, filename): - """return the portion of a filename that is 'relative' to the directories in this lookup.""" - filename = posixpath.normpath(filename) - for dir in self.directories: - if filename[0:len(dir)] == dir: - return filename[len(dir):] - else: - return None - - def __load(self, filename, uri): - self._mutex.acquire() - try: - try: - # try returning from collection one more time in case concurrent thread already loaded - return self.__collection[uri] - except KeyError: - pass - try: - self.__collection[uri] = Template(uri=uri, filename=posixpath.normpath(filename), lookup=self, module_filename=(self.modulename_callable is not None and self.modulename_callable(filename, uri) or None), **self.template_args) - return self.__collection[uri] - except: - self.__collection.pop(uri, None) - raise - finally: - self._mutex.release() - - def __check(self, uri, template): - if template.filename is None: - return template - if not os.path.exists(template.filename): - self.__collection.pop(uri, None) - raise exceptions.TemplateLookupException("Cant locate template for uri '%s'" % uri) - elif template.module._modified_time < os.stat(template.filename)[stat.ST_MTIME]: - self.__collection.pop(uri, None) - return self.__load(template.filename, uri) - else: - return template - - def put_string(self, uri, text): - self.__collection[uri] = Template(text, lookup=self, uri=uri, **self.template_args) - def put_template(self, uri, template): - self.__collection[uri] = template - diff --git a/planet/mako/parsetree.py b/planet/mako/parsetree.py deleted file mode 100644 index 426dc95c..00000000 --- a/planet/mako/parsetree.py +++ /dev/null @@ -1,293 +0,0 @@ -# parsetree.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""defines the parse tree components for Mako templates.""" - -from mako import exceptions, ast, util, filters -import re - -class Node(object): - """base class for a Node in the parse tree.""" - def __init__(self, source, lineno, pos, filename): - self.source = source - self.lineno = lineno - self.pos = pos - self.filename = filename - - exception_kwargs = property(lambda self:{'source':self.source, 'lineno':self.lineno, 'pos':self.pos, 'filename':self.filename}) - - def get_children(self): - return [] - def accept_visitor(self, visitor): - def traverse(node): - for n in node.get_children(): - n.accept_visitor(visitor) - method = getattr(visitor, "visit" + self.__class__.__name__, traverse) - method(self) - -class TemplateNode(Node): - """a 'container' node that stores the overall collection of nodes.""" - def __init__(self, filename): - super(TemplateNode, self).__init__('', 0, 0, filename) - self.nodes = [] - self.page_attributes = {} - def get_children(self): - return self.nodes - def __repr__(self): - return "TemplateNode(%s, %s)" % (repr(self.page_attributes), repr(self.nodes)) - -class ControlLine(Node): - """defines a control line, a line-oriented python line or end tag. - - % if foo: - (markup) - % endif - """ - def __init__(self, keyword, isend, text, **kwargs): - super(ControlLine, self).__init__(**kwargs) - self.text = text - self.keyword = keyword - self.isend = isend - self.is_primary = keyword in ['for','if', 'while', 'try'] - if self.isend: - self._declared_identifiers = [] - self._undeclared_identifiers = [] - else: - code = ast.PythonFragment(text, **self.exception_kwargs) - (self._declared_identifiers, self._undeclared_identifiers) = (code.declared_identifiers, code.undeclared_identifiers) - def declared_identifiers(self): - return self._declared_identifiers - def undeclared_identifiers(self): - return self._undeclared_identifiers - def is_ternary(self, keyword): - """return true if the given keyword is a ternary keyword for this ControlLine""" - return keyword in { - 'if':util.Set(['else', 'elif']), - 'try':util.Set(['except', 'finally']), - 'for':util.Set(['else']) - }.get(self.keyword, []) - def __repr__(self): - return "ControlLine(%s, %s, %s, %s)" % (repr(self.keyword), repr(self.text), repr(self.isend), repr((self.lineno, self.pos))) - -class Text(Node): - """defines plain text in the template.""" - def __init__(self, content, **kwargs): - super(Text, self).__init__(**kwargs) - self.content = content - def __repr__(self): - return "Text(%s, %s)" % (repr(self.content), repr((self.lineno, self.pos))) - -class Code(Node): - """defines a Python code block, either inline or module level. - - inline: - <% - x = 12 - %> - - module level: - <%! - import logger - %> - - """ - def __init__(self, text, ismodule, **kwargs): - super(Code, self).__init__(**kwargs) - self.text = text - self.ismodule = ismodule - self.code = ast.PythonCode(text, **self.exception_kwargs) - def declared_identifiers(self): - return self.code.declared_identifiers - def undeclared_identifiers(self): - return self.code.undeclared_identifiers - def __repr__(self): - return "Code(%s, %s, %s)" % (repr(self.text), repr(self.ismodule), repr((self.lineno, self.pos))) - -class Comment(Node): - """defines a comment line. - - # this is a comment - - """ - def __init__(self, text, **kwargs): - super(Comment, self).__init__(**kwargs) - self.text = text - def __repr__(self): - return "Comment(%s, %s)" % (repr(self.text), repr((self.lineno, self.pos))) - -class Expression(Node): - """defines an inline expression. - - ${x+y} - - """ - def __init__(self, text, escapes, **kwargs): - super(Expression, self).__init__(**kwargs) - self.text = text - self.escapes = escapes - self.escapes_code = ast.ArgumentList(escapes, **self.exception_kwargs) - self.code = ast.PythonCode(text, **self.exception_kwargs) - def declared_identifiers(self): - return [] - def undeclared_identifiers(self): - # TODO: make the "filter" shortcut list configurable at parse/gen time - return self.code.undeclared_identifiers.union(self.escapes_code.undeclared_identifiers.difference(util.Set(filters.DEFAULT_ESCAPES.keys()))) - def __repr__(self): - return "Expression(%s, %s, %s)" % (repr(self.text), repr(self.escapes_code.args), repr((self.lineno, self.pos))) - -class _TagMeta(type): - """metaclass to allow Tag to produce a subclass according to its keyword""" - _classmap = {} - def __init__(cls, clsname, bases, dict): - if cls.__keyword__ is not None: - cls._classmap[cls.__keyword__] = cls - super(_TagMeta, cls).__init__(clsname, bases, dict) - def __call__(cls, keyword, attributes, **kwargs): - try: - cls = _TagMeta._classmap[keyword] - except KeyError: - raise exceptions.CompileException("No such tag: '%s'" % keyword, source=kwargs['source'], lineno=kwargs['lineno'], pos=kwargs['pos'], filename=kwargs['filename']) - return type.__call__(cls, keyword, attributes, **kwargs) - -class Tag(Node): - """abstract base class for tags. - - <%sometag/> - - <%someothertag> - stuff - </%someothertag> - """ - __metaclass__ = _TagMeta - __keyword__ = None - def __init__(self, keyword, attributes, expressions, nonexpressions, required, **kwargs): - """construct a new Tag instance. - - this constructor not called directly, and is only called by subclasses. - - keyword - the tag keyword - - attributes - raw dictionary of attribute key/value pairs - - expressions - a util.Set of identifiers that are legal attributes, which can also contain embedded expressions - - nonexpressions - a util.Set of identifiers that are legal attributes, which cannot contain embedded expressions - - **kwargs - other arguments passed to the Node superclass (lineno, pos)""" - super(Tag, self).__init__(**kwargs) - self.keyword = keyword - self.attributes = attributes - self._parse_attributes(expressions, nonexpressions) - missing = [r for r in required if r not in self.parsed_attributes] - if len(missing): - raise exceptions.CompileException("Missing attribute(s): %s" % ",".join([repr(m) for m in missing]), **self.exception_kwargs) - self.parent = None - self.nodes = [] - def is_root(self): - return self.parent is None - def get_children(self): - return self.nodes - def _parse_attributes(self, expressions, nonexpressions): - undeclared_identifiers = util.Set() - self.parsed_attributes = {} - for key in self.attributes: - if key in expressions: - expr = [] - for x in re.split(r'(\${.+?})', self.attributes[key]): - m = re.match(r'^\${(.+?)}$', x) - if m: - code = ast.PythonCode(m.group(1), **self.exception_kwargs) - undeclared_identifiers = undeclared_identifiers.union(code.undeclared_identifiers) - expr.append(m.group(1)) - else: - if x: - expr.append(repr(x)) - self.parsed_attributes[key] = " + ".join(expr) - elif key in nonexpressions: - if re.search(r'${.+?}', self.attributes[key]): - raise exceptions.CompileException("Attibute '%s' in tag '%s' does not allow embedded expressions" %(key, self.keyword), **self.exception_kwargs) - self.parsed_attributes[key] = repr(self.attributes[key]) - else: - raise exceptions.CompileException("Invalid attribute for tag '%s': '%s'" %(self.keyword, key), **self.exception_kwargs) - self.expression_undeclared_identifiers = undeclared_identifiers - def declared_identifiers(self): - return [] - def undeclared_identifiers(self): - return self.expression_undeclared_identifiers - def __repr__(self): - return "%s(%s, %s, %s, %s)" % (self.__class__.__name__, repr(self.keyword), repr(self.attributes), repr((self.lineno, self.pos)), repr([repr(x) for x in self.nodes])) - -class IncludeTag(Tag): - __keyword__ = 'include' - def __init__(self, keyword, attributes, **kwargs): - super(IncludeTag, self).__init__(keyword, attributes, ('file', 'import', 'args'), (), ('file',), **kwargs) - self.page_args = ast.PythonCode("__DUMMY(%s)" % attributes.get('args', ''), **self.exception_kwargs) - def declared_identifiers(self): - return [] - def undeclared_identifiers(self): - identifiers = self.page_args.undeclared_identifiers.difference(util.Set(["__DUMMY"])) - return identifiers.union(super(IncludeTag, self).undeclared_identifiers()) - -class NamespaceTag(Tag): - __keyword__ = 'namespace' - def __init__(self, keyword, attributes, **kwargs): - super(NamespaceTag, self).__init__(keyword, attributes, (), ('name','inheritable','file','import','module'), (), **kwargs) - self.name = attributes.get('name', '__anon_%s' % hex(abs(id(self)))) - if not 'name' in attributes and not 'import' in attributes: - raise exceptions.CompileException("'name' and/or 'import' attributes are required for <%namespace>", **self.exception_kwargs) - def declared_identifiers(self): - return [] - -class TextTag(Tag): - __keyword__ = 'text' - def __init__(self, keyword, attributes, **kwargs): - super(TextTag, self).__init__(keyword, attributes, (), ('filter'), (), **kwargs) - self.filter_args = ast.ArgumentList(attributes.get('filter', ''), **self.exception_kwargs) - -class DefTag(Tag): - __keyword__ = 'def' - def __init__(self, keyword, attributes, **kwargs): - super(DefTag, self).__init__(keyword, attributes, ('buffered', 'cached', 'cache_key', 'cache_timeout', 'cache_type', 'cache_dir', 'cache_url'), ('name','filter'), ('name',), **kwargs) - name = attributes['name'] - if re.match(r'^[\w_]+$',name): - raise exceptions.CompileException("Missing parenthesis in %def", **self.exception_kwargs) - self.function_decl = ast.FunctionDecl("def " + name + ":pass", **self.exception_kwargs) - self.name = self.function_decl.funcname - self.filter_args = ast.ArgumentList(attributes.get('filter', ''), **self.exception_kwargs) - def declared_identifiers(self): - return self.function_decl.argnames - def undeclared_identifiers(self): - res = [] - for c in self.function_decl.defaults: - res += list(ast.PythonCode(c, **self.exception_kwargs).undeclared_identifiers) - return res + list(self.filter_args.undeclared_identifiers.difference(util.Set(filters.DEFAULT_ESCAPES.keys()))) - -class CallTag(Tag): - __keyword__ = 'call' - def __init__(self, keyword, attributes, **kwargs): - super(CallTag, self).__init__(keyword, attributes, ('args'), ('expr',), ('expr',), **kwargs) - self.code = ast.PythonCode(attributes['expr'], **self.exception_kwargs) - self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs) - def declared_identifiers(self): - return self.code.declared_identifiers.union(self.body_decl.argnames) - def undeclared_identifiers(self): - return self.code.undeclared_identifiers - -class InheritTag(Tag): - __keyword__ = 'inherit' - def __init__(self, keyword, attributes, **kwargs): - super(InheritTag, self).__init__(keyword, attributes, ('file',), (), ('file',), **kwargs) - -class PageTag(Tag): - __keyword__ = 'page' - def __init__(self, keyword, attributes, **kwargs): - super(PageTag, self).__init__(keyword, attributes, ('cached', 'cache_key', 'cache_timeout', 'cache_type', 'cache_dir', 'cache_url', 'args', 'expression_filter'), (), (), **kwargs) - self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs) - self.filter_args = ast.ArgumentList(attributes.get('expression_filter', ''), **self.exception_kwargs) - def declared_identifiers(self): - return self.body_decl.argnames - - diff --git a/planet/mako/pygen.py b/planet/mako/pygen.py deleted file mode 100644 index b73325fc..00000000 --- a/planet/mako/pygen.py +++ /dev/null @@ -1,267 +0,0 @@ -# pygen.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""utilities for generating and formatting literal Python code.""" - -import re, string -from StringIO import StringIO - -class PythonPrinter(object): - def __init__(self, stream): - # indentation counter - self.indent = 0 - - # a stack storing information about why we incremented - # the indentation counter, to help us determine if we - # should decrement it - self.indent_detail = [] - - # the string of whitespace multiplied by the indent - # counter to produce a line - self.indentstring = " " - - # the stream we are writing to - self.stream = stream - - # a list of lines that represents a buffered "block" of code, - # which can be later printed relative to an indent level - self.line_buffer = [] - - self.in_indent_lines = False - - self._reset_multi_line_flags() - - def write(self, text): - self.stream.write(text) - - def write_indented_block(self, block): - """print a line or lines of python which already contain indentation. - - The indentation of the total block of lines will be adjusted to that of - the current indent level.""" - self.in_indent_lines = False - for l in re.split(r'\r?\n', block): - self.line_buffer.append(l) - - def writelines(self, *lines): - """print a series of lines of python.""" - for line in lines: - self.writeline(line) - - def writeline(self, line): - """print a line of python, indenting it according to the current indent level. - - this also adjusts the indentation counter according to the content of the line.""" - - if not self.in_indent_lines: - self._flush_adjusted_lines() - self.in_indent_lines = True - - decreased_indent = False - - if (line is None or - re.match(r"^\s*#",line) or - re.match(r"^\s*$", line) - ): - hastext = False - else: - hastext = True - - is_comment = line and len(line) and line[0] == '#' - - # see if this line should decrease the indentation level - if (not decreased_indent and - not is_comment and - (not hastext or self._is_unindentor(line)) - ): - - if self.indent > 0: - self.indent -=1 - # if the indent_detail stack is empty, the user - # probably put extra closures - the resulting - # module wont compile. - if len(self.indent_detail) == 0: - raise "Too many whitespace closures" - self.indent_detail.pop() - - if line is None: - return - - # write the line - self.stream.write(self._indent_line(line) + "\n") - - # see if this line should increase the indentation level. - # note that a line can both decrase (before printing) and - # then increase (after printing) the indentation level. - - if re.search(r":[ \t]*(?:#.*)?$", line): - # increment indentation count, and also - # keep track of what the keyword was that indented us, - # if it is a python compound statement keyword - # where we might have to look for an "unindent" keyword - match = re.match(r"^\s*(if|try|elif|while|for)", line) - if match: - # its a "compound" keyword, so we will check for "unindentors" - indentor = match.group(1) - self.indent +=1 - self.indent_detail.append(indentor) - else: - indentor = None - # its not a "compound" keyword. but lets also - # test for valid Python keywords that might be indenting us, - # else assume its a non-indenting line - m2 = re.match(r"^\s*(def|class|else|elif|except|finally)", line) - if m2: - self.indent += 1 - self.indent_detail.append(indentor) - - def close(self): - """close this printer, flushing any remaining lines.""" - self._flush_adjusted_lines() - - def _is_unindentor(self, line): - """return true if the given line is an 'unindentor', relative to the last 'indent' event received.""" - - # no indentation detail has been pushed on; return False - if len(self.indent_detail) == 0: - return False - - indentor = self.indent_detail[-1] - - # the last indent keyword we grabbed is not a - # compound statement keyword; return False - if indentor is None: - return False - - # if the current line doesnt have one of the "unindentor" keywords, - # return False - match = re.match(r"^\s*(else|elif|except|finally).*\:", line) - if not match: - return False - - # whitespace matches up, we have a compound indentor, - # and this line has an unindentor, this - # is probably good enough - return True - - # should we decide that its not good enough, heres - # more stuff to check. - #keyword = match.group(1) - - # match the original indent keyword - #for crit in [ - # (r'if|elif', r'else|elif'), - # (r'try', r'except|finally|else'), - # (r'while|for', r'else'), - #]: - # if re.match(crit[0], indentor) and re.match(crit[1], keyword): return True - - #return False - - def _indent_line(self, line, stripspace = ''): - """indent the given line according to the current indent level. - - stripspace is a string of space that will be truncated from the start of the line - before indenting.""" - return re.sub(r"^%s" % stripspace, self.indentstring * self.indent, line) - - def _reset_multi_line_flags(self): - """reset the flags which would indicate we are in a backslashed or triple-quoted section.""" - (self.backslashed, self.triplequoted) = (False, False) - - def _in_multi_line(self, line): - """return true if the given line is part of a multi-line block, via backslash or triple-quote.""" - # we are only looking for explicitly joined lines here, - # not implicit ones (i.e. brackets, braces etc.). this is just - # to guard against the possibility of modifying the space inside - # of a literal multiline string with unfortunately placed whitespace - - current_state = (self.backslashed or self.triplequoted) - - if re.search(r"\\$", line): - self.backslashed = True - else: - self.backslashed = False - - triples = len(re.findall(r"\"\"\"|\'\'\'", line)) - if triples == 1 or triples % 2 != 0: - self.triplequoted = not self.triplequoted - - return current_state - - def _flush_adjusted_lines(self): - stripspace = None - self._reset_multi_line_flags() - - for entry in self.line_buffer: - if self._in_multi_line(entry): - self.stream.write(entry + "\n") - else: - entry = string.expandtabs(entry) - if stripspace is None and re.search(r"^[ \t]*[^# \t]", entry): - stripspace = re.match(r"^([ \t]*)", entry).group(1) - self.stream.write(self._indent_line(entry, stripspace) + "\n") - - self.line_buffer = [] - self._reset_multi_line_flags() - - -def adjust_whitespace(text): - """remove the left-whitespace margin of a block of Python code.""" - state = [False, False] - (backslashed, triplequoted) = (0, 1) - - def in_multi_line(line): - start_state = (state[backslashed] or state[triplequoted]) - - if re.search(r"\\$", line): - state[backslashed] = True - else: - state[backslashed] = False - - def match(reg, t): - m = re.match(reg, t) - if m: - return m, t[len(m.group(0)):] - else: - return None, t - - while line: - if state[triplequoted]: - m, line = match(r"%s" % state[triplequoted], line) - if m: - state[triplequoted] = False - else: - m, line = match(r".*?(?=%s|$)" % state[triplequoted], line) - else: - m, line = match(r'#', line) - if m: - return start_state - - m, line = match(r"\"\"\"|\'\'\'", line) - if m: - state[triplequoted] = m.group(0) - continue - - m, line = match(r".*?(?=\"\"\"|\'\'\'|#|$)", line) - - return start_state - - def _indent_line(line, stripspace = ''): - return re.sub(r"^%s" % stripspace, '', line) - - lines = [] - stripspace = None - - for line in re.split(r'\r?\n', text): - if in_multi_line(line): - lines.append(line) - else: - line = string.expandtabs(line) - if stripspace is None and re.search(r"^[ \t]*[^# \t]", line): - stripspace = re.match(r"^([ \t]*)", line).group(1) - lines.append(_indent_line(line, stripspace)) - return "\n".join(lines) diff --git a/planet/mako/pyparser.py b/planet/mako/pyparser.py deleted file mode 100644 index cc9c04e2..00000000 --- a/planet/mako/pyparser.py +++ /dev/null @@ -1,377 +0,0 @@ -# ast.py -# Copyright (C) Mako developers -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""Handles parsing of Python code. - -Parsing to AST is done via _ast on Python > 2.5, otherwise the compiler -module is used. -""" - -import sys -from StringIO import StringIO -from mako import exceptions, util - -# words that cannot be assigned to (notably smaller than the total keys in __builtins__) -reserved = util.Set(['True', 'False', 'None']) - -jython = sys.platform.startswith('java') - -try: - import _ast - util.restore__ast(_ast) - import _ast_util -except ImportError: - _ast = None - from compiler import parse as compiler_parse - from compiler import visitor - - -def parse(code, mode='exec', **exception_kwargs): - """Parse an expression into AST""" - try: - if _ast: - return _ast_util.parse(code, '<unknown>', mode) - else: - return compiler_parse(code, mode) - except Exception, e: - raise exceptions.SyntaxException("(%s) %s (%s)" % (e.__class__.__name__, str(e), repr(code[0:50])), **exception_kwargs) - - -if _ast: - class FindIdentifiers(_ast_util.NodeVisitor): - def __init__(self, listener, **exception_kwargs): - self.in_function = False - self.in_assign_targets = False - self.local_ident_stack = {} - 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 visit_ClassDef(self, node): - self._add_declared(node.name) - 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) - 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_FunctionDef(self, node): - 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" - saved = {} - inf = self.in_function - self.in_function = True - for arg in node.args.args: - if arg.id in self.local_ident_stack: - saved[arg.id] = True - else: - self.local_ident_stack[arg.id] = True - for n in node.body: - self.visit(n) - self.in_function = inf - for arg in node.args.args: - if arg.id not in saved: - del self.local_ident_stack[arg.id] - 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 jython: - if node.ctx == _ast.Store: - self._add_declared(node.id) - elif isinstance(node.ctx, _ast.Store): - self._add_declared(node.id) - if 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 for arg in node.args.args] - if node.args.vararg: - argnames.append(node.args.vararg) - if node.args.kwarg: - argnames.append(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 = {} - 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 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 visitFunction(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" - saved = {} - inf = self.in_function - self.in_function = True - for arg in node.argnames: - if arg in self.local_ident_stack: - saved[arg] = True - else: - self.local_ident_stack[arg] = True - for n in node.getChildNodes(): - self.visit(n, *args) - self.in_function = inf - for arg in node.argnames: - if arg not in saved: - del self.local_ident_stack[arg] - 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 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("'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) - def visit(self, expr): - visitor.walk(expr, self) #, walker=walker()) - - 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 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(object): - 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(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(")") - - class walker(visitor.ASTVisitor): - def dispatch(self, node, *args): - print "Node:", str(node) - #print "dir:", dir(node) - return visitor.ASTVisitor.dispatch(self, node, *args) diff --git a/planet/mako/runtime.py b/planet/mako/runtime.py deleted file mode 100644 index a82ffb40..00000000 --- a/planet/mako/runtime.py +++ /dev/null @@ -1,389 +0,0 @@ -# runtime.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""provides runtime services for templates, including Context, Namespace, and various helper functions.""" - -from mako import exceptions, util -import inspect, sys -import __builtin__ - -class Context(object): - """provides runtime namespace, output buffer, and various callstacks for templates.""" - def __init__(self, buffer, **data): - self._buffer_stack = [buffer] - self._data = dict(__builtin__.__dict__) - self._data.update(data) - self._kwargs = data.copy() - self._with_template = None - self.namespaces = {} - - # "capture" function which proxies to the generic "capture" function - self._data['capture'] = lambda x, *args, **kwargs: capture(self, x, *args, **kwargs) - - # "caller" stack used by def calls with content - self.caller_stack = self._data['caller'] = CallerStack() - - lookup = property(lambda self:self._with_template.lookup) - kwargs = property(lambda self:self._kwargs.copy()) - - def push_caller(self, caller): - self.caller_stack.append(caller) - - def pop_caller(self): - del self.caller_stack[-1] - - def keys(self): - return self._data.keys() - - def __getitem__(self, key): - return self._data[key] - - def _push_writer(self): - """push a capturing buffer onto this Context and return the new Writer function.""" - - buf = util.FastEncodingBuffer() - self._buffer_stack.append(buf) - return buf.write - - def _pop_buffer_and_writer(self): - """pop the most recent capturing buffer from this Context - and return the current writer after the pop. - - """ - - buf = self._buffer_stack.pop() - return buf, self._buffer_stack[-1].write - - def _push_buffer(self): - """push a capturing buffer onto this Context.""" - - self._push_writer() - - def _pop_buffer(self): - """pop the most recent capturing buffer from this Context.""" - - return self._buffer_stack.pop() - - def get(self, key, default=None): - return self._data.get(key, default) - - def write(self, string): - """write a string to this Context's underlying output buffer.""" - - self._buffer_stack[-1].write(string) - - def writer(self): - """return the current writer function""" - - return self._buffer_stack[-1].write - - def _copy(self): - c = Context.__new__(Context) - c._buffer_stack = self._buffer_stack - c._data = self._data.copy() - c._kwargs = self._kwargs - c._with_template = self._with_template - c.namespaces = self.namespaces - c.caller_stack = self.caller_stack - return c - def locals_(self, d): - """create a new Context with a copy of this Context's current state, updated with the given dictionary.""" - if len(d) == 0: - return self - c = self._copy() - c._data.update(d) - return c - def _clean_inheritance_tokens(self): - """create a new copy of this Context with tokens related to inheritance state removed.""" - c = self._copy() - x = c._data - x.pop('self', None) - x.pop('parent', None) - x.pop('next', None) - return c - -class CallerStack(list): - def __init__(self): - self.nextcaller = None - def __nonzero__(self): - return self._get_caller() and True or False - def _get_caller(self): - return self[-1] - def __getattr__(self, key): - return getattr(self._get_caller(), key) - def _push_frame(self): - self.append(self.nextcaller or None) - self.nextcaller = None - def _pop_frame(self): - self.nextcaller = self.pop() - - -class Undefined(object): - """represents an undefined value in a template.""" - def __str__(self): - raise NameError("Undefined") - def __nonzero__(self): - return False - -UNDEFINED = Undefined() - -class _NSAttr(object): - def __init__(self, parent): - self.__parent = parent - def __getattr__(self, key): - ns = self.__parent - while ns: - if hasattr(ns.module, key): - return getattr(ns.module, key) - else: - ns = ns.inherits - raise AttributeError(key) - -class Namespace(object): - """provides access to collections of rendering methods, which can be local, from other templates, or from imported modules""" - def __init__(self, name, context, module=None, template=None, templateuri=None, callables=None, inherits=None, populate_self=True, calling_uri=None): - self.name = name - if module is not None: - mod = __import__(module) - for token in module.split('.')[1:]: - mod = getattr(mod, token) - self._module = mod - else: - self._module = None - if templateuri is not None: - self.template = _lookup_template(context, templateuri, calling_uri) - self._templateuri = self.template.module._template_uri - else: - self.template = template - if self.template is not None: - self._templateuri = self.template.module._template_uri - self.context = context - self.inherits = inherits - if callables is not None: - self.callables = dict([(c.func_name, c) for c in callables]) - else: - self.callables = None - if populate_self and self.template is not None: - (lclcallable, lclcontext) = _populate_self_namespace(context, self.template, self_ns=self) - - module = property(lambda s:s._module or s.template.module) - filename = property(lambda s:s._module and s._module.__file__ or s.template.filename) - uri = property(lambda s:s.template.uri) - - def attr(self): - if not hasattr(self, '_attr'): - self._attr = _NSAttr(self) - return self._attr - attr = property(attr) - - def get_namespace(self, uri): - """return a namespace corresponding to the given template uri. - - if a relative uri, it is adjusted to that of the template of this namespace""" - key = (self, uri) - if self.context.namespaces.has_key(key): - return self.context.namespaces[key] - else: - ns = Namespace(uri, self.context._copy(), templateuri=uri, calling_uri=self._templateuri) - self.context.namespaces[key] = ns - return ns - - def get_template(self, uri): - return _lookup_template(self.context, uri, self._templateuri) - - def get_cached(self, key, **kwargs): - if self.template: - if self.template.cache_dir: - kwargs.setdefault('data_dir', self.template.cache_dir) - if self.template.cache_type: - kwargs.setdefault('type', self.template.cache_type) - if self.template.cache_url: - kwargs.setdefault('url', self.template.cache_url) - return self.cache.get(key, **kwargs) - - def cache(self): - return self.template.cache - cache = property(cache) - - def include_file(self, uri, **kwargs): - """include a file at the given uri""" - _include_file(self.context, uri, self._templateuri, **kwargs) - - def _populate(self, d, l): - for ident in l: - if ident == '*': - for (k, v) in self._get_star(): - d[k] = v - else: - d[ident] = getattr(self, ident) - - def _get_star(self): - if self.callables: - for key in self.callables: - yield (key, self.callables[key]) - if self.template: - def get(key): - callable_ = self.template.get_def(key).callable_ - return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) - for k in self.template.module._exports: - yield (k, get(k)) - if self._module: - def get(key): - callable_ = getattr(self._module, key) - return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) - for k in dir(self._module): - if k[0] != '_': - yield (k, get(k)) - - def __getattr__(self, key): - if self.callables and key in self.callables: - return self.callables[key] - - if self.template and self.template.has_def(key): - callable_ = self.template.get_def(key).callable_ - return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) - - if self._module and hasattr(self._module, key): - callable_ = getattr(self._module, key) - return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) - - if self.inherits is not None: - return getattr(self.inherits, key) - raise exceptions.RuntimeException("Namespace '%s' has no member '%s'" % (self.name, key)) - -def supports_caller(func): - """apply a caller_stack compatibility decorator to a plain Python function.""" - def wrap_stackframe(context, *args, **kwargs): - context.caller_stack._push_frame() - try: - return func(context, *args, **kwargs) - finally: - context.caller_stack._pop_frame() - return wrap_stackframe - -def capture(context, callable_, *args, **kwargs): - """execute the given template def, capturing the output into a buffer.""" - if not callable(callable_): - raise exceptions.RuntimeException("capture() function expects a callable as its argument (i.e. capture(func, *args, **kwargs))") - context._push_buffer() - try: - callable_(*args, **kwargs) - finally: - buf = context._pop_buffer() - return buf.getvalue() - -def _include_file(context, uri, calling_uri, **kwargs): - """locate the template from the given uri and include it in the current output.""" - template = _lookup_template(context, uri, calling_uri) - (callable_, ctx) = _populate_self_namespace(context._clean_inheritance_tokens(), template) - callable_(ctx, **_kwargs_for_callable(callable_, context._data, **kwargs)) - -def _inherit_from(context, uri, calling_uri): - """called by the _inherit method in template modules to set up the inheritance chain at the start - of a template's execution.""" - if uri is None: - return None - template = _lookup_template(context, uri, calling_uri) - self_ns = context['self'] - ih = self_ns - while ih.inherits is not None: - ih = ih.inherits - lclcontext = context.locals_({'next':ih}) - ih.inherits = Namespace("self:%s" % template.uri, lclcontext, template = template, populate_self=False) - context._data['parent'] = lclcontext._data['local'] = ih.inherits - callable_ = getattr(template.module, '_mako_inherit', None) - if callable_ is not None: - ret = callable_(template, lclcontext) - if ret: - return ret - - gen_ns = getattr(template.module, '_mako_generate_namespaces', None) - if gen_ns is not None: - gen_ns(context) - return (template.callable_, lclcontext) - -def _lookup_template(context, uri, relativeto): - lookup = context._with_template.lookup - if lookup is None: - raise exceptions.TemplateLookupException("Template '%s' has no TemplateLookup associated" % context._with_template.uri) - uri = lookup.adjust_uri(uri, relativeto) - try: - return lookup.get_template(uri) - except exceptions.TopLevelLookupException, e: - raise exceptions.TemplateLookupException(str(e)) - -def _populate_self_namespace(context, template, self_ns=None): - if self_ns is None: - self_ns = Namespace('self:%s' % template.uri, context, template=template, populate_self=False) - context._data['self'] = context._data['local'] = self_ns - if hasattr(template.module, '_mako_inherit'): - ret = template.module._mako_inherit(template, context) - if ret: - return ret - return (template.callable_, context) - -def _render(template, callable_, args, data, as_unicode=False): - """create a Context and return the string output of the given template and template callable.""" - - if as_unicode: - buf = util.FastEncodingBuffer(unicode=True) - elif template.output_encoding: - buf = util.FastEncodingBuffer(unicode=as_unicode, encoding=template.output_encoding, errors=template.encoding_errors) - else: - buf = util.StringIO() - context = Context(buf, **data) - context._with_template = template - _render_context(template, callable_, context, *args, **_kwargs_for_callable(callable_, data)) - return context._pop_buffer().getvalue() - -def _kwargs_for_callable(callable_, data, **kwargs): - argspec = inspect.getargspec(callable_) - namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None] - for arg in namedargs: - if arg != 'context' and arg in data and arg not in kwargs: - kwargs[arg] = data[arg] - return kwargs - -def _render_context(tmpl, callable_, context, *args, **kwargs): - import mako.template as template - # create polymorphic 'self' namespace for this template with possibly updated context - if not isinstance(tmpl, template.DefTemplate): - # if main render method, call from the base of the inheritance stack - (inherit, lclcontext) = _populate_self_namespace(context, tmpl) - _exec_template(inherit, lclcontext, args=args, kwargs=kwargs) - else: - # otherwise, call the actual rendering method specified - (inherit, lclcontext) = _populate_self_namespace(context, tmpl.parent) - _exec_template(callable_, context, args=args, kwargs=kwargs) - -def _exec_template(callable_, context, args=None, kwargs=None): - """execute a rendering callable given the callable, a Context, and optional explicit arguments - - the contextual Template will be located if it exists, and the error handling options specified - on that Template will be interpreted here. - """ - 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, e: - error = e - except: - e = sys.exc_info()[0] - error = e - if error: - if template.error_handler: - result = template.error_handler(context, error) - if not result: - raise error - else: - error_template = exceptions.html_error_template() - context._buffer_stack[:] = [util.FastEncodingBuffer(error_template.output_encoding, error_template.encoding_errors)] - context._with_template = error_template - error_template.render_context(context, error=error) - else: - callable_(context, *args, **kwargs) diff --git a/planet/mako/template.py b/planet/mako/template.py deleted file mode 100644 index d50eb8ad..00000000 --- a/planet/mako/template.py +++ /dev/null @@ -1,274 +0,0 @@ -# template.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""provides the Template class, a facade for parsing, generating and executing template strings, -as well as template runtime operations.""" - -from mako.lexer import Lexer -from mako import codegen -from mako import runtime, util, exceptions -import imp, os, re, shutil, stat, sys, tempfile, time, types, weakref - - -class Template(object): - """a compiled template""" - def __init__(self, text=None, filename=None, uri=None, format_exceptions=False, error_handler=None, - lookup=None, output_encoding=None, encoding_errors='strict', module_directory=None, cache_type=None, - cache_dir=None, cache_url=None, module_filename=None, input_encoding=None, disable_unicode=False, default_filters=None, - buffer_filters=[], imports=None, preprocessor=None): - """construct a new Template instance using either literal template text, or a previously loaded template module - - text - textual template source, or None if a module is to be provided - - uri - the uri of this template, or some identifying string. defaults to the - full filename given, or "memory:(hex id of this Template)" if no filename - - filename - filename of the source template, if any - - format_exceptions - catch exceptions and format them into an error display template - """ - - if uri: - self.module_id = re.sub(r'\W', "_", uri) - self.uri = uri - elif filename: - self.module_id = re.sub(r'\W', "_", filename) - self.uri = filename - else: - self.module_id = "memory:" + hex(id(self)) - self.uri = self.module_id - - self.input_encoding = input_encoding - self.output_encoding = output_encoding - self.encoding_errors = encoding_errors - self.disable_unicode = disable_unicode - if default_filters is None: - if self.disable_unicode: - self.default_filters = ['str'] - else: - self.default_filters = ['unicode'] - else: - self.default_filters = default_filters - self.buffer_filters = buffer_filters - - self.imports = imports - self.preprocessor = preprocessor - - # if plain text, compile code in memory only - if text is not None: - (code, module) = _compile_text(self, text, filename) - self._code = code - self._source = text - ModuleInfo(module, None, self, filename, code, text) - elif filename is not None: - # if template filename and a module directory, load - # a filesystem-based module file, generating if needed - if module_filename is not None: - path = module_filename - elif module_directory is not None: - u = self.uri - if u[0] == '/': - u = u[1:] - path = os.path.abspath(os.path.join(module_directory.replace('/', os.path.sep), u + ".py")) - else: - path = None - if path is not None: - util.verify_directory(os.path.dirname(path)) - filemtime = os.stat(filename)[stat.ST_MTIME] - if not os.path.exists(path) or os.stat(path)[stat.ST_MTIME] < filemtime: - _compile_module_file(self, file(filename).read(), filename, path) - module = imp.load_source(self.module_id, path, file(path)) - del sys.modules[self.module_id] - if module._magic_number != codegen.MAGIC_NUMBER: - _compile_module_file(self, file(filename).read(), filename, path) - module = imp.load_source(self.module_id, path, file(path)) - del sys.modules[self.module_id] - ModuleInfo(module, path, self, filename, None, None) - else: - # template filename and no module directory, compile code - # in memory - (code, module) = _compile_text(self, file(filename).read(), filename) - self._source = None - self._code = code - ModuleInfo(module, None, self, filename, code, None) - else: - raise exceptions.RuntimeException("Template requires text or filename") - - self.module = module - self.filename = filename - self.callable_ = self.module.render_body - self.format_exceptions = format_exceptions - self.error_handler = error_handler - self.lookup = lookup - self.cache_type = cache_type - self.cache_dir = cache_dir - self.cache_url = cache_url - - def source(self): - """return the template source code for this Template.""" - return _get_module_info_from_callable(self.callable_).source - source = property(source) - - def code(self): - """return the module source code for this Template""" - return _get_module_info_from_callable(self.callable_).code - code = property(code) - - def cache(self): - return self.module._template_cache - cache = property(cache) - - def render(self, *args, **data): - """render the output of this template as a string. - - if the template specifies an output encoding, the string will be encoded accordingly, else the output - is raw (raw output uses cStringIO and can't handle multibyte characters). - a Context object is created corresponding to the given data. Arguments that are explictly - declared by this template's internal rendering method are also pulled from the given *args, **data - members.""" - return runtime._render(self, self.callable_, args, data) - - def render_unicode(self, *args, **data): - """render the output of this template as a unicode object.""" - - return runtime._render(self, self.callable_, args, data, as_unicode=True) - - def render_context(self, context, *args, **kwargs): - """render this Template with the given context. - - the data is written to the context's buffer.""" - if getattr(context, '_with_template', None) is None: - context._with_template = self - runtime._render_context(self, self.callable_, context, *args, **kwargs) - - def has_def(self, name): - return hasattr(self.module, "render_%s" % name) - - def get_def(self, name): - """return a def of this template as an individual Template of its own.""" - return DefTemplate(self, getattr(self.module, "render_%s" % name)) - - -class ModuleTemplate(Template): - """A Template which is constructed given an existing Python module. - - e.g.:: - - t = Template("this is a template") - f = file("mymodule.py", "w") - f.write(t.code) - f.close() - - import mymodule - - t = ModuleTemplate(mymodule) - print t.render() - - """ - - def __init__(self, module, - module_filename=None, - template=None, template_filename=None, - module_source=None, template_source=None, - output_encoding=None, encoding_errors='strict', disable_unicode=False, format_exceptions=False, - error_handler=None, lookup=None, cache_type=None, cache_dir=None, cache_url=None - ): - self.module_id = re.sub(r'\W', "_", module._template_uri) - self.uri = module._template_uri - self.input_encoding = module._source_encoding - self.output_encoding = output_encoding - self.encoding_errors = encoding_errors - self.disable_unicode = disable_unicode - self.module = module - self.filename = template_filename - ModuleInfo(module, module_filename, self, template_filename, module_source, template_source) - - self.callable_ = self.module.render_body - self.format_exceptions = format_exceptions - self.error_handler = error_handler - self.lookup = lookup - self.cache_type = cache_type - self.cache_dir = cache_dir - self.cache_url = cache_url - -class DefTemplate(Template): - """a Template which represents a callable def in a parent template.""" - def __init__(self, parent, callable_): - self.parent = parent - self.callable_ = callable_ - self.output_encoding = parent.output_encoding - self.encoding_errors = parent.encoding_errors - self.format_exceptions = parent.format_exceptions - self.error_handler = parent.error_handler - self.lookup = parent.lookup - - def get_def(self, name): - return self.parent.get_def(name) - -class ModuleInfo(object): - """stores information about a module currently loaded into memory, - provides reverse lookups of template source, module source code based on - a module's identifier.""" - _modules = weakref.WeakValueDictionary() - - def __init__(self, module, module_filename, template, template_filename, module_source, template_source): - self.module = module - self.module_filename = module_filename - self.template_filename = template_filename - self.module_source = module_source - self.template_source = template_source - self._modules[module.__name__] = template._mmarker = self - if module_filename: - self._modules[module_filename] = self - def _get_code(self): - if self.module_source is not None: - return self.module_source - else: - return file(self.module_filename).read() - code = property(_get_code) - def _get_source(self): - if self.template_source is not None: - if self.module._source_encoding and not isinstance(self.template_source, unicode): - return self.template_source.decode(self.module._source_encoding) - else: - return self.template_source - else: - if self.module._source_encoding: - return file(self.template_filename).read().decode(self.module._source_encoding) - else: - return file(self.template_filename).read() - source = property(_get_source) - -def _compile_text(template, text, filename): - identifier = template.module_id - lexer = Lexer(text, filename, disable_unicode=template.disable_unicode, input_encoding=template.input_encoding, preprocessor=template.preprocessor) - node = lexer.parse() - source = codegen.compile(node, template.uri, filename, default_filters=template.default_filters, buffer_filters=template.buffer_filters, imports=template.imports, source_encoding=lexer.encoding, generate_unicode=not template.disable_unicode) - #print source - cid = identifier - if isinstance(cid, unicode): - cid = cid.encode() - module = types.ModuleType(cid) - code = compile(source, cid, 'exec') - exec code in module.__dict__, module.__dict__ - return (source, module) - -def _compile_module_file(template, text, filename, outputpath): - identifier = template.module_id - lexer = Lexer(text, filename, disable_unicode=template.disable_unicode, input_encoding=template.input_encoding, preprocessor=template.preprocessor) - node = lexer.parse() - source = codegen.compile(node, template.uri, filename, default_filters=template.default_filters, buffer_filters=template.buffer_filters, imports=template.imports, source_encoding=lexer.encoding, generate_unicode=not template.disable_unicode) - (dest, name) = tempfile.mkstemp() - os.write(dest, source) - os.close(dest) - shutil.move(name, outputpath) - -def _get_module_info_from_callable(callable_): - return _get_module_info(callable_.func_globals['__name__']) - -def _get_module_info(filename): - return ModuleInfo._modules[filename] - diff --git a/planet/mako/util.py b/planet/mako/util.py deleted file mode 100644 index 58a18a7e..00000000 --- a/planet/mako/util.py +++ /dev/null @@ -1,256 +0,0 @@ -# util.py -# Copyright (C) 2006, 2007, 2008 Michael Bayer [email protected] -# -# This module is part of Mako and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -import sys -try: - Set = set -except: - import sets - Set = sets.Set - -try: - from cStringIO import StringIO -except: - from StringIO import StringIO - -import codecs, re, weakref, os, time - -try: - import threading - import thread -except ImportError: - import dummy_threading as threading - import dummy_thread as thread - -if sys.platform.startswith('win') or sys.platform.startswith('java'): - time_func = time.clock -else: - time_func = time.time - -def verify_directory(dir): - """create and/or verify a filesystem directory.""" - - tries = 0 - - while not os.path.exists(dir): - try: - tries += 1 - os.makedirs(dir, 0750) - except: - if tries > 5: - raise - -class SetLikeDict(dict): - """a dictionary that has some setlike methods on it""" - def union(self, other): - """produce a 'union' of this dict and another (at the key level). - - values in the second dict take precedence over that of the first""" - x = SetLikeDict(**self) - x.update(other) - return x - -class FastEncodingBuffer(object): - """a very rudimentary buffer that is faster than StringIO, but doesnt crash on unicode data like cStringIO.""" - - def __init__(self, encoding=None, errors='strict', unicode=False): - self.data = [] - self.encoding = encoding - if unicode: - self.delim = u'' - else: - self.delim = '' - self.unicode = unicode - self.errors = errors - self.write = self.data.append - - def getvalue(self): - if self.encoding: - return self.delim.join(self.data).encode(self.encoding, self.errors) - else: - return self.delim.join(self.data) - -class LRUCache(dict): - """A dictionary-like object that stores a limited number of items, discarding - lesser used items periodically. - - this is a rewrite of LRUCache from Myghty to use a periodic timestamp-based - paradigm so that synchronization is not really needed. the size management - is inexact. - """ - - class _Item(object): - def __init__(self, key, value): - self.key = key - self.value = value - self.timestamp = time_func() - def __repr__(self): - return repr(self.value) - - def __init__(self, capacity, threshold=.5): - self.capacity = capacity - self.threshold = threshold - - def __getitem__(self, key): - item = dict.__getitem__(self, key) - item.timestamp = time_func() - return item.value - - def values(self): - return [i.value for i in dict.values(self)] - - def setdefault(self, key, value): - if key in self: - return self[key] - else: - self[key] = value - return value - - def __setitem__(self, key, value): - item = dict.get(self, key) - if item is None: - item = self._Item(key, value) - dict.__setitem__(self, key, item) - else: - item.value = value - self._manage_size() - - def _manage_size(self): - while len(self) > self.capacity + self.capacity * self.threshold: - bytime = dict.values(self) - bytime.sort(lambda a, b: cmp(b.timestamp, a.timestamp)) - for item in bytime[self.capacity:]: - try: - del self[item.key] - except KeyError: - # if we couldnt find a key, most likely some other thread broke in - # on us. loop around and try again - break - -# Regexp to match python magic encoding line -_PYTHON_MAGIC_COMMENT_re = re.compile( - r'[ \t\f]* \# .* coding[=:][ \t]*([-\w.]+)', - re.VERBOSE) - -def parse_encoding(fp): - """Deduce the encoding of a source file from magic comment. - - It does this in the same way as the `Python interpreter`__ - - .. __: http://docs.python.org/ref/encodings.html - - The ``fp`` argument should be a seekable file object. - """ - pos = fp.tell() - fp.seek(0) - try: - line1 = fp.readline() - has_bom = line1.startswith(codecs.BOM_UTF8) - if has_bom: - line1 = line1[len(codecs.BOM_UTF8):] - - m = _PYTHON_MAGIC_COMMENT_re.match(line1) - if not m: - try: - import parser - parser.suite(line1) - except (ImportError, SyntaxError): - # Either it's a real syntax error, in which case the source - # is not valid python source, or line2 is a continuation of - # line1, in which case we don't want to scan line2 for a magic - # comment. - pass - else: - line2 = fp.readline() - m = _PYTHON_MAGIC_COMMENT_re.match(line2) - - if has_bom: - if m: - raise SyntaxError, \ - "python refuses to compile code with both a UTF8" \ - " byte-order-mark and a magic encoding comment" - return 'utf_8' - elif m: - return m.group(1) - else: - return None - finally: - fp.seek(pos) - -def restore__ast(_ast): - """Attempt to restore the required classes to the _ast module if it - appears to be missing them - """ - if hasattr(_ast, 'AST'): - return - _ast.PyCF_ONLY_AST = 2 << 9 - m = compile("""\ -def foo(): pass -class Bar(object): pass -if False: pass -baz = 'mako' -1 + 2 - 3 * 4 / 5 -6 // 7 % 8 << 9 >> 10 -11 & 12 ^ 13 | 14 -15 and 16 or 17 --baz + (not +18) - ~17 -baz and 'foo' or 'bar' -(mako is baz == baz) is not baz != mako -mako > baz < mako >= baz <= mako -mako in baz not in mako""", '<unknown>', 'exec', _ast.PyCF_ONLY_AST) - _ast.Module = type(m) - - for cls in _ast.Module.__mro__: - if cls.__name__ == 'mod': - _ast.mod = cls - elif cls.__name__ == 'AST': - _ast.AST = cls - - _ast.FunctionDef = type(m.body[0]) - _ast.ClassDef = type(m.body[1]) - _ast.If = type(m.body[2]) - - _ast.Name = type(m.body[3].targets[0]) - _ast.Store = type(m.body[3].targets[0].ctx) - _ast.Str = type(m.body[3].value) - - _ast.Sub = type(m.body[4].value.op) - _ast.Add = type(m.body[4].value.left.op) - _ast.Div = type(m.body[4].value.right.op) - _ast.Mult = type(m.body[4].value.right.left.op) - - _ast.RShift = type(m.body[5].value.op) - _ast.LShift = type(m.body[5].value.left.op) - _ast.Mod = type(m.body[5].value.left.left.op) - _ast.FloorDiv = type(m.body[5].value.left.left.left.op) - - _ast.BitOr = type(m.body[6].value.op) - _ast.BitXor = type(m.body[6].value.left.op) - _ast.BitAnd = type(m.body[6].value.left.left.op) - - _ast.Or = type(m.body[7].value.op) - _ast.And = type(m.body[7].value.values[0].op) - - _ast.Invert = type(m.body[8].value.right.op) - _ast.Not = type(m.body[8].value.left.right.op) - _ast.UAdd = type(m.body[8].value.left.right.operand.op) - _ast.USub = type(m.body[8].value.left.left.op) - - _ast.Or = type(m.body[9].value.op) - _ast.And = type(m.body[9].value.values[0].op) - - _ast.IsNot = type(m.body[10].value.ops[0]) - _ast.NotEq = type(m.body[10].value.ops[1]) - _ast.Is = type(m.body[10].value.left.ops[0]) - _ast.Eq = type(m.body[10].value.left.ops[1]) - - _ast.Gt = type(m.body[11].value.ops[0]) - _ast.Lt = type(m.body[11].value.ops[1]) - _ast.GtE = type(m.body[11].value.ops[2]) - _ast.LtE = type(m.body[11].value.ops[3]) - - _ast.In = type(m.body[12].value.ops[0]) - _ast.NotIn = type(m.body[12].value.ops[1]) diff --git a/planet/template/base.tmpl b/planet/template/base.tmpl index 53d82768..dbaff931 100644 --- a/planet/template/base.tmpl +++ b/planet/template/base.tmpl @@ -16,7 +16,7 @@ <div class="cb"></div> </div> <!-- planetHeader --> <div id="planetMain"> -${self.body()} +{% block content %} {% endblock %} </div> <!-- planetMain --> </div> <!-- planetWrap --> diff --git a/planet/template/index.tmpl b/planet/template/index.tmpl index a9fbfb11..3d80283e 100644 --- a/planet/template/index.tmpl +++ b/planet/template/index.tmpl @@ -1,10 +1,11 @@ -<%inherit file="base.tmpl" /> +{% extends "base.tmpl" %} +{% block content %} <div id="planetRight"> <div class="planetRightTitle">Subscriptions</div> <ul> -% for feed in feeds: - <li><a href="${feed.feedurl}"><img src="img/feed-icon-14x14.png" alt="RSS"/></a> <%call expr="coalescelink(feed.name, feed.blogurl)"></%call></li> -% endfor +{% for feed in feeds %} + <li><a href="{{feed.feedurl}}"><img src="img/feed-icon-14x14.png" alt="RSS"/></a> {%if feed.blogurl %}<a href="{{feed.blogurl}}">{{feed.name}}</a>{%else%}{{feed.name}}{%endif%}</li> +{% endfor %} </ul> <div class="planetRightTitle">Feeds</div> <ul> @@ -14,23 +15,17 @@ <p><a href="policy.html">Policy</a> for being listed on Planet PostgreSQL</p> </div> <!-- planetRight --> <div id="planetLeft"> -% for post in posts: +{% for post in posts %} <div class="planetPost"> - <div class="planetPostTitle"><a href="${post.link}">${post.title}</a></div> + <div class="planetPostTitle"><a href="{{post.link}}">{{post.title}}</a></div> <div class="planetPostAuthor"> <div class="ppa_top"> </div> - <p>Posted by <%call expr="coalescelink(post.author, post.blogurl)"></%call> on <span class="date">${post.datetime}</span></p> + <p>Posted by {%if post.blogurl%}<a href="{{post.blogurl}}">{{post.author}}</a>{%else%}{{post.author}}{%endif%} on <span class="date">{{post.datetime}}</span></p> <div class="ppa_bottom"> </div> </div> - <div class="planetPostContent">${post.contents}</div> + <div class="planetPostContent">{{post.contents|safe}}</div> <div class="cl"></div> </div> -% endfor +{% endfor %} </div> <!-- planetLeft --> -<%def name="coalescelink(txt, link)"> -%if link=='': -${txt} -%else: -<a href="${link}">${txt}</a> -%endif -</%def> +{% endblock %} diff --git a/planet/template/policy.tmpl b/planet/template/policy.tmpl index 5329deaf..17c9dd38 100644 --- a/planet/template/policy.tmpl +++ b/planet/template/policy.tmpl @@ -1,4 +1,5 @@ -<%inherit file="base.tmpl" /> +{%extends "base.tmpl"%} +{%block content%} <h1>Planet PostgreSQL policy</h1> <p> The following simple rules cover the blogs being listed on Planet PostgreSQL: @@ -16,3 +17,4 @@ The following simple rules cover the blogs being listed on Planet PostgreSQL: <li>Publishing of advertising in the syndicated part of your blog is not permitted</li> </ul> <p>Violating these rules will cause your blog to be removed from Planet PostgreSQL.</p> +{%endblock%} |