assert frame is None, 'no root frame allowed'
self.writeline('from jinja2.runtime import *')
self.writeline('filename = %r' % self.filename)
- self.writeline('template_context = TemplateContext(global_context, '
- 'filename)')
+
+ # find all blocks
+ for block in node.find_all(nodes.Block):
+ if block.name in self.blocks:
+ raise TemplateAssertionError('block %r defined twice' %
+ block.name, block.lineno,
+ self.filename)
+ self.blocks[block.name] = block
# generate the root render function.
- self.writeline('def root(context=template_context):', extra=1)
+ self.writeline('def root(context):', extra=1)
self.indent()
self.writeline('parent_root = None')
self.outdent()
block_frame = Frame()
block_frame.inspect(block.body)
block_frame.block = name
+ print block_frame.identifiers.__dict__
self.writeline('def block_%s(context):' % name, block, 1)
self.pull_locals(block_frame)
self.blockvisit(block.body, block_frame, True)
def visit_Block(self, node, frame):
"""Call a block and register it for the template."""
- if node.name in self.blocks:
- raise TemplateAssertionError("the block '%s' was already defined" %
- node.name, node.lineno,
- self.filename)
- self.blocks[node.name] = node
self.writeline('for event in block_%s(context):' % node.name)
self.indent()
self.writeline('yield event')
def visit_Output(self, node, frame):
self.newline(node)
+ if self.environment.finalize is unicode:
+ finalizer = 'unicode'
+ else:
+ finalizer = 'context.finalize'
+
# try to evaluate as many chunks as possible into a static
# string at compile time.
body = []
self.writeline('yield %s' % repr(u''.join(item)))
else:
self.newline(item)
- self.write('yield unicode(')
+ self.write('yield %s(' % finalizer)
self.visit(item, frame)
self.write(')')
for idx, argument in enumerate(arguments):
if idx:
self.write(', ')
+ if finalizer != 'unicode':
+ self.write('(')
self.visit(argument, frame)
+ if finalizer != 'unicode':
+ self.write(')')
self.write(idx == 0 and ',)' or ')')
def visit_Assign(self, node, frame):
self.visit(item, frame)
self.write(idx == 0 and ',)' or ')')
+ def visit_List(self, node, frame):
+ self.write('[')
+ for idx, item in enumerate(node.items):
+ if idx:
+ self.write(', ')
+ self.visit(item, frame)
+ self.write(']')
+
+ def visit_Dict(self, node, frame):
+ self.write('{')
+ for idx, item in enumerate(node.items):
+ if idx:
+ self.write(', ')
+ self.visit(item.key, frame)
+ self.write(': ')
+ self.visit(item.value, frame)
+ self.write('}')
+
def binop(operator):
def visitor(self, node, frame):
self.write('(')
"""
from jinja2.lexer import Lexer
from jinja2.parser import Parser
-from jinja2.runtime import Undefined
from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
class Environment(object):
- """
- The Jinja environment.
+ """The Jinja environment.
The core component of Jinja is the `Environment`. It contains
important shared variables like configuration, filters, tests,
comment_end_string='#}',
trim_blocks=False,
template_charset='utf-8'):
- """
- Here the possible initialization parameters:
+ """Here the possible initialization parameters:
========================= ============================================
`block_start_string` the string marking the begin of a block.
self.tests = DEFAULT_TESTS.copy()
self.globals = DEFAULT_NAMESPACE.copy()
- # the factory that creates the undefined object
- self.undefined_factory = Undefined
+ # if no finalize function/method exists we default to unicode. The
+ # compiler check if the finalize attribute *is* unicode, if yes no
+ # finalizaion is written where it can be avoided.
+ if not hasattr(self, 'finalize'):
+ self.finalize = unicode
# create lexer
self.lexer = Lexer(self)
def parse(self, source, filename=None):
- """
- Parse the sourcecode and return the abstract syntax tree. This tree
- of nodes is used by the `translators`_ to convert the template into
+ """Parse the sourcecode and return the abstract syntax tree. This tree
+ of nodes is used by the compiler to convert the template into
executable source- or bytecode.
-
- .. _translators: translators.txt
"""
parser = Parser(self, source, filename)
return parser.parse()
def lex(self, source, filename=None):
- """
- Lex the given sourcecode and return a generator that yields tokens.
+ """Lex the given sourcecode and return a generator that yields tokens.
The stream returned is not usable for Jinja but can be used if
Jinja templates should be processed by other tools (for example
syntax highlighting etc)
except ImportError:
itemgetter = lambda a: lambda b: b[a]
from urllib import urlencode, quote
-from jinja.utils import escape
+from jinja2.utils import escape
_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
def contextfilter(f):
- """
- Decorator for marking context dependent filters. The current context
+ """Decorator for marking context dependent filters. The current context
argument will be passed as first argument.
"""
f.contextfilter = True
def do_upper(s):
- """
- Convert a value to uppercase.
- """
- return s.upper()
+ """Convert a value to uppercase."""
+ return unicode(s).upper()
def do_lower(s):
- """
- Convert a value to lowercase.
- """
- return s.lower()
+ """Convert a value to lowercase."""
+ return unicode(s).lower()
def do_escape(s, attribute=False):
This method will have no effect it the value is already escaped.
"""
- # XXX: Does this still exists?
- #if isinstance(s, TemplateData):
- # return s
- if hasattr(s, '__html__'):
- return s.__html__()
return escape(unicode(s), attribute)
class ContextStack(object):
"""Simple compile time context implementation."""
+ undefined = object()
def __init__(self, initial=None):
self.stack = [{}]
except KeyError:
return default
+ def undef(self, name):
+ if name in self:
+ self[name] = self.undefined
+
+ def __contains__(self, key):
+ try:
+ self[key]
+ except KeyError:
+ return False
+ return True
+
def __getitem__(self, key):
for level in reversed(self.stack):
if key in level:
- return level[key]
+ rv = level[key]
+ if rv is self.undefined:
+ raise KeyError(key)
+ return rv
raise KeyError(key)
def __setitem__(self, key, value):
def visit_Block(self, node, context):
return self.generic_visit(node, context.blank())
+ def visit_Macro(self, node, context):
+ context.push()
+ try:
+ return self.generic_visit(node, context)
+ finally:
+ context.pop()
+
def visit_For(self, node, context):
"""Loop unrolling for iterable constant values."""
try:
# we only unroll them if they have a length and are iterable
iter(iterable)
len(iterable)
+ # we also don't want unrolling if macros are defined in it
+ if node.find(nodes.Macro) is not None:
+ raise TypeError()
except (nodes.Impossible, TypeError):
return self.generic_visit(node, context)
def visit_Name(self, node, context):
if node.ctx != 'load':
+ # something overwrote the variable, we can no longer use
+ # the constant from the context
+ context.undef(node.name)
return node
try:
return nodes.Const.from_untrusted(context[node.name],
if token_type in _statement_keywords:
return getattr(self, 'parse_' + token_type)()
elif token_type is 'call':
- self.stream.next()
return self.parse_call_block()
lineno = self.stream.current
expr = self.parse_tuple()
def parse_call_block(self):
node = nodes.CallBlock(lineno=self.stream.expect('call').lineno)
- node.call = self.parse_call()
+ node.call = self.parse_expression()
+ if not isinstance(node.call, nodes.Call):
+ raise TemplateSyntaxError('expected call', node.lineno,
+ self.filename)
node.body = self.parse_statements(('endcall',), drop_needle=True)
return node
return Undefined(obj, argument)
+class TemplateData(unicode):
+ """Marks data as "coming from the template". This is used to let the
+ system know that this data is already processed if a finalization is
+ used."""
+
+ def __html__(self):
+ return self
+
+
class TemplateContext(dict):
- """
- Holds the variables of the local template or of the global one. It's
+ """Holds the variables of the local template or of the global one. It's
not save to use this class outside of the compiled code. For example
update and other methods will not work as they seem (they don't update
the exported variables for example).
arguments['l_' + name] = value
if self.catch_all:
arguments['l_arguments'] = kwargs
- return u''.join(self.func(**arguments))
+ return TemplateData(u''.join(self.func(**arguments)))
class Undefined(object):
:copyright: 2008 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
+
+
+def escape(obj, attribute=False):
+ """HTML escape an object."""
+ if hasattr(obj, '__html__'):
+ return obj.__html__()
+ s = unicode(obj) \
+ .replace('&', '&') \
+ .replace('>', '>') \
+ .replace('<', '<')
+ if attribute:
+ s = s.replace('"', '"')
+ return s
env = Environment()
ast = env.parse("""
-{% block body %}
- {% b = 23 %}
- {% macro foo(a) %}[{{ a }}|{{ b }}|{{ c }}]{% endmacro %}
- {% for item in seq %}
- {{ foo(item) }}
- {%- endfor %}
+hallo
+{% block root %}
+ inhalt
+ {% x = 3 %}
+ {% block inner %}
+ {% x = x + 2 %}
+ {{ x }}
+ {% endblock %}
+ {{ x }}
{% endblock %}
+ende
""")
-print ast
-print
source = generate(ast, env, "foo.html")
print source
-print
-
-# execute the template
-code = compile(source, 'jinja://foo.html', 'exec')
-context = {'seq': range(5), 'c': 'foobar'}
-namespace = {'global_context': context}
-exec code in namespace
-for event in namespace['root'](context):
- sys.stdout.write(event)