a clean restart
authorArmin Ronacher <armin.ronacher@active-4.com>
Mon, 31 Mar 2008 12:18:49 +0000 (14:18 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Mon, 31 Mar 2008 12:18:49 +0000 (14:18 +0200)
--HG--
branch : trunk
rename : jinja/__init__.py => jinja2/__init__.py
rename : jinja/_debugger.c => jinja2/_debugger.c
rename : jinja/_native.py => jinja2/_native.py
rename : jinja/_speedups.c => jinja2/_speedups.c
rename : jinja/constants.py => jinja2/constants.py
rename : jinja/contrib/__init__.py => jinja2/contrib/__init__.py
rename : jinja/contrib/_djangosupport.py => jinja2/contrib/_djangosupport.py
rename : jinja/contrib/djangosupport.py => jinja2/contrib/djangosupport.py
rename : jinja/datastructure.py => jinja2/datastructure.py
rename : jinja/defaults.py => jinja2/defaults.py
rename : jinja/environment.py => jinja2/environment.py
rename : jinja/exceptions.py => jinja2/exceptions.py
rename : jinja/filters.py => jinja2/filters.py
rename : jinja/lexer.py => jinja2/lexer.py
rename : jinja/loaders.py => jinja2/loaders.py
rename : jinja/nodes.py => jinja2/nodes.py
rename : jinja/parser.py => jinja2/parser.py
rename : jinja/tests.py => jinja2/tests.py
rename : jinja/translators/__init__.py => jinja2/translators/__init__.py
rename : jinja/translators/python.py => jinja2/translators/python.py
rename : jinja/utils.py => jinja2/utils.py

30 files changed:
jinja/datastructure.py [deleted file]
jinja/debugger.py [deleted file]
jinja/environment.py [deleted file]
jinja/exceptions.py [deleted file]
jinja/loaders.py [deleted file]
jinja/nodes.py [deleted file]
jinja/parser.py [deleted file]
jinja/plugin.py [deleted file]
jinja/utils.py [deleted file]
jinja2/__init__.py [moved from jinja/__init__.py with 71% similarity]
jinja2/_debugger.c [moved from jinja/_debugger.c with 100% similarity]
jinja2/_native.py [moved from jinja/_native.py with 100% similarity]
jinja2/_speedups.c [moved from jinja/_speedups.c with 100% similarity]
jinja2/constants.py [moved from jinja/constants.py with 100% similarity]
jinja2/contrib/__init__.py [moved from jinja/contrib/__init__.py with 100% similarity]
jinja2/contrib/_djangosupport.py [moved from jinja/contrib/_djangosupport.py with 100% similarity]
jinja2/contrib/djangosupport.py [moved from jinja/contrib/djangosupport.py with 100% similarity]
jinja2/datastructure.py [new file with mode: 0644]
jinja2/defaults.py [moved from jinja/defaults.py with 86% similarity]
jinja2/environment.py [new file with mode: 0644]
jinja2/exceptions.py [new file with mode: 0644]
jinja2/filters.py [moved from jinja/filters.py with 99% similarity]
jinja2/lexer.py [moved from jinja/lexer.py with 94% similarity]
jinja2/loaders.py [new file with mode: 0644]
jinja2/nodes.py [new file with mode: 0644]
jinja2/parser.py [new file with mode: 0644]
jinja2/tests.py [moved from jinja/tests.py with 100% similarity]
jinja2/translators/__init__.py [moved from jinja/translators/__init__.py with 100% similarity]
jinja2/translators/python.py [moved from jinja/translators/python.py with 100% similarity]
jinja2/utils.py [new file with mode: 0644]

diff --git a/jinja/datastructure.py b/jinja/datastructure.py
deleted file mode 100644 (file)
index eb213d4..0000000
+++ /dev/null
@@ -1,708 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    jinja.datastructure
-    ~~~~~~~~~~~~~~~~~~~
-
-    Module that helds several data types used in the template engine.
-
-    :copyright: 2007 by Armin Ronacher.
-    :license: BSD, see LICENSE for more details.
-"""
-from jinja.exceptions import TemplateSyntaxError, TemplateRuntimeError
-
-_missing = object()
-
-
-def contextcallable(f):
-    """
-    Mark a function context callable.
-    """
-    f.jinja_context_callable = True
-    return f
-
-
-def unsafe(f):
-    """
-    Mark function as unsafe.
-    """
-    f.jinja_unsafe_call = True
-    return f
-
-
-def make_undefined(implementation):
-    """
-    Creates an undefined singleton based on a given implementation.
-    It performs some tests that make sure the undefined type implements
-    everything it should.
-    """
-    self = object.__new__(implementation)
-    self.__reduce__()
-    return self
-
-
-class AbstractUndefinedType(object):
-    """
-    Base class for any undefined type.
-    """
-    __slots__ = ()
-
-    def __init__(self):
-        raise TypeError('cannot create %r instances' %
-                        self.__class__.__name__)
-
-    def __setattr__(self, name, value):
-        raise AttributeError('%r object has no attribute %r' % (
-            self.__class__.__name__,
-            name
-        ))
-
-    def __eq__(self, other):
-        return self is other
-
-    def __ne__(self, other):
-        return self is not other
-
-    def __copy__(self):
-        return self
-    __deepcopy__ = __copy__
-
-    def __repr__(self):
-        return 'Undefined'
-
-    def __reduce__(self):
-        raise TypeError('undefined objects have to provide a __reduce__')
-
-
-class SilentUndefinedType(AbstractUndefinedType):
-    """
-    An object that does not exist.
-    """
-    __slots__ = ()
-
-    def __add__(self, other):
-        """Any operator returns the operand."""
-        return other
-    __sub__ = __mul__ = __div__ = __rsub__ = __rmul__ = __div__ = __mod__ =\
-    __radd__ = __rmod__ = __add__
-
-    def __getitem__(self, arg):
-        """Getting any item returns `Undefined`"""
-        return self
-
-    def __iter__(self):
-        """Iterating over `Undefined` returns an empty iterator."""
-        if False:
-            yield None
-
-    def __getattr__(self, arg):
-        """Getting any attribute returns `Undefined`"""
-        return self
-
-    def __nonzero__(self):
-        """`Undefined` is considered boolean `False`"""
-        return False
-
-    def __len__(self):
-        """`Undefined` is an empty sequence"""
-        return 0
-
-    def __str__(self):
-        """The string representation is empty."""
-        return ''
-
-    def __unicode__(self):
-        """The unicode representation is empty."""
-        return u''
-
-    def __int__(self):
-        """Converting `Undefined` to an integer ends up in ``0``"""
-        return 0
-
-    def __float__(self):
-        """Converting `Undefined` to an float ends up in ``0.0``"""
-        return 0.0
-
-    def __call__(self, *args, **kwargs):
-        """Calling `Undefined` returns `Undefined`"""
-        return self
-
-    def __reduce__(self):
-        """Helper for pickle."""
-        return 'SilentUndefined'
-
-
-class ComplainingUndefinedType(AbstractUndefinedType):
-    """
-    An object that does not exist.
-    """
-    __slots__ = ()
-
-    def __len__(self):
-        """Getting the length raises error."""
-        raise TemplateRuntimeError('Operated on undefined object')
-
-    def __iter__(self):
-        """Iterating over `Undefined` raises an error."""
-        raise TemplateRuntimeError('Iterated over undefined object')
-
-    def __nonzero__(self):
-        """`Undefined` is considered boolean `False`"""
-        return False
-
-    def __str__(self):
-        """The string representation raises an error."""
-        raise TemplateRuntimeError('Undefined object rendered')
-
-    def __unicode__(self):
-        """The unicode representation raises an error."""
-        self.__str__()
-
-    def __call__(self, *args, **kwargs):
-        """Calling `Undefined` returns `Undefined`"""
-        raise TemplateRuntimeError('Undefined object called')
-
-    def __reduce__(self):
-        """Helper for pickle."""
-        return 'ComplainingUndefined'
-
-
-#: the singleton instances for the undefined objects
-SilentUndefined = make_undefined(SilentUndefinedType)
-ComplainingUndefined = make_undefined(ComplainingUndefinedType)
-
-#: jinja 1.0 compatibility
-Undefined = SilentUndefined
-UndefinedType = SilentUndefinedType
-
-
-class FakeTranslator(object):
-    """
-    Default null translator.
-    """
-
-    def gettext(self, s):
-        """
-        Translate a singular string.
-        """
-        return s
-
-    def ngettext(self, s, p, n):
-        """
-        Translate a plural string.
-        """
-        if n == 1:
-            return s
-        return p
-
-
-class Deferred(object):
-    """
-    Object marking an deferred value. Deferred objects are
-    objects that are called first access in the context.
-    """
-
-    def __init__(self, factory):
-        self.factory = factory
-
-    def __call__(self, context, name):
-        return self.factory(context.environment, context, name)
-
-
-class Markup(unicode):
-    """
-    Compatibility for Pylons and probably some other frameworks.
-
-    It's only used in Jinja environments with `auto_escape` set
-    to true.
-    """
-
-    def __html__(self):
-        return unicode(self)
-
-
-class TemplateData(Markup):
-    """
-    Subclass of unicode to mark objects that are coming from the
-    template. The autoescape filter can use that.
-    """
-
-
-# import these here because those modules import Deferred and Undefined
-# from this module.
-try:
-    # try to use the c implementation of the base context if available
-    from jinja._speedups import BaseContext
-except ImportError:
-    # if there is no c implementation we go with a native python one
-    from jinja._native import BaseContext
-
-
-class Context(BaseContext):
-    """
-    Dict like object containing the variables for the template.
-    """
-
-    def __init__(self, *args, **kwargs):
-        environment = args[0]
-        if not kwargs and len(args) == 2 and isinstance(args[1], dict):
-            base = args[1]
-        else:
-            base = dict(*args[1:], **kwargs)
-        super(Context, self).__init__(environment.undefined_singleton,
-                                      environment.globals, base)
-        self._translate_func = None
-        self.cache = {}
-        self.environment = environment
-
-    def to_dict(self):
-        """
-        Convert the context into a dict. This skips the globals.
-        """
-        result = {}
-        for layer in self.stack[1:]:
-            for key, value in layer.iteritems():
-                if key.startswith('::'):
-                    continue
-                result[key] = value
-        return result
-
-    def set_nonlocal(self, name, value):
-        """
-        Set a value in an outer scope.
-        """
-        for layer in self.stack[:0:-1]:
-            if name in layer:
-                layer[name] = value
-                return
-        self.initial[name] = value
-
-    def translate_func(self):
-        """
-        The translation function for this context. It takes
-        4 parameters. The singular string, the optional plural one,
-        The name of the variable in the replacements dict and the
-        replacements dict. This is only used by the i18n system
-        internally the simplified version (just one argument) is
-        available in the template for the user too.
-        """
-        if self._translate_func is not None:
-            return self._translate_func
-        translator = self.environment.get_translator(self)
-        gettext = translator.gettext
-        ngettext = translator.ngettext
-        def translate(s, p=None, n=None, r=None):
-            if p is None:
-                s = gettext(s)
-            else:
-                s = ngettext(s, p, r[n])
-            # apply replacement substitution only if replacements
-            # are given. This is the case for {% trans %}...{% endtrans %}
-            # but for the "_()" syntax and a trans tag without a body.
-            if r is not None:
-                return s % r
-            return s
-        translate.__doc__ = Context.translate_func.__doc__
-        self._translate_func = translate
-        return translate
-    translate_func = property(translate_func, doc=translate_func.__doc__)
-
-    def __repr__(self):
-        """
-        String representation of the context.
-        """
-        return 'Context(%r)' % self.to_dict()
-
-    def __pretty__(self, p, cycle):
-        if cycle:
-            return p.text('Context({...})')
-        p.begin_group(9, 'Context({')
-        for idx, (key, value) in enumerate(self.to_dict().iteritems()):
-            if idx:
-                p.text(',')
-                p.breakable()
-            p.pretty(key)
-            p.text(': ')
-            p.pretty(value)
-        p.end_group(9, '})')
-
-
-class LoopContext(object):
-    """
-    Simple class that provides special loop variables.
-    Used by `Environment.iterate`.
-    """
-
-    jinja_allowed_attributes = ['index', 'index0', 'length', 'parent',
-                                'even', 'odd', 'revindex0', 'revindex',
-                                'first', 'last']
-
-    def __init__(self, seq, parent, loop_function):
-        self.loop_function = loop_function
-        self.parent = parent
-        self._stack = []
-        if loop_function is None:
-            self.push(seq)
-
-    def push(self, seq):
-        """
-        Push a sequence to the loop stack. This is used by the
-        recursive for loop.
-        """
-        # iteration over None is catched, but we don't catch iteration
-        # over undefined because that behavior is handled in the
-        # undefined singleton
-        if seq is None:
-            seq = ()
-            length = 0
-        else:
-            try:
-                length = len(seq)
-            except (AttributeError, TypeError):
-                seq = list(seq)
-                length = len(seq)
-        self._stack.append({
-            'index':            -1,
-            'seq':              seq,
-            'length':           length
-        })
-        return self
-
-    def pop(self):
-        """Remove the last layer from the loop stack."""
-        return self._stack.pop()
-
-    iterated = property(lambda s: s._stack[-1]['index'] > -1)
-    index0 = property(lambda s: s._stack[-1]['index'])
-    index = property(lambda s: s._stack[-1]['index'] + 1)
-    revindex0 = property(lambda s: s._stack[-1]['length'] -
-                                   s._stack[-1]['index'] - 1)
-    revindex = property(lambda s: s._stack[-1]['length'] -
-                                  s._stack[-1]['index'])
-    length = property(lambda s: s._stack[-1]['length'])
-    even = property(lambda s: s._stack[-1]['index'] % 2 == 1)
-    odd = property(lambda s: s._stack[-1]['index'] % 2 == 0)
-    first = property(lambda s: s._stack[-1]['index'] == 0)
-    last = property(lambda s: s._stack[-1]['index'] ==
-                              s._stack[-1]['length'] - 1)
-
-    def __iter__(self):
-        s = self._stack[-1]
-        for idx, item in enumerate(s['seq']):
-            s['index'] = idx
-            yield item
-
-    def __len__(self):
-        return self._stack[-1]['length']
-
-    def __call__(self, seq):
-        if self.loop_function is not None:
-            return self.loop_function(seq)
-        raise TemplateRuntimeError('In order to make loops callable you have '
-                                   'to define them with the "recursive" '
-                                   'modifier.')
-
-    def __repr__(self):
-        if self._stack:
-            return '<LoopContext %d/%d%s>' % (
-                self.index,
-                self.length,
-                self.loop_function is not None and ' recursive' or ''
-            )
-        return '<LoopContext (empty)>'
-
-
-class CycleContext(object):
-    """
-    Helper class used for cycling.
-    """
-
-    def __init__(self, seq=None):
-        self.pos = -1
-        # bind the correct helper function based on the constructor signature
-        if seq is not None:
-            self.seq = seq
-            self.length = len(seq)
-            self.cycle = self.cycle_static
-        else:
-            self.cycle = self.cycle_dynamic
-
-    def cycle_static(self):
-        """Helper function for static cycling."""
-        self.pos = (self.pos + 1) % self.length
-        return self.seq[self.pos]
-
-    def cycle_dynamic(self, seq):
-        """Helper function for dynamic cycling."""
-        self.pos = pos = (self.pos + 1) % len(seq)
-        return seq[pos]
-
-
-class SuperBlock(object):
-    """
-    Helper class for ``{{ super() }}``.
-    """
-    jinja_allowed_attributes = ['name']
-
-    def __init__(self, name, blocks, level, context):
-        self.name = name
-        self.context = context
-        if name in blocks:
-            self.stack = blocks[name]
-            self.level = level
-        else:
-            self.stack = None
-
-    def __call__(self, offset=1):
-        if self.stack is not None:
-            level = self.level + (offset - 1)
-            if level < len(self.stack):
-                return self.stack[level](self.context)
-        raise TemplateRuntimeError('no super block for %r' % self.name)
-
-    def __repr__(self):
-        return '<SuperBlock %r>' % self.name
-
-
-class StateTest(object):
-    """
-    Wrapper class for basic lambdas in order to simplify
-    debugging in the parser. It also provides static helper
-    functions that replace some lambda expressions
-    """
-
-    def __init__(self, func, msg):
-        self.func = func
-        self.msg = msg
-
-    def __call__(self, token):
-        return self.func(token)
-
-    def expect_token(*types, **kw):
-        """Scans until one of the given tokens is found."""
-        msg = kw.pop('msg', None)
-        if kw:
-            raise TypeError('unexpected keyword argument %r' % iter(kw).next())
-        if len(types) == 1:
-            if msg is None:
-                msg = "expected '%s'" % types[0]
-            return StateTest(lambda t: t.type == types[0], msg)
-        if msg is None:
-            msg = 'expected one of %s' % ', '.join(["'%s'" % type
-                                                    for type in types])
-        return StateTest(lambda t: t.type in types, msg)
-    expect_token = staticmethod(expect_token)
-
-
-class Token(object):
-    """
-    Token class.
-    """
-    __slots__ = ('lineno', 'type', 'value')
-
-    def __init__(self, lineno, type, value):
-        self.lineno = lineno
-        self.type = intern(str(type))
-        self.value = value
-
-    def __str__(self):
-        from jinja.lexer import keywords, reverse_operators
-        if self.type in keywords:
-            return self.type
-        elif self.type in reverse_operators:
-            return reverse_operators[self.type]
-        return self.value
-
-    def __repr__(self):
-        return 'Token(%r, %r, %r)' % (
-            self.lineno,
-            self.type,
-            self.value
-        )
-
-
-class TokenStreamIterator(object):
-    """
-    The iterator for tokenstreams. Iterate over the stream
-    until the eof token is reached.
-    """
-
-    def __init__(self, stream):
-        self._stream = stream
-
-    def __iter__(self):
-        return self
-
-    def next(self):
-        token = self._stream.current
-        if token.type == 'eof':
-            self._stream.close()
-            raise StopIteration()
-        self._stream.next()
-        return token
-
-
-class TokenStream(object):
-    """
-    A token stream wraps a generator and supports pushing tokens back.
-    It also provides some functions to expect tokens and similar stuff.
-
-    Important note: Do never push more than one token back to the
-                    stream. Although the stream object won't stop you
-                    from doing so, the behavior is undefined. Multiple
-                    pushed tokens are only used internally!
-    """
-
-    def __init__(self, generator, filename):
-        self._next = generator.next
-        self._pushed = []
-        self.current = Token(1, 'initial', '')
-        self.filename = filename
-        self.next()
-
-    def __iter__(self):
-        return TokenStreamIterator(self)
-
-    def bound(self):
-        """Return True if the token stream is bound to a parser."""
-        return self.parser is not None
-    bound = property(bound, doc=bound.__doc__)
-
-    def lineno(self):
-        """The current line number."""
-        return self.current.lineno
-    lineno = property(lineno, doc=lineno.__doc__)
-
-    def __nonzero__(self):
-        """Are we at the end of the tokenstream?"""
-        return bool(self._pushed) or self.current.type != 'eof'
-
-    eos = property(lambda x: not x.__nonzero__(), doc=__nonzero__.__doc__)
-
-    def look(self):
-        """See what's the next token."""
-        if self._pushed:
-            return self._pushed[-1]
-        old_token = self.current
-        self.next()
-        new_token = self.current
-        self.current = old_token
-        self.push(new_token)
-        return new_token
-
-    def push(self, token):
-        """Push a token back to the stream."""
-        self._pushed.append(token)
-
-    def skip(self, n):
-        """Got n tokens ahead."""
-        for x in xrange(n):
-            self.next()
-
-    def shift(self, token):
-        """
-        Push one token into the stream.
-        """
-        old_current = self.current
-        self.next()
-        self.push(self.current)
-        self.push(old_current)
-        self.push(token)
-        self.next()
-
-    def next(self):
-        """Go one token ahead."""
-        if self._pushed:
-            self.current = self._pushed.pop()
-        elif self.current.type != 'eof':
-            try:
-                self.current = self._next()
-            except StopIteration:
-                self.close()
-
-    def read_whitespace(self):
-        """Read all the whitespace, up to the next tag."""
-        lineno = self.current.lineno
-        buf = []
-        while self.current.type == 'data' and not \
-              self.current.value.strip():
-            buf.append(self.current.value)
-            self.next()
-        if buf:
-            return Token(lineno, 'data', u''.join(buf))
-
-    def close(self):
-        """Close the stream."""
-        self.current = Token(self.current.lineno, 'eof', '')
-        self._next = None
-
-    def expect(self, token_type, token_value=_missing):
-        """Expect a given token type and return it"""
-        if self.current.type != token_type:
-            raise TemplateSyntaxError("expected token %r, got %r" %
-                                      (token_type, self.current.type),
-                                      self.current.lineno,
-                                      self.filename)
-        elif token_value is not _missing and \
-             self.current.value != token_value:
-            raise TemplateSyntaxError("expected %r, got %r" %
-                                      (token_value, self.current.value),
-                                      self.current.lineno,
-                                      self.filename)
-        try:
-            return self.current
-        finally:
-            self.next()
-
-
-class TemplateStream(object):
-    """
-    Wraps a genererator for outputing template streams.
-    """
-
-    def __init__(self, gen):
-        self._gen = gen
-        self._next = gen.next
-        self.buffered = False
-
-    def disable_buffering(self):
-        """
-        Disable the output buffering.
-        """
-        self._next = self._gen.next
-        self.buffered = False
-
-    def enable_buffering(self, size=5):
-        """
-        Enable buffering. Buffer `size` items before
-        yielding them.
-        """
-        if size <= 1:
-            raise ValueError('buffer size too small')
-        self.buffered = True
-
-        def buffering_next():
-            buf = []
-            c_size = 0
-            push = buf.append
-            next = self._gen.next
-
-            try:
-                while True:
-                    item = next()
-                    if item:
-                        push(item)
-                        c_size += 1
-                    if c_size >= size:
-                        raise StopIteration()
-            except StopIteration:
-                if not c_size:
-                    raise
-            return u''.join(buf)
-
-        self._next = buffering_next
-
-    def __iter__(self):
-        return self
-
-    def next(self):
-        return self._next()
diff --git a/jinja/debugger.py b/jinja/debugger.py
deleted file mode 100644 (file)
index 932fb1c..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    jinja.debugger
-    ~~~~~~~~~~~~~~
-
-    This module implements helper function Jinja uses to give the users a
-    possibility to develop Jinja templates like they would debug python code.
-    It seamlessly integreates into the python traceback system, in fact it
-    just modifies the trackback stack so that the line numbers are correct
-    and the frame information are bound to the context and not the frame of
-    the template evaluation loop.
-
-    To achive this it raises the exception it cought before in an isolated
-    namespace at a given line. The locals namespace is set to the current
-    template context.
-
-    The traceback generated by raising that exception is then either returned
-    or linked with the former traceback if the `jinja._debugger` module is
-    available. Because it's not possible to modify traceback objects from the
-    python space this module is needed for this process.
-
-    If it's not available it just ignores the other frames. Because this can
-    lead to actually harder to debug code there is a setting on the jinja
-    environment to disable the debugging system.
-
-    The isolated namespace which is used to raise the exception also contains
-    a `__loader__` name that helds a reference to a PEP 302 compatible loader.
-    Because there are currently some traceback systems (such as the paste
-    evalexception debugger) that do not provide the frame globals when
-    retrieving the source from the linecache module, Jinja injects the source
-    to the linecache module itself and changes the filename to a URL style
-    "virtual filename" so that Jinja doesn't acidentally override other files
-    in the linecache.
-
-    :copyright: 2007 by Armin Ronacher.
-    :license: BSD, see LICENSE for more details.
-"""
-
-import sys
-from random import randrange
-
-# if we have extended debugger support we should really use it
-try:
-    from jinja._debugger import *
-    has_extended_debugger = True
-except ImportError:
-    has_extended_debugger = False
-
-# we need the RUNTIME_EXCEPTION_OFFSET to skip the not used frames
-from jinja.utils import reversed, RUNTIME_EXCEPTION_OFFSET
-
-
-def fake_template_exception(exc_type, exc_value, tb, filename, lineno,
-                            source, context_or_env, tb_back=None):
-    """
-    Raise an exception "in a template". Return a traceback
-    object. This is used for runtime debugging, not compile time.
-    """
-    # some traceback systems allow to skip frames
-    __traceback_hide__ = True
-
-    # create the namespace which will be the local namespace
-    # of the new frame then. Some debuggers show local variables
-    # so we better inject the context and not the evaluation loop context.
-    from jinja.datastructure import Context
-    if isinstance(context_or_env, Context):
-        env = context_or_env.environment
-        namespace = context_or_env.to_dict()
-    else:
-        env = context_or_env
-        namespace = {}
-
-    # no unicode for filenames
-    if isinstance(filename, unicode):
-        filename = filename.encode('utf-8')
-
-    # generate an jinja unique filename used so that linecache
-    # gets data that doesn't interfere with other modules
-    if filename is None:
-        vfilename = 'jinja://~%d' % randrange(0, 10000)
-        filename = '<string>'
-    else:
-        vfilename = 'jinja://%s' % filename
-
-    # now create the used loaded and update the linecache
-    loader = TracebackLoader(env, source, filename)
-    loader.update_linecache(vfilename)
-    globals = {
-        '__name__':                 vfilename,
-        '__file__':                 vfilename,
-        '__loader__':               loader
-    }
-
-    # use the simple debugger to reraise the exception in the
-    # line where the error originally occoured
-    globals['__exception_to_raise__'] = (exc_type, exc_value)
-    offset = '\n' * (lineno - 1)
-    code = compile(offset + 'raise __exception_to_raise__[0], '
-                            '__exception_to_raise__[1]',
-                   vfilename or '<template>', 'exec')
-    try:
-        exec code in globals, namespace
-    except:
-        exc_info = sys.exc_info()
-
-    # if we have an extended debugger we set the tb_next flag so that
-    # we don't loose the higher stack items.
-    if has_extended_debugger:
-        if tb_back is not None:
-            tb_set_next(tb_back, exc_info[2])
-        if tb is not None:
-            tb_set_next(exc_info[2].tb_next, tb.tb_next)
-
-    # otherwise just return the exc_info from the simple debugger
-    return exc_info
-
-
-def translate_exception(template, context, exc_type, exc_value, tb):
-    """
-    Translate an exception and return the new traceback.
-    """
-    # depending on the python version we have to skip some frames to
-    # step to get the frame of the current template. The frames before
-    # are the toolchain used to render that thing.
-    for x in xrange(RUNTIME_EXCEPTION_OFFSET):
-        tb = tb.tb_next
-
-    result_tb = prev_tb = None
-    initial_tb = tb
-
-    # translate all the jinja frames in this traceback
-    while tb is not None:
-        if tb.tb_frame.f_globals.get('__jinja_template__'):
-            debug_info = tb.tb_frame.f_globals['debug_info']
-
-            # the next thing we do is matching the current error line against the
-            # debugging table to get the correct source line. If we can't find the
-            # filename and line number we return the traceback object unaltered.
-            error_line = tb.tb_lineno
-            for code_line, tmpl_filename, tmpl_line in reversed(debug_info):
-                if code_line <= error_line:
-                    source = tb.tb_frame.f_globals['template_source']
-                    tb = fake_template_exception(exc_type, exc_value, tb,
-                                                 tmpl_filename, tmpl_line,
-                                                 source, context, prev_tb)[-1]
-                    break
-        if result_tb is None:
-            result_tb = tb
-        prev_tb = tb
-        tb = tb.tb_next
-
-    # under some conditions we cannot translate any frame. in that
-    # situation just return the original traceback.
-    return (exc_type, exc_value, result_tb or intial_tb)
-
-
-def raise_syntax_error(exception, env, source=None):
-    """
-    This method raises an exception that includes more debugging
-    informations so that debugging works better. Unlike
-    `translate_exception` this method raises the exception with
-    the traceback.
-    """
-    exc_info = fake_template_exception(exception, None, None,
-                                       exception.filename,
-                                       exception.lineno, source, env)
-    raise exc_info[0], exc_info[1], exc_info[2]
-
-
-class TracebackLoader(object):
-    """
-    Fake importer that just returns the source of a template. It's just used
-    by Jinja interally and you shouldn't use it on your own.
-    """
-
-    def __init__(self, environment, source, filename):
-        self.loader = environment.loader
-        self.source = source
-        self.filename = filename
-
-    def update_linecache(self, virtual_filename):
-        """
-        Hacky way to let traceback systems know about the
-        Jinja template sourcecode. Very hackish indeed.
-        """
-        # check for linecache, not every implementation of python
-        # might have such an module (this check is pretty senseless
-        # because we depend on cpython anway)
-        try:
-            from linecache import cache
-        except ImportError:
-            return
-        data = self.get_source(None)
-        cache[virtual_filename] = (
-            len(data),
-            None,
-            data.splitlines(True),
-            virtual_filename
-        )
-
-    def get_source(self, impname):
-        """Return the source as bytestring."""
-        source = ''
-        if self.source is not None:
-            source = self.source
-        elif self.loader is not None:
-            try:
-                source = self.loader.get_source(self.filename)
-            except TemplateNotFound:
-                pass
-        if isinstance(source, unicode):
-            source = source.encode('utf-8')
-        return source
diff --git a/jinja/environment.py b/jinja/environment.py
deleted file mode 100644 (file)
index a1dd568..0000000
+++ /dev/null
@@ -1,404 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    jinja.environment
-    ~~~~~~~~~~~~~~~~~
-
-    Provides a class that holds runtime and parsing time options.
-
-    :copyright: 2007 by Armin Ronacher.
-    :license: BSD, see LICENSE for more details.
-"""
-from jinja.lexer import Lexer
-from jinja.parser import Parser
-from jinja.loaders import LoaderWrapper
-from jinja.datastructure import SilentUndefined, Markup, Context, FakeTranslator
-from jinja.translators.python import PythonTranslator 
-from jinja.utils import collect_translations, get_attribute
-from jinja.exceptions import FilterNotFound, TestNotFound, \
-     SecurityException, TemplateSyntaxError
-from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
-
-
-__all__ = ['Environment']
-
-
-#: minor speedup
-_getattr = getattr
-
-
-class Environment(object):
-    """
-    The Jinja environment.
-
-    The core component of Jinja is the `Environment`. It contains
-    important shared variables like configuration, filters, tests,
-    globals and others.
-    """
-
-    def __init__(self,
-                 block_start_string='{%',
-                 block_end_string='%}',
-                 variable_start_string='{{',
-                 variable_end_string='}}',
-                 comment_start_string='{#',
-                 comment_end_string='#}',
-                 trim_blocks=False,
-                 auto_escape=False,
-                 default_filters=None,
-                 template_charset='utf-8',
-                 charset='utf-8',
-                 namespace=None,
-                 loader=None,
-                 filters=None,
-                 tests=None,
-                 context_class=Context,
-                 undefined_singleton=SilentUndefined,
-                 disable_regexps=False,
-                 friendly_traceback=True,
-                 translator_factory=None,
-                 template_translator=PythonTranslator):
-        """
-        Here the possible initialization parameters:
-
-        ========================= ============================================
-        `block_start_string` *    the string marking the begin of a block.
-                                  this defaults to ``'{%'``.
-        `block_end_string` *      the string marking the end of a block.
-                                  defaults to ``'%}'``.
-        `variable_start_string` * the string marking the begin of a print
-                                  statement. defaults to ``'{{'``.
-        `comment_start_string` *  the string marking the begin of a
-                                  comment. defaults to ``'{#'``.
-        `comment_end_string` *    the string marking the end of a comment.
-                                  defaults to ``'#}'``.
-        `trim_blocks` *           If this is set to ``True`` the first newline
-                                  after a block is removed (block, not
-                                  variable tag!). Defaults to ``False``.
-        `auto_escape`             If this is set to ``True`` Jinja will
-                                  automatically escape all variables using xml
-                                  escaping methods. If you don't want to
-                                  escape a string you have to wrap it in a
-                                  ``Markup`` object from the
-                                  ``jinja.datastructure`` module. If
-                                  `auto_escape` is ``True`` there will be also
-                                  a ``Markup`` object in the template
-                                  namespace to define partial html fragments.
-                                  Note that we do not recommend this feature.
-        `default_filters`         list of tuples in the form (``filter_name``,
-                                  ``arguments``) where ``filter_name`` is the
-                                  name of a registered filter and
-                                  ``arguments`` a tuple with the filter
-                                  arguments. The filters specified here will
-                                  always be applied when printing data to the
-                                  template. *new in Jinja 1.1*
-        `template_charset`        The charset of the templates. Defaults
-                                  to ``'utf-8'``.
-        `charset`                 Charset of all string input data. Defaults
-                                  to ``'utf-8'``.
-        `namespace`               Global namespace for all templates.
-        `loader`                  Specify a template loader.
-        `filters`                 dict of filters or the default filters if
-                                  not defined.
-        `tests`                   dict of tests of the default tests if not
-                                  defined.
-        `context_class`           the context class this template should use.
-                                  See the `Context` documentation for more
-                                  details.
-        `undefined_singleton`     The singleton value that is used for missing
-                                  variables. *new in Jinja 1.1*
-        `disable_regexps`         Disable support for regular expresssions.
-        `friendly_traceback`      Set this to `False` to disable the developer
-                                  friendly traceback rewriting. Whenever an
-                                  runtime or syntax error occours jinja will
-                                  try to make a developer friendly traceback
-                                  that shows the error in the template line.
-                                  This however can be annoying when debugging
-                                  broken functions that are called from the
-                                  template. *new in Jinja 1.1*
-        `translator_factory`      A callback function that is called with
-                                  the context as first argument to get the
-                                  translator for the current instance.
-                                  *new in Jinja 1.2*
-        `template_translator`     An class that defines a static method called
-                                  process which can be used to process the 
-                                  template's AST into a compiled python module.
-                                  *new in Jinja 1.2*
-        ========================= ============================================
-
-        All of these variables except those marked with a star (*) are
-        modifiable after environment initialization.
-        """
-
-        # lexer / parser information
-        self.block_start_string = block_start_string
-        self.block_end_string = block_end_string
-        self.variable_start_string = variable_start_string
-        self.variable_end_string = variable_end_string
-        self.comment_start_string = comment_start_string
-        self.comment_end_string = comment_end_string
-        self.trim_blocks = trim_blocks
-
-        # other stuff
-        self.template_charset = template_charset
-        self.charset = charset
-        self.loader = loader
-        if filters is None:
-            filters = DEFAULT_FILTERS.copy()
-        self.filters = filters
-        if tests is None:
-            tests = DEFAULT_TESTS.copy()
-        self.tests = tests
-        self.default_filters = default_filters or []
-        self.context_class = context_class
-        self.undefined_singleton = undefined_singleton
-        self.disable_regexps = disable_regexps
-        self.friendly_traceback = friendly_traceback
-
-        # global namespace
-        if namespace is None:
-            namespace = DEFAULT_NAMESPACE.copy()
-        self.globals = namespace
-
-        # jinja 1.0 compatibility
-        if auto_escape:
-            self.default_filters.append(('escape', (True,)))
-            self.globals['Markup'] = Markup
-
-        # and here the translator factory
-        self.translator_factory = translator_factory
-
-        # and here the AST translator
-        self.template_translator = template_translator
-
-        # create lexer
-        self.lexer = Lexer(self)
-
-    def loader(self, value):
-        """
-        Get or set the template loader.
-        """
-        self._loader = LoaderWrapper(self, value)
-    loader = property(lambda s: s._loader, loader, doc=loader.__doc__)
-
-    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
-        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.
-        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)
-
-        The tuples are returned in the form ``(lineno, token, value)``.
-        """
-        return self.lexer.tokeniter(source, filename)
-
-    def from_string(self, source):
-        """
-        Load and parse a template source and translate it into eval-able
-        Python code. This code is wrapped within a `Template` class that
-        allows you to render it.
-        """
-        from jinja.translators.python import PythonTranslator
-        try:
-            rv = PythonTranslator.process(self, Parser(self, source).parse(),
-                                          source)
-        except TemplateSyntaxError, e:
-            # on syntax errors rewrite the traceback if wanted
-            if not self.friendly_traceback:
-                raise
-            from jinja.debugger import raise_syntax_error
-            if __debug__:
-                __traceback_hide__ = True
-            raise_syntax_error(e, self, source)
-        else:
-            return rv
-
-    def get_template(self, filename):
-        """
-        Load a template from a loader. If the template does not exist, you
-        will get a `TemplateNotFound` exception.
-        """
-        return self._loader.load(filename, translator=self.template_translator)
-
-    def to_unicode(self, value):
-        """
-        Convert a value to unicode with the rules defined on the environment.
-        """
-        # undefined and None expand to ""
-        if value in (None, self.undefined_singleton):
-            return u''
-        # things that are already unicode can pass. As long as nobody
-        # does ugly things with the class it works for jinja too
-        elif isinstance(value, unicode):
-            return value
-        # otherwise try to use __unicode__ or decode __str__
-        try:
-            return unicode(value)
-        except UnicodeError:
-            return str(value).decode(self.charset, 'ignore')
-
-    def get_translator(self, context):
-        """
-        Return the translator for i18n.
-
-        A translator is an object that provides the two functions
-        ``gettext(string)`` and ``ngettext(singular, plural, n)``. Note
-        that both of them have to return unicode!
-        """
-        if self.translator_factory is not None:
-            return self.translator_factory(context)
-        return FakeTranslator()
-
-    def get_translations(self, name):
-        """
-        Load template `name` and return all translatable strings (note that
-        that it really just returns the strings form this template, not from
-        the parent or any included templates!)
-        """
-        return collect_translations(self.loader.parse(name))
-
-    def get_translations_for_string(self, string):
-        """
-        Like `get_translations`, but the translations are loaded from a
-        normal string that represents the template.
-        """
-        return collect_translations(self.parse(string))
-
-    def apply_filters(self, value, context, filters):
-        """
-        Apply a list of filters on the variable.
-        """
-        # some traceback systems allow to skip frames. but allow
-        # disabling that via -O to not make things slow
-        if __debug__:
-            __traceback_hide__ = True
-
-        cache = context.cache
-        for key in filters:
-            if key in cache:
-                func = cache[key]
-            else:
-                filtername, args = key
-                if filtername not in self.filters:
-                    raise FilterNotFound(filtername)
-                cache[key] = func = self.filters[filtername](*args)
-            value = func(self, context, value)
-        return value
-
-    def perform_test(self, context, testname, args, value):
-        """
-        Perform a test on a variable.
-        """
-        # some traceback systems allow to skip frames. but allow
-        # disabling that via -O to not make things slow
-        if __debug__:
-            __traceback_hide__ = True
-
-        key = (testname, args)
-        if key in context.cache:
-            func = context.cache[key]
-        else:
-            if testname not in self.tests:
-                raise TestNotFound(testname)
-            context.cache[key] = func = self.tests[testname](*args)
-        return not not func(self, context, value)
-
-    def get_attribute(self, obj, name):
-        """
-        Get one attribute from an object.
-        """
-        # some traceback systems allow to skip frames. but allow
-        # disabling that via -O to not make things slow
-        if __debug__:
-            __traceback_hide__ = True
-
-        try:
-            return obj[name]
-        except (TypeError, KeyError, IndexError, AttributeError):
-            try:
-                return get_attribute(obj, name)
-            except (AttributeError, SecurityException):
-                pass
-        if obj is self.undefined_singleton:
-            return _getattr(obj, name)
-        return self.undefined_singleton
-
-    def get_attributes(self, obj, attributes):
-        """
-        Get some attributes from an object. If attributes is an
-        empty sequence the object is returned as it.
-        """
-        get = self.get_attribute
-        for name in attributes:
-            obj = get(obj, name)
-        return obj
-
-    def call_function(self, f, context, args, kwargs, dyn_args, dyn_kwargs):
-        """
-        Function call helper. Called for all functions that are passed
-        any arguments.
-        """
-        # some traceback systems allow to skip frames. but allow
-        # disabling that via -O to not make things slow
-        if __debug__:
-            __traceback_hide__ = True
-
-        if dyn_args is not None:
-            args += tuple(dyn_args)
-        if dyn_kwargs is not None:
-            kwargs.update(dyn_kwargs)
-        if _getattr(f, 'jinja_unsafe_call', False) or \
-           _getattr(f, 'alters_data', False):
-            return self.undefined_singleton
-        if _getattr(f, 'jinja_context_callable', False):
-            args = (self, context) + args
-        return f(*args, **kwargs)
-
-    def call_function_simple(self, f, context):
-        """
-        Function call without arguments. Because of the smaller signature and
-        fewer logic here we have a bit of redundant code.
-        """
-        # some traceback systems allow to skip frames. but allow
-        # disabling that via -O to not make things slow
-        if __debug__:
-            __traceback_hide__ = True
-
-        if _getattr(f, 'jinja_unsafe_call', False) or \
-           _getattr(f, 'alters_data', False):
-            return self.undefined_singleton
-        if _getattr(f, 'jinja_context_callable', False):
-            return f(self, context)
-        return f()
-
-    def finish_var(self, value, ctx):
-        """
-        As long as no write_var function is passed to the template
-        evaluator the source generated by the python translator will
-        call this function for all variables.
-        """
-        # some traceback systems allow to skip frames. but allow
-        # disabling that via -O to not make things slow
-        if __debug__:
-            __traceback_hide__ = True
-
-        if value is None:
-            return u''
-        elif value is self.undefined_singleton:
-            return unicode(value)
-        elif _getattr(value, 'jinja_no_finalization', False):
-            return value
-        val = self.to_unicode(value)
-        if self.default_filters:
-            val = self.apply_filters(val, ctx, self.default_filters)
-        return val
diff --git a/jinja/exceptions.py b/jinja/exceptions.py
deleted file mode 100644 (file)
index c900815..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    jinja.exceptions
-    ~~~~~~~~~~~~~~~~
-
-    Jinja exceptions.
-
-    :copyright: 2007 by Armin Ronacher.
-    :license: BSD, see LICENSE for more details.
-"""
-
-
-class TemplateError(RuntimeError):
-    pass
-
-
-class SecurityException(TemplateError):
-    """
-    Raise if the template designer tried to do something dangerous.
-    """
-
-
-class FilterNotFound(KeyError, TemplateError):
-    """
-    Raised if a filter does not exist.
-    """
-
-    def __init__(self, message):
-        KeyError.__init__(self, message)
-
-
-class FilterArgumentError(TypeError, TemplateError):
-    """
-    An argument passed to the filter was invalid.
-    """
-
-    def __init__(self, message):
-        TypeError.__init__(self, message)
-
-
-class TestNotFound(KeyError, TemplateError):
-    """
-    Raised if a test does not exist.
-    """
-
-    def __init__(self, message):
-        KeyError.__init__(self, message)
-
-
-class TestArgumentError(TypeError, TemplateError):
-    """
-    An argument passed to a test function was invalid.
-    """
-
-    def __init__(self, message):
-        TypeError.__init__(self, message)
-
-
-class TemplateNotFound(IOError, LookupError, TemplateError):
-    """
-    Raised if a template does not exist.
-    """
-
-    def __init__(self, name):
-        IOError.__init__(self, name)
-        self.name = name
-
-
-class TemplateSyntaxError(SyntaxError, TemplateError):
-    """
-    Raised to tell the user that there is a problem with the template.
-    """
-
-    def __init__(self, message, lineno, filename):
-        SyntaxError.__init__(self, message)
-        self.lineno = lineno
-        self.filename = filename
-
-
-class TemplateRuntimeError(TemplateError):
-    """
-    Raised by the template engine if a tag encountered an error when
-    rendering.
-    """
-
-
-class TemplateIncludeError(TemplateError):
-    """
-    Raised by the `ControlledLoader` if recursive includes where
-    detected.
-    """
diff --git a/jinja/loaders.py b/jinja/loaders.py
deleted file mode 100644 (file)
index 03a8905..0000000
+++ /dev/null
@@ -1,825 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    jinja.loaders
-    ~~~~~~~~~~~~~
-
-    Jinja loader classes.
-
-    :copyright: 2007 by Armin Ronacher, Bryan McLemore.
-    :license: BSD, see LICENSE for more details.
-"""
-
-import codecs
-try:
-    from hashlib import sha1
-except ImportError:
-    from sha import new as sha1
-import time
-from os import path
-from threading import Lock
-from jinja.parser import Parser
-from jinja.translators.python import PythonTranslator, Template
-from jinja.exceptions import TemplateNotFound, TemplateSyntaxError, \
-     TemplateIncludeError
-from jinja.utils import CacheDict
-
-
-#: when updating this, update the listing in the jinja package too
-__all__ = ['FileSystemLoader', 'PackageLoader', 'DictLoader', 'ChoiceLoader',
-           'FunctionLoader', 'MemcachedFileSystemLoader']
-
-
-def get_template_filename(searchpath, name):
-    """
-    Return the filesystem filename wanted.
-    """
-    for sep in path.sep, path.altsep:
-        if sep and sep in name:
-            name = name.replace(sep, '/')
-    return path.join(searchpath, *[p for p in name.split('/')
-                     if p and p[0] != '.'])
-
-
-def get_cachename(cachepath, name, salt=None):
-    """
-    Return the filename for a cached file.
-    """
-    return path.join(cachepath, 'jinja_%s.cache' %
-                     sha1('jinja(%s|%s)tmpl' %
-                          (name, salt or '')).hexdigest())
-
-
-def _loader_missing(*args, **kwargs):
-    """Helper function for `LoaderWrapper`."""
-    raise RuntimeError('no loader defined')
-
-
-class LoaderWrapper(object):
-    """
-    Wraps a loader so that it's bound to an environment.
-    Also handles template syntax errors.
-    """
-
-    def __init__(self, environment, loader):
-        self.environment = environment
-        self.loader = loader
-        if self.loader is None:
-            self.get_source = self.parse = self.load = _loader_missing
-            self.available = False
-        else:
-            self.available = True
-
-    def __getattr__(self, name):
-        """
-        Not found attributes are redirected to the loader
-        """
-        return getattr(self.loader, name)
-
-    def get_source(self, name, parent=None):
-        """Retrieve the sourcecode of a template."""
-        # just ascii chars are allowed as template names
-        name = str(name)
-        return self.loader.get_source(self.environment, name, parent)
-
-    def parse(self, name, parent=None):
-        """Retreive a template and parse it."""
-        # just ascii chars are allowed as template names
-        name = str(name)
-        return self.loader.parse(self.environment, name, parent)
-
-    def load(self, name, translator=PythonTranslator):
-        """
-        Translate a template and return it. This must not necesarily
-        be a template class. The javascript translator for example
-        will just output a string with the translated code.
-        """
-        # just ascii chars are allowed as template names
-        name = str(name)
-        try:
-            return self.loader.load(self.environment, name, translator)
-        except TemplateSyntaxError, e:
-            if not self.environment.friendly_traceback:
-                raise
-            __traceback_hide__ = True
-            from jinja.debugger import raise_syntax_error
-            raise_syntax_error(e, self.environment)
-
-    def get_controlled_loader(self):
-        """
-        Return a loader that runs in a controlled environment.  (Keeps
-        track of templates that it loads and is not thread safe).
-        """
-        return ControlledLoader(self.environment, self.loader)
-
-    def _loader_missing(self, *args, **kwargs):
-        """Helper method that overrides all other methods if no
-        loader is defined."""
-        raise RuntimeError('no loader defined')
-
-    def __nonzero__(self):
-        return self.available
-
-
-class ControlledLoader(LoaderWrapper):
-    """
-    Used for template extending and including.
-    """
-
-    def __init__(self, environment, loader):
-        LoaderWrapper.__init__(self, environment, loader)
-        self._stack = []
-
-    def get_controlled_loader(self):
-        raise TypeError('Cannot get new controlled loader from an already '
-                        'controlled loader.')
-
-    def mark_as_processed(self):
-        """Mark the last parsed/sourced/included template as processed."""
-        if not self._stack:
-            raise RuntimeError('No template for marking found')
-        self._stack.pop()
-
-    def _controlled(method):
-        def new_method(self, name, *args, **kw):
-            if name in self._stack:
-                raise TemplateIncludeError('Circular imports/extends '
-                                           'detected.  %r appeared twice.' %
-                                           name)
-            self._stack.append(name)
-            return method(self, name, *args, **kw)
-        try:
-            new_method.__name__ = method.__name__
-            new_method.__doc__ = method.__doc__
-        except:
-            pass
-        return new_method
-
-    get_source = _controlled(LoaderWrapper.get_source)
-    parse = _controlled(LoaderWrapper.parse)
-    load = _controlled(LoaderWrapper.load)
-    del _controlled
-
-
-class BaseLoader(object):
-    """
-    Use this class to implement loaders.
-
-    Just inherit from this class and implement a method called
-    `get_source` with the signature (`environment`, `name`, `parent`)
-    that returns sourcecode for the template.
-
-    For more complex loaders you probably want to override `load` to
-    or not use the `BaseLoader` at all.
-    """
-
-    def parse(self, environment, name, parent):
-        """
-        Load and parse a template
-        """
-        source = self.get_source(environment, name, parent)
-        return Parser(environment, source, name).parse()
-
-    def load(self, environment, name, translator):
-        """
-        Load and translate a template
-        """
-        ast = self.parse(environment, name, None)
-        return translator.process(environment, ast)
-
-    def get_source(self, environment, name, parent):
-        """
-        Override this method to get the source for a template.
-        """
-        raise TemplateNotFound(name)
-
-
-class CachedLoaderMixin(object):
-    """
-    Mixin this class to implement simple memory and disk caching. The
-    memcaching just uses a dict in the loader so if you have a global
-    environment or at least a global loader this can speed things up.
-
-    If the memcaching is enabled you can use (with Jinja 1.1 onwards)
-    the `clear_memcache` function to clear the cache.
-
-    For memcached support check the `MemcachedLoaderMixin`.
-    """
-
-    def __init__(self, use_memcache, cache_size, cache_folder, auto_reload,
-                 cache_salt=None):
-        if use_memcache:
-            self.__memcache = CacheDict(cache_size)
-        else:
-            self.__memcache = None
-        self.__cache_folder = cache_folder
-        if not hasattr(self, 'check_source_changed'):
-            self.__auto_reload = False
-        else:
-            self.__auto_reload = auto_reload
-        self.__salt = cache_salt
-        self.__times = {}
-        self.__lock = Lock()
-
-    def clear_memcache(self):
-        """
-        Clears the memcache.
-        """
-        if self.__memcache is not None:
-            self.__memcache.clear()
-
-    def load(self, environment, name, translator):
-        """
-        Load and translate a template. First we check if there is a
-        cached version of this template in the memory cache. If this is
-        not the cache check for a compiled template in the disk cache
-        folder. And if none of this is the case we translate the temlate,
-        cache and return it.
-        """
-        self.__lock.acquire()
-        try:
-            # caching is only possible for the python translator. skip
-            # all other translators
-            if not issubclass(translator, PythonTranslator):
-                return super(CachedLoaderMixin, self).load(
-                             environment, name, translator)
-
-            tmpl = None
-            save_to_disk = False
-            push_to_memory = False
-
-            # auto reload enabled? check for the last change of
-            # the template
-            if self.__auto_reload:
-                last_change = self.check_source_changed(environment, name)
-            else:
-                last_change = None
-
-            # check if we have something in the memory cache and the
-            # memory cache is enabled.
-            if self.__memcache is not None:
-                if name in self.__memcache:
-                    tmpl = self.__memcache[name]
-                    # if auto reload is enabled check if the template changed
-                    if last_change and last_change > self.__times[name]:
-                        tmpl = None
-                        push_to_memory = True
-                else:
-                    push_to_memory = True
-
-            # mem cache disabled or not cached by now
-            # try to load if from the disk cache
-            if tmpl is None and self.__cache_folder is not None:
-                cache_fn = get_cachename(self.__cache_folder, name, self.__salt)
-                if last_change is not None:
-                    try:
-                        cache_time = path.getmtime(cache_fn)
-                    except OSError:
-                        cache_time = 0
-                if last_change is None or (cache_time and
-                   last_change <= cache_time):
-                    try:
-                        f = file(cache_fn, 'rb')
-                    except IOError:
-                        tmpl = None
-                        save_to_disk = True
-                    else:
-                        try:
-                            tmpl = Template.load(environment, f)
-                        finally:
-                            f.close()
-                else:
-                    save_to_disk = True
-
-            # if we still have no template we load, parse and translate it.
-            if tmpl is None:
-                tmpl = super(CachedLoaderMixin, self).load(
-                             environment, name, translator)
-
-            # save the compiled template on the disk if enabled
-            if save_to_disk:
-                f = file(cache_fn, 'wb')
-                try:
-                    tmpl.dump(f)
-                finally:
-                    f.close()
-
-            # if memcaching is enabled and the template not loaded
-            # we add that there.
-            if push_to_memory:
-                self.__times[name] = time.time()
-                self.__memcache[name] = tmpl
-            return tmpl
-        finally:
-            self.__lock.release()
-
-
-class MemcachedLoaderMixin(object):
-    """
-    Uses a memcached server to cache the templates.
-
-    Requires the memcache library from `tummy`_ or the cmemcache library
-    from `Gijsbert de Haan`_.
-
-    With Jinja 1.2 onwards you can also provide a `client` keyword argument
-    that takes an already instanciated memcache client or memcache client
-    like object.
-
-    .. _tummy: http://www.tummy.com/Community/software/python-memcached/
-    .. _Gisjsbert de Haan: http://gijsbert.org/cmemcache/
-    """
-
-    def __init__(self, use_memcache, memcache_time=60 * 60 * 24 * 7,
-                 memcache_host=None, item_prefix='template/', client=None):
-        if memcache_host is None:
-            memcache_host = ['127.0.0.1:11211']
-        if use_memcache:
-            if client is None:
-                try:
-                    try:
-                        from cmemcache import Client
-                    except ImportError:
-                        from memcache import Client
-                except ImportError:
-                    raise RuntimeError('the %r loader requires an installed '
-                                       'memcache module' %
-                                       self.__class__.__name__)
-                client = Client(list(memcache_host))
-            self.__memcache = client
-            self.__memcache_time = memcache_time
-        else:
-            self.__memcache = None
-        self.__item_prefix = item_prefix
-        self.__lock = Lock()
-
-    def load(self, environment, name, translator):
-        """
-        Load and translate a template. First we check if there is a
-        cached version of this template in the memory cache. If this is
-        not the cache check for a compiled template in the disk cache
-        folder. And if none of this is the case we translate the template,
-        cache and return it.
-        """
-        self.__lock.acquire()
-        try:
-            # caching is only possible for the python translator. skip
-            # all other translators
-            if not issubclass(translator, PythonTranslator):
-                return super(MemcachedLoaderMixin, self).load(
-                             environment, name, translator)
-            tmpl = None
-            push_to_memory = False
-
-            # check if we have something in the memory cache and the
-            # memory cache is enabled.
-            if self.__memcache is not None:
-                bytecode = self.__memcache.get(self.__item_prefix + name)
-                if bytecode:
-                    tmpl = Template.load(environment, bytecode)
-                else:
-                    push_to_memory = True
-
-            # if we still have no template we load, parse and translate it.
-            if tmpl is None:
-                tmpl = super(MemcachedLoaderMixin, self).load(
-                             environment, name, translator)
-
-            # if memcaching is enabled and the template not loaded
-            # we add that there.
-            if push_to_memory:
-                self.__memcache.set(self.__item_prefix + name, tmpl.dump(),
-                                    self.__memcache_time)
-            return tmpl
-        finally:
-            self.__lock.release()
-
-
-class BaseFileSystemLoader(BaseLoader):
-    """
-    Baseclass for the file system loader that does not do any caching.
-    It exists to avoid redundant code, just don't use it without subclassing.
-
-    How subclassing can work:
-
-    .. sourcecode:: python
-
-        from jinja.loaders import BaseFileSystemLoader
-
-        class MyFileSystemLoader(BaseFileSystemLoader):
-            def __init__(self):
-                BaseFileSystemLoader.__init__(self, '/path/to/templates')
-
-    The base file system loader only takes one parameter beside self which
-    is the path to the templates.
-    """
-
-    def __init__(self, searchpath):
-        self.searchpath = path.abspath(searchpath)
-
-    def get_source(self, environment, name, parent):
-        filename = get_template_filename(self.searchpath, name)
-        if path.isfile(filename):
-            f = codecs.open(filename, 'r', environment.template_charset)
-            try:
-                return f.read()
-            finally:
-                f.close()
-        else:
-            raise TemplateNotFound(name)
-
-
-class FileSystemLoader(CachedLoaderMixin, BaseFileSystemLoader):
-    """
-    Loads templates from the filesystem:
-
-    .. sourcecode:: python
-
-        from jinja import Environment, FileSystemLoader
-        e = Environment(loader=FileSystemLoader('templates/'))
-
-    You can pass the following keyword arguments to the loader on
-    initialization:
-
-    =================== =================================================
-    ``searchpath``      String with the path to the templates on the
-                        filesystem.
-    ``use_memcache``    Set this to ``True`` to enable memory caching.
-                        This is usually a good idea in production mode,
-                        but disable it during development since it won't
-                        reload template changes automatically.
-                        This only works in persistent environments like
-                        FastCGI.
-    ``memcache_size``   Number of template instance you want to cache.
-                        Defaults to ``40``.
-    ``cache_folder``    Set this to an existing directory to enable
-                        caching of templates on the file system. Note
-                        that this only affects templates transformed
-                        into python code. Default is ``None`` which means
-                        that caching is disabled.
-    ``auto_reload``     Set this to `False` for a slightly better
-                        performance. In that case Jinja won't check for
-                        template changes on the filesystem.
-    ``cache_salt``      Optional unique number to not confuse the
-                        caching system when caching more than one
-                        template loader in the same folder. Defaults
-                        to the searchpath. *New in Jinja 1.1*
-    =================== =================================================
-    """
-
-    def __init__(self, searchpath, use_memcache=False, memcache_size=40,
-                 cache_folder=None, auto_reload=True, cache_salt=None):
-        BaseFileSystemLoader.__init__(self, searchpath)
-
-        if cache_salt is None:
-            cache_salt = self.searchpath
-        CachedLoaderMixin.__init__(self, use_memcache, memcache_size,
-                                   cache_folder, auto_reload, cache_salt)
-
-    def check_source_changed(self, environment, name):
-        filename = get_template_filename(self.searchpath, name)
-        if path.isfile(filename):
-            return path.getmtime(filename)
-        return -1
-
-
-class MemcachedFileSystemLoader(MemcachedLoaderMixin, BaseFileSystemLoader):
-    """
-    Loads templates from the filesystem and caches them on a memcached
-    server.
-
-    .. sourcecode:: python
-
-        from jinja import Environment, MemcachedFileSystemLoader
-        e = Environment(loader=MemcachedFileSystemLoader('templates/',
-            memcache_host=['192.168.2.250:11211']
-        ))
-
-    You can pass the following keyword arguments to the loader on
-    initialization:
-
-    =================== =================================================
-    ``searchpath``      String with the path to the templates on the
-                        filesystem.
-    ``use_memcache``    Set this to ``True`` to enable memcached caching.
-                        In that case it behaves like a normal
-                        `FileSystemLoader` with disabled caching.
-    ``memcache_time``   The expire time of a template in the cache.
-    ``memcache_host``   a list of memcached servers.
-    ``item_prefix``     The prefix for the items on the server. Defaults
-                        to ``'template/'``.
-    =================== =================================================
-    """
-
-    def __init__(self, searchpath, use_memcache=True,
-                 memcache_time=60 * 60 * 24 * 7, memcache_host=None,
-                 item_prefix='template/'):
-        BaseFileSystemLoader.__init__(self, searchpath)
-        MemcachedLoaderMixin.__init__(self, use_memcache, memcache_time,
-                                      memcache_host, item_prefix)
-
-
-class BasePackageLoader(BaseLoader):
-    """
-    Baseclass for the package loader that does not do any caching.
-
-    It accepts two parameters: The name of the package and the path relative
-    to the package:
-
-    .. sourcecode:: python
-
-        from jinja.loaders import BasePackageLoader
-
-        class MyPackageLoader(BasePackageLoader):
-            def __init__(self):
-                BasePackageLoader.__init__(self, 'my_package', 'shared/templates')
-
-    The relative path must use slashes as path delimiters, even on Mac OS
-    and Microsoft Windows.
-
-    It uses the `pkg_resources` libraries distributed with setuptools for
-    retrieving the data from the packages. This works for eggs too so you
-    don't have to mark your egg as non zip safe. If pkg_resources is not
-    available it just falls back to path joining relative to the package.
-    """
-
-    def __init__(self, package_name, package_path, force_native=False):
-        try:
-            import pkg_resources
-        except ImportError:
-            raise RuntimeError('setuptools not installed')
-        self.package_name = package_name
-        self.package_path = package_path
-        self.force_native = force_native
-
-    def _get_load_func(self):
-        if hasattr(self, '_load_func'):
-            return self._load_func
-        try:
-            from pkg_resources import resource_exists, resource_string
-            if self.force_native:
-                raise ImportError()
-        except ImportError:
-            basepath = path.dirname(__import__(self.package_name, None, None,
-                                    ['__file__']).__file__)
-            def load_func(name):
-                filename = path.join(basepath, *(
-                    self.package_path.split('/') +
-                    [p for p in name.split('/') if p != '..'])
-                )
-                if path.isfile(filename):
-                    f = file(filename)
-                    try:
-                        return f.read()
-                    finally:
-                        f.close()
-        else:
-            def load_func(name):
-                path = '/'.join([self.package_path] + [p for p in name.split('/')
-                         if p != '..'])
-                if resource_exists(self.package_name, path):
-                    return resource_string(self.package_name, path)
-        self._load_func = load_func
-        return load_func
-
-    def get_source(self, environment, name, parent):
-        load_func = self._get_load_func()
-        contents = load_func(name)
-        if contents is None:
-            raise TemplateNotFound(name)
-        return contents.decode(environment.template_charset)
-
-
-class PackageLoader(CachedLoaderMixin, BasePackageLoader):
-    """
-    Loads templates from python packages using setuptools.
-
-    .. sourcecode:: python
-
-        from jinja import Environment, PackageLoader
-        e = Environment(loader=PackageLoader('yourapp', 'template/path'))
-
-    You can pass the following keyword arguments to the loader on
-    initialization:
-
-    =================== =================================================
-    ``package_name``    Name of the package containing the templates.
-    ``package_path``    Path of the templates inside the package.
-    ``use_memcache``    Set this to ``True`` to enable memory caching.
-                        This is usually a good idea in production mode,
-                        but disable it during development since it won't
-                        reload template changes automatically.
-                        This only works in persistent environments like
-                        FastCGI.
-    ``memcache_size``   Number of template instance you want to cache.
-                        Defaults to ``40``.
-    ``cache_folder``    Set this to an existing directory to enable
-                        caching of templates on the file system. Note
-                        that this only affects templates transformed
-                        into python code. Default is ``None`` which means
-                        that caching is disabled.
-    ``auto_reload``     Set this to `False` for a slightly better
-                        performance. In that case Jinja won't check for
-                        template changes on the filesystem. If the
-                        templates are inside of an egg file this won't
-                        have an effect.
-    ``cache_salt``      Optional unique number to not confuse the
-                        caching system when caching more than one
-                        template loader in the same folder. Defaults
-                        to ``package_name + '/' + package_path``.
-                        *New in Jinja 1.1*
-    =================== =================================================
-
-    Important note: If you're using an application that is inside of an
-    egg never set `auto_reload` to `True`. The egg resource manager will
-    automatically export files to the file system and touch them so that
-    you not only end up with additional temporary files but also an automatic
-    reload each time you load a template.
-    """
-
-    def __init__(self, package_name, package_path, use_memcache=False,
-                 memcache_size=40, cache_folder=None, auto_reload=True,
-                 cache_salt=None):
-        BasePackageLoader.__init__(self, package_name, package_path)
-
-        if cache_salt is None:
-            cache_salt = package_name + '/' + package_path
-        CachedLoaderMixin.__init__(self, use_memcache, memcache_size,
-                                   cache_folder, auto_reload, cache_salt)
-
-    def check_source_changed(self, environment, name):
-        from pkg_resources import resource_exists, resource_filename
-        fn = resource_filename(self.package_name, '/'.join([self.package_path] +
-                               [p for p in name.split('/') if p and p[0] != '.']))
-        if resource_exists(self.package_name, fn):
-            return path.getmtime(fn)
-        return -1
-
-
-class BaseFunctionLoader(BaseLoader):
-    """
-    Baseclass for the function loader that doesn't do any caching.
-
-    It just accepts one parameter which is the function which is called
-    with the name of the requested template. If the return value is `None`
-    the loader will raise a `TemplateNotFound` error.
-
-    .. sourcecode:: python
-
-        from jinja.loaders import BaseFunctionLoader
-
-        templates = {...}
-
-        class MyFunctionLoader(BaseFunctionLoader):
-            def __init__(self):
-                BaseFunctionLoader(templates.get)
-    """
-
-    def __init__(self, loader_func):
-        self.loader_func = loader_func
-
-    def get_source(self, environment, name, parent):
-        rv = self.loader_func(name)
-        if rv is None:
-            raise TemplateNotFound(name)
-        if isinstance(rv, str):
-            return rv.decode(environment.template_charset)
-        return rv
-
-
-class FunctionLoader(CachedLoaderMixin, BaseFunctionLoader):
-    """
-    Loads templates by calling a function which has to return a string
-    or `None` if an error occoured.
-
-    .. sourcecode:: python
-
-        from jinja import Environment, FunctionLoader
-
-        def my_load_func(template_name):
-            if template_name == 'foo':
-                return '...'
-
-        e = Environment(loader=FunctionLoader(my_load_func))
-
-    Because the interface is limited there is no way to cache such
-    templates. Usually you should try to use a loader with a more
-    solid backend.
-
-    You can pass the following keyword arguments to the loader on
-    initialization:
-
-    =================== =================================================
-    ``loader_func``     Function that takes the name of the template to
-                        load. If it returns a string or unicode object
-                        it's used to load a template. If the return
-                        value is None it's considered missing.
-    ``getmtime_func``   Function used to check if templates requires
-                        reloading. Has to return the UNIX timestamp of
-                        the last template change or ``-1`` if this template
-                        does not exist or requires updates at any cost.
-    ``use_memcache``    Set this to ``True`` to enable memory caching.
-                        This is usually a good idea in production mode,
-                        but disable it during development since it won't
-                        reload template changes automatically.
-                        This only works in persistent environments like
-                        FastCGI.
-    ``memcache_size``   Number of template instance you want to cache.
-                        Defaults to ``40``.
-    ``cache_folder``    Set this to an existing directory to enable
-                        caching of templates on the file system. Note
-                        that this only affects templates transformed
-                        into python code. Default is ``None`` which means
-                        that caching is disabled.
-    ``auto_reload``     Set this to `False` for a slightly better
-                        performance. In that case of `getmtime_func`
-                        not being provided this won't have an effect.
-    ``cache_salt``      Optional unique number to not confuse the
-                        caching system when caching more than one
-                        template loader in the same folder.
-    =================== =================================================
-    """
-
-    def __init__(self, loader_func, getmtime_func=None, use_memcache=False,
-                 memcache_size=40, cache_folder=None, auto_reload=True,
-                 cache_salt=None):
-        BaseFunctionLoader.__init__(self, loader_func)
-        # when changing the signature also check the jinja.plugin function
-        # loader instantiation.
-        self.getmtime_func = getmtime_func
-        if auto_reload and getmtime_func is None:
-            auto_reload = False
-        CachedLoaderMixin.__init__(self, use_memcache, memcache_size,
-                                   cache_folder, auto_reload, cache_salt)
-
-    def check_source_changed(self, environment, name):
-        return self.getmtime_func(name)
-
-
-class DictLoader(BaseLoader):
-    """
-    Load templates from a given dict:
-
-    .. sourcecode:: python
-
-        from jinja import Environment, DictLoader
-        e = Environment(loader=DictLoader(dict(
-            layout='...',
-            index='{% extends 'layout' %}...'
-        )))
-
-    This loader does not have any caching capabilities.
-    """
-
-    def __init__(self, templates):
-        self.templates = templates
-
-    def get_source(self, environment, name, parent):
-        if name in self.templates:
-            return self.templates[name]
-        raise TemplateNotFound(name)
-
-
-class ChoiceLoader(object):
-    """
-    A loader that tries multiple loaders in the order they are given to
-    the `ChoiceLoader`:
-
-    .. sourcecode:: python
-
-        from jinja import ChoiceLoader, FileSystemLoader
-        loader1 = FileSystemLoader("templates1")
-        loader2 = FileSystemLoader("templates2")
-        loader = ChoiceLoader([loader1, loader2])
-    """
-
-    def __init__(self, loaders):
-        self.loaders = list(loaders)
-
-    def get_source(self, environment, name, parent):
-        for loader in self.loaders:
-            try:
-                return loader.get_source(environment, name, parent)
-            except TemplateNotFound, e:
-                if e.name != name:
-                    raise
-                continue
-        raise TemplateNotFound(name)
-
-    def parse(self, environment, name, parent):
-        for loader in self.loaders:
-            try:
-                return loader.parse(environment, name, parent)
-            except TemplateNotFound, e:
-                if e.name != name:
-                    raise
-                continue
-        raise TemplateNotFound(name)
-
-    def load(self, environment, name, translator):
-        for loader in self.loaders:
-            try:
-                return loader.load(environment, name, translator)
-            except TemplateNotFound, e:
-                if e.name != name:
-                    raise
-                continue
-        raise TemplateNotFound(name)
diff --git a/jinja/nodes.py b/jinja/nodes.py
deleted file mode 100644 (file)
index 9dd4584..0000000
+++ /dev/null
@@ -1,792 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    jinja.nodes
-    ~~~~~~~~~~~
-
-    This module implements additional nodes derived from the ast base node.
-
-    It also provides some node tree helper functions like `in_lineno` and
-    `get_nodes` used by the parser and translator in order to normalize
-    python and jinja nodes.
-
-    :copyright: 2007 by Armin Ronacher.
-    :license: BSD, see LICENSE for more details.
-"""
-from itertools import chain
-from copy import copy
-
-
-def get_nodes(nodetype, tree, exclude_root=True):
-    """
-    Get all nodes from nodetype in the tree excluding the
-    node passed if `exclude_root` is `True` (default).
-    """
-    if exclude_root:
-        todo = tree.get_child_nodes()
-    else:
-        todo = [tree]
-    while todo:
-        node = todo.pop()
-        if node.__class__ is nodetype:
-            yield node
-        todo.extend(node.get_child_nodes())
-
-
-class NotPossible(NotImplementedError):
-    """
-    If a given node cannot do something.
-    """
-
-
-class Node(object):
-    """
-    Jinja node.
-    """
-
-    def __init__(self, lineno=None, filename=None):
-        self.lineno = lineno
-        self.filename = filename
-
-    def get_items(self):
-        return []
-
-    def get_child_nodes(self):
-        return [x for x in self.get_items() if isinstance(x, Node)]
-
-    def allows_assignments(self):
-        return False
-
-    def __repr__(self):
-        return 'Node()'
-
-
-class Text(Node):
-    """
-    Node that represents normal text.
-    """
-
-    def __init__(self, text, variables, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.text = text
-        self.variables = variables
-
-    def get_items(self):
-        return [self.text] + list(self.variables)
-
-    def __repr__(self):
-        return 'Text(%r, %r)' % (
-            self.text,
-            self.variables
-        )
-
-
-class NodeList(list, Node):
-    """
-    A node that stores multiple childnodes.
-    """
-
-    def __init__(self, data, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        list.__init__(self, data)
-
-    def get_items(self):
-        return list(self)
-
-    def __repr__(self):
-        return 'NodeList(%s)' % list.__repr__(self)
-
-
-class Template(Node):
-    """
-    Node that represents a template.
-    """
-
-    def __init__(self, extends, body, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.extends = extends
-        self.body = body
-
-    def get_items(self):
-        return [self.extends, self.body]
-
-    def __repr__(self):
-        return 'Template(%r, %r)' % (
-            self.extends,
-            self.body
-        )
-
-
-class ForLoop(Node):
-    """
-    A node that represents a for loop
-    """
-
-    def __init__(self, item, seq, body, else_, recursive, lineno=None,
-                 filename=None):
-        Node.__init__(self, lineno, filename)
-        self.item = item
-        self.seq = seq
-        self.body = body
-        self.else_ = else_
-        self.recursive = recursive
-
-    def get_items(self):
-        return [self.item, self.seq, self.body, self.else_, self.recursive]
-
-    def __repr__(self):
-        return 'ForLoop(%r, %r, %r, %r, %r)' % (
-            self.item,
-            self.seq,
-            self.body,
-            self.else_,
-            self.recursive
-        )
-
-
-class IfCondition(Node):
-    """
-    A node that represents an if condition.
-    """
-
-    def __init__(self, tests, else_, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.tests = tests
-        self.else_ = else_
-
-    def get_items(self):
-        result = []
-        for test in self.tests:
-            result.extend(test)
-        result.append(self.else_)
-        return result
-
-    def __repr__(self):
-        return 'IfCondition(%r, %r)' % (
-            self.tests,
-            self.else_
-        )
-
-
-class Cycle(Node):
-    """
-    A node that represents the cycle statement.
-    """
-
-    def __init__(self, seq, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.seq = seq
-
-    def get_items(self):
-        return [self.seq]
-
-    def __repr__(self):
-        return 'Cycle(%r)' % (self.seq,)
-
-
-class Print(Node):
-    """
-    A node that represents variable tags and print calls.
-    """
-
-    def __init__(self, expr, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.expr = expr
-
-    def get_items(self):
-        return [self.expr]
-
-    def __repr__(self):
-        return 'Print(%r)' % (self.expr,)
-
-
-class Macro(Node):
-    """
-    A node that represents a macro.
-    """
-
-    def __init__(self, name, arguments, body, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.name = name
-        self.arguments = arguments
-        self.body = body
-
-    def get_items(self):
-        return [self.name] + list(chain(*self.arguments)) + [self.body]
-
-    def __repr__(self):
-        return 'Macro(%r, %r, %r)' % (
-            self.name,
-            self.arguments,
-            self.body
-        )
-
-
-class Call(Node):
-    """
-    A node that represents am extended macro call.
-    """
-
-    def __init__(self, expr, body, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.expr = expr
-        self.body = body
-
-    def get_items(self):
-        return [self.expr, self.body]
-
-    def __repr__(self):
-        return 'Call(%r, %r)' % (
-            self.expr,
-            self.body
-        )
-
-
-class Set(Node):
-    """
-    Allows defining own variables.
-    """
-
-    def __init__(self, name, expr, scope_local, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.name = name
-        self.expr = expr
-        self.scope_local = scope_local
-
-    def get_items(self):
-        return [self.name, self.expr, self.scope_local]
-
-    def __repr__(self):
-        return 'Set(%r, %r, %r)' % (
-            self.name,
-            self.expr,
-            self.scope_local
-        )
-
-
-class Filter(Node):
-    """
-    Node for filter sections.
-    """
-
-    def __init__(self, body, filters, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.body = body
-        self.filters = filters
-
-    def get_items(self):
-        return [self.body] + list(self.filters)
-
-    def __repr__(self):
-        return 'Filter(%r, %r)' % (
-            self.body,
-            self.filters
-        )
-
-
-class Block(Node):
-    """
-    A node that represents a block.
-    """
-
-    def __init__(self, name, body, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.name = name
-        self.body = body
-
-    def replace(self, node):
-        """
-        Replace the current data with the copied data of another block
-        node.
-        """
-        assert node.__class__ is Block
-        self.lineno = node.lineno
-        self.filename = node.filename
-        self.name = node.name
-        self.body = copy(node.body)
-
-    def clone(self):
-        """
-        Create an independent clone of this node.
-        """
-        return copy(self)
-
-    def get_items(self):
-        return [self.name, self.body]
-
-    def __repr__(self):
-        return 'Block(%r, %r)' % (
-            self.name,
-            self.body
-        )
-
-
-class Include(Node):
-    """
-    A node that represents the include tag.
-    """
-
-    def __init__(self, template, lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.template = template
-
-    def get_items(self):
-        return [self.template]
-
-    def __repr__(self):
-        return 'Include(%r)' % (
-            self.template
-        )
-
-
-class Trans(Node):
-    """
-    A node for translatable sections.
-    """
-
-    def __init__(self, singular, plural, indicator, replacements,
-                 lineno=None, filename=None):
-        Node.__init__(self, lineno, filename)
-        self.singular = singular
-        self.plural = plural
-        self.indicator = indicator
-        self.replacements = replacements
-
-    def get_items(self):
-        rv = [self.singular, self.plural, self.indicator]
-        if self.replacements:
-            rv.extend(self.replacements.values())
-            rv.extend(self.replacements.keys())
-        return rv
-
-    def __repr__(self):
-        return 'Trans(%r, %r, %r, %r)' % (
-            self.singular,
-            self.plural,
-            self.indicator,
-            self.replacements
-        )
-
-
-class Expression(Node):
-    """
-    Baseclass for all expressions.
-    """
-
-
-class BinaryExpression(Expression):
-    """
-    Baseclass for all binary expressions.
-    """
-
-    def __init__(self, left, right, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.left = left
-        self.right = right
-
-    def get_items(self):
-        return [self.left, self.right]
-
-    def __repr__(self):
-        return '%s(%r, %r)' % (
-            self.__class__.__name__,
-            self.left,
-            self.right
-        )
-
-
-class UnaryExpression(Expression):
-    """
-    Baseclass for all unary expressions.
-    """
-
-    def __init__(self, node, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.node = node
-
-    def get_items(self):
-        return [self.node]
-
-    def __repr__(self):
-        return '%s(%r)' % (
-            self.__class__.__name__,
-            self.node
-        )
-
-
-class ConstantExpression(Expression):
-    """
-    any constat such as {{ "foo" }}
-    """
-
-    def __init__(self, value, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.value = value
-
-    def get_items(self):
-        return [self.value]
-
-    def __repr__(self):
-        return 'ConstantExpression(%r)' % (self.value,)
-
-
-class UndefinedExpression(Expression):
-    """
-    represents the special 'undefined' value.
-    """
-
-    def __repr__(self):
-        return 'UndefinedExpression()'
-
-
-class RegexExpression(Expression):
-    """
-    represents the regular expression literal.
-    """
-
-    def __init__(self, value, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.value = value
-
-    def get_items(self):
-        return [self.value]
-
-    def __repr__(self):
-        return 'RegexExpression(%r)' % (self.value,)
-
-
-class NameExpression(Expression):
-    """
-    any name such as {{ foo }}
-    """
-
-    def __init__(self, name, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.name = name
-
-    def get_items(self):
-        return [self.name]
-
-    def allows_assignments(self):
-        return self.name != '_'
-
-    def __repr__(self):
-        return 'NameExpression(%r)' % self.name
-
-
-class ListExpression(Expression):
-    """
-    any list literal such as {{ [1, 2, 3] }}
-    """
-
-    def __init__(self, items, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.items = items
-
-    def get_items(self):
-        return list(self.items)
-
-    def __repr__(self):
-        return 'ListExpression(%r)' % (self.items,)
-
-
-class DictExpression(Expression):
-    """
-    any dict literal such as {{ {1: 2, 3: 4} }}
-    """
-
-    def __init__(self, items, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.items = items
-
-    def get_items(self):
-        return list(chain(*self.items))
-
-    def __repr__(self):
-        return 'DictExpression(%r)' % (self.items,)
-
-
-class SetExpression(Expression):
-    """
-    any set literal such as {{ @(1, 2, 3) }}
-    """
-
-    def __init__(self, items, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.items = items
-
-    def get_items(self):
-        return self.items[:]
-
-    def __repr__(self):
-        return 'SetExpression(%r)' % (self.items,)
-
-
-class ConditionalExpression(Expression):
-    """
-    {{ foo if bar else baz }}
-    """
-
-    def __init__(self, test, expr1, expr2, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.test = test
-        self.expr1 = expr1
-        self.expr2 = expr2
-
-    def get_items(self):
-        return [self.test, self.expr1, self.expr2]
-
-    def __repr__(self):
-        return 'ConstantExpression(%r, %r, %r)' % (
-            self.test,
-            self.expr1,
-            self.expr2
-        )
-
-
-class FilterExpression(Expression):
-    """
-    {{ foo|bar|baz }}
-    """
-
-    def __init__(self, node, filters, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.node = node
-        self.filters = filters
-
-    def get_items(self):
-        result = [self.node]
-        for filter, args in self.filters:
-            result.append(filter)
-            result.extend(args)
-        return result
-
-    def __repr__(self):
-        return 'FilterExpression(%r, %r)' % (
-            self.node,
-            self.filters
-        )
-
-
-class TestExpression(Expression):
-    """
-    {{ foo is lower }}
-    """
-
-    def __init__(self, node, name, args, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.node = node
-        self.name = name
-        self.args = args
-
-    def get_items(self):
-        return [self.node, self.name] + list(self.args)
-
-    def __repr__(self):
-        return 'TestExpression(%r, %r, %r)' % (
-            self.node,
-            self.name,
-            self.args
-        )
-
-
-class CallExpression(Expression):
-    """
-    {{ foo(bar) }}
-    """
-
-    def __init__(self, node, args, kwargs, dyn_args, dyn_kwargs,
-                 lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.node = node
-        self.args = args
-        self.kwargs = kwargs
-        self.dyn_args = dyn_args
-        self.dyn_kwargs = dyn_kwargs
-
-    def get_items(self):
-        return [self.node, self.args, self.kwargs, self.dyn_args,
-                self.dyn_kwargs]
-
-    def __repr__(self):
-        return 'CallExpression(%r, %r, %r, %r, %r)' % (
-            self.node,
-            self.args,
-            self.kwargs,
-            self.dyn_args,
-            self.dyn_kwargs
-        )
-
-
-class SubscriptExpression(Expression):
-    """
-    {{ foo.bar }} and {{ foo['bar'] }} etc.
-    """
-
-    def __init__(self, node, arg, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.node = node
-        self.arg = arg
-
-    def get_items(self):
-        return [self.node, self.arg]
-
-    def __repr__(self):
-        return 'SubscriptExpression(%r, %r)' % (
-            self.node,
-            self.arg
-        )
-
-
-class SliceExpression(Expression):
-    """
-    1:2:3 etc.
-    """
-
-    def __init__(self, start, stop, step, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.start = start
-        self.stop = stop
-        self.step = step
-
-    def get_items(self):
-        return [self.start, self.stop, self.step]
-
-    def __repr__(self):
-        return 'SliceExpression(%r, %r, %r)' % (
-            self.start,
-            self.stop,
-            self.step
-        )
-
-
-class TupleExpression(Expression):
-    """
-    For loop unpacking and some other things like multiple arguments
-    for subscripts.
-    """
-
-    def __init__(self, items, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.items = items
-
-    def get_items(self):
-        return list(self.items)
-
-    def allows_assignments(self):
-        for item in self.items:
-            if not item.allows_assignments():
-                return False
-        return True
-
-    def __repr__(self):
-        return 'TupleExpression(%r)' % (self.items,)
-
-
-class ConcatExpression(Expression):
-    """
-    For {{ foo ~ bar }}. Because of various reasons (especially because
-    unicode conversion takes place for the left and right expression and
-    is better optimized that way)
-    """
-
-    def __init__(self, args, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.args = args
-
-    def get_items(self):
-        return list(self.args)
-
-    def __repr__(self):
-        return 'ConcatExpression(%r)' % (self.items,)
-
-
-class CompareExpression(Expression):
-    """
-    {{ foo == bar }}, {{ foo >= bar }} etc.
-    """
-
-    def __init__(self, expr, ops, lineno=None, filename=None):
-        Expression.__init__(self, lineno, filename)
-        self.expr = expr
-        self.ops = ops
-
-    def get_items(self):
-        return [self.expr] + list(chain(*self.ops))
-
-    def __repr__(self):
-        return 'CompareExpression(%r, %r)' % (
-            self.expr,
-            self.ops
-        )
-
-
-class MulExpression(BinaryExpression):
-    """
-    {{ foo * bar }}
-    """
-
-
-class DivExpression(BinaryExpression):
-    """
-    {{ foo / bar }}
-    """
-
-
-class FloorDivExpression(BinaryExpression):
-    """
-    {{ foo // bar }}
-    """
-
-
-class AddExpression(BinaryExpression):
-    """
-    {{ foo + bar }}
-    """
-
-
-class SubExpression(BinaryExpression):
-    """
-    {{ foo - bar }}
-    """
-
-
-class ModExpression(BinaryExpression):
-    """
-    {{ foo % bar }}
-    """
-
-
-class PowExpression(BinaryExpression):
-    """
-    {{ foo ** bar }}
-    """
-
-
-class AndExpression(BinaryExpression):
-    """
-    {{ foo and bar }}
-    """
-
-
-class OrExpression(BinaryExpression):
-    """
-    {{ foo or bar }}
-    """
-
-
-class NotExpression(UnaryExpression):
-    """
-    {{ not foo }}
-    """
-
-
-class NegExpression(UnaryExpression):
-    """
-    {{ -foo }}
-    """
-
-
-class PosExpression(UnaryExpression):
-    """
-    {{ +foo }}
-    """
diff --git a/jinja/parser.py b/jinja/parser.py
deleted file mode 100644 (file)
index ba9317a..0000000
+++ /dev/null
@@ -1,1187 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    jinja.parser
-    ~~~~~~~~~~~~
-
-    Implements the template parser.
-
-    The Jinja template parser is not a real parser but a combination of the
-    python compiler package and some postprocessing. The tokens yielded by
-    the lexer are used to separate template data and expressions. The
-    expression tokens are then converted into strings again and processed
-    by the python parser.
-
-    :copyright: 2007 by Armin Ronacher.
-    :license: BSD, see LICENSE for more details.
-"""
-from jinja import nodes
-from jinja.datastructure import StateTest
-from jinja.exceptions import TemplateSyntaxError
-from jinja.utils import set
-
-
-__all__ = ['Parser']
-
-
-# general callback functions for the parser
-end_of_block = StateTest.expect_token('block_end',
-                                      msg='expected end of block tag')
-end_of_variable = StateTest.expect_token('variable_end',
-                                         msg='expected end of variable')
-end_of_comment = StateTest.expect_token('comment_end',
-                                        msg='expected end of comment')
-
-# internal tag callbacks
-switch_for = StateTest.expect_token('else', 'endfor')
-end_of_for = StateTest.expect_token('endfor')
-switch_if = StateTest.expect_token('else', 'elif', 'endif')
-end_of_if = StateTest.expect_token('endif')
-end_of_filter = StateTest.expect_token('endfilter')
-end_of_macro = StateTest.expect_token('endmacro')
-end_of_call = StateTest.expect_token('endcall')
-end_of_block_tag = StateTest.expect_token('endblock')
-end_of_trans = StateTest.expect_token('endtrans')
-
-# this ends a tuple
-tuple_edge_tokens = set(['rparen', 'block_end', 'variable_end', 'in',
-                         'recursive'])
-
-
-class Parser(object):
-    """
-    The template parser class.
-
-    Transforms sourcecode into an abstract syntax tree.
-    """
-
-    def __init__(self, environment, source, filename=None):
-        self.environment = environment
-        if isinstance(source, str):
-            source = source.decode(environment.template_charset, 'ignore')
-        if isinstance(filename, unicode):
-            filename = filename.encode('utf-8')
-        self.source = source
-        self.filename = filename
-        self.closed = False
-
-        #: set for blocks in order to keep them unique
-        self.blocks = set()
-
-        #: mapping of directives that require special treatment
-        self.directives = {
-            # "fake" directives that just trigger errors
-            'raw':          self.parse_raw_directive,
-            'extends':      self.parse_extends_directive,
-
-            # real directives
-            'for':          self.parse_for_loop,
-            'if':           self.parse_if_condition,
-            'cycle':        self.parse_cycle_directive,
-            'call':         self.parse_call_directive,
-            'set':          self.parse_set_directive,
-            'filter':       self.parse_filter_directive,
-            'print':        self.parse_print_directive,
-            'macro':        self.parse_macro_directive,
-            'block':        self.parse_block_directive,
-            'include':      self.parse_include_directive,
-            'trans':        self.parse_trans_directive
-        }
-
-        #: set of directives that are only available in a certain
-        #: context.
-        self.context_directives = set([
-            'elif', 'else', 'endblock', 'endfilter', 'endfor', 'endif',
-            'endmacro', 'endraw', 'endtrans', 'pluralize'
-        ])
-
-        #: get the `no_variable_block` flag
-        self.no_variable_block = self.environment.lexer.no_variable_block
-
-        self.stream = environment.lexer.tokenize(source, filename)
-
-    def parse_raw_directive(self):
-        """
-        Handle fake raw directive. (real raw directives are handled by
-        the lexer. But if there are arguments to raw or the end tag
-        is missing the parser tries to resolve this directive. In that
-        case present the user a useful error message.
-        """
-        if self.stream:
-            raise TemplateSyntaxError('raw directive does not support '
-                                      'any arguments.', self.stream.lineno,
-                                      self.filename)
-        raise TemplateSyntaxError('missing end tag for raw directive.',
-                                  self.stream.lineno, self.filename)
-
-    def parse_extends_directive(self):
-        """
-        Handle the extends directive used for inheritance.
-        """
-        raise TemplateSyntaxError('mispositioned extends tag. extends must '
-                                  'be the first tag of a template.',
-                                  self.stream.lineno, self.filename)
-
-    def parse_for_loop(self):
-        """
-        Handle a for directive and return a ForLoop node
-        """
-        token = self.stream.expect('for')
-        item = self.parse_tuple_expression(simplified=True)
-        if not item.allows_assignments():
-            raise TemplateSyntaxError('cannot assign to expression',
-                                      token.lineno, self.filename)
-
-        self.stream.expect('in')
-        seq = self.parse_tuple_expression()
-        if self.stream.current.type == 'recursive':
-            self.stream.next()
-            recursive = True
-        else:
-            recursive = False
-        self.stream.expect('block_end')
-
-        body = self.subparse(switch_for)
-
-        # do we have an else section?
-        if self.stream.current.type == 'else':
-            self.stream.next()
-            self.stream.expect('block_end')
-            else_ = self.subparse(end_of_for, True)
-        else:
-            self.stream.next()
-            else_ = None
-        self.stream.expect('block_end')
-
-        return nodes.ForLoop(item, seq, body, else_, recursive,
-                             token.lineno, self.filename)
-
-    def parse_if_condition(self):
-        """
-        Handle if/else blocks.
-        """
-        token = self.stream.expect('if')
-        expr = self.parse_expression()
-        self.stream.expect('block_end')
-        tests = [(expr, self.subparse(switch_if))]
-        else_ = None
-
-        # do we have an else section?
-        while True:
-            if self.stream.current.type == 'else':
-                self.stream.next()
-                self.stream.expect('block_end')
-                else_ = self.subparse(end_of_if, True)
-            elif self.stream.current.type == 'elif':
-                self.stream.next()
-                expr = self.parse_expression()
-                self.stream.expect('block_end')
-                tests.append((expr, self.subparse(switch_if)))
-                continue
-            else:
-                self.stream.next()
-            break
-        self.stream.expect('block_end')
-
-        return nodes.IfCondition(tests, else_, token.lineno, self.filename)
-
-    def parse_cycle_directive(self):
-        """
-        Handle {% cycle foo, bar, baz %}.
-        """
-        token = self.stream.expect('cycle')
-        expr = self.parse_tuple_expression()
-        self.stream.expect('block_end')
-        return nodes.Cycle(expr, token.lineno, self.filename)
-
-    def parse_set_directive(self):
-        """
-        Handle {% set foo = 'value of foo' %}.
-        """
-        token = self.stream.expect('set')
-        name = self.stream.expect('name')
-        self.test_name(name.value)
-        self.stream.expect('assign')
-        value = self.parse_expression()
-        if self.stream.current.type == 'bang':
-            self.stream.next()
-            scope_local = False
-        else:
-            scope_local = True
-        self.stream.expect('block_end')
-        return nodes.Set(name.value, value, scope_local,
-                         token.lineno, self.filename)
-
-    def parse_filter_directive(self):
-        """
-        Handle {% filter foo|bar %} directives.
-        """
-        token = self.stream.expect('filter')
-        filters = []
-        while self.stream.current.type != 'block_end':
-            if filters:
-                self.stream.expect('pipe')
-            token = self.stream.expect('name')
-            args = []
-            if self.stream.current.type == 'lparen':
-                self.stream.next()
-                while self.stream.current.type != 'rparen':
-                    if args:
-                        self.stream.expect('comma')
-                    args.append(self.parse_expression())
-                self.stream.expect('rparen')
-            filters.append((token.value, args))
-        self.stream.expect('block_end')
-        body = self.subparse(end_of_filter, True)
-        self.stream.expect('block_end')
-        return nodes.Filter(body, filters, token.lineno, self.filename)
-
-    def parse_print_directive(self):
-        """
-        Handle {% print foo %}.
-        """
-        token = self.stream.expect('print')
-        expr = self.parse_tuple_expression()
-        node = nodes.Print(expr, token.lineno, self.filename)
-        self.stream.expect('block_end')
-        return node
-
-    def parse_macro_directive(self):
-        """
-        Handle {% macro foo bar, baz %} as well as
-        {% macro foo(bar, baz) %}.
-        """
-        token = self.stream.expect('macro')
-        macro_name = self.stream.expect('name')
-        self.test_name(macro_name.value)
-        if self.stream.current.type == 'lparen':
-            self.stream.next()
-            needle_token = 'rparen'
-        else:
-            needle_token = 'block_end'
-
-        args = []
-        while self.stream.current.type != needle_token:
-            if args:
-                self.stream.expect('comma')
-            name = self.stream.expect('name').value
-            self.test_name(name)
-            if self.stream.current.type == 'assign':
-                self.stream.next()
-                default = self.parse_expression()
-            else:
-                default = None
-            args.append((name, default))
-
-        self.stream.next()
-        if needle_token == 'rparen':
-            self.stream.expect('block_end')
-
-        body = self.subparse(end_of_macro, True)
-        self.stream.expect('block_end')
-
-        return nodes.Macro(macro_name.value, args, body, token.lineno,
-                           self.filename)
-
-    def parse_call_directive(self):
-        """
-        Handle {% call foo() %}...{% endcall %}
-        """
-        token = self.stream.expect('call')
-        expr = self.parse_call_expression()
-        self.stream.expect('block_end')
-        body = self.subparse(end_of_call, True)
-        self.stream.expect('block_end')
-        return nodes.Call(expr, body, token.lineno, self.filename)
-
-    def parse_block_directive(self):
-        """
-        Handle block directives used for inheritance.
-        """
-        token = self.stream.expect('block')
-        name = self.stream.expect('name').value
-
-        # check if this block does not exist by now.
-        if name in self.blocks:
-            raise TemplateSyntaxError('block %r defined twice' %
-                                       name, token.lineno,
-                                       self.filename)
-        self.blocks.add(name)
-
-        if self.stream.current.type != 'block_end':
-            lineno = self.stream.lineno
-            expr = self.parse_tuple_expression()
-            node = nodes.Print(expr, lineno, self.filename)
-            body = nodes.NodeList([node], lineno, self.filename)
-            self.stream.expect('block_end')
-        else:
-            # otherwise parse the body and attach it to the block
-            self.stream.expect('block_end')
-            body = self.subparse(end_of_block_tag, True)
-            self.stream.expect('block_end')
-        return nodes.Block(name, body, token.lineno, self.filename)
-
-    def parse_include_directive(self):
-        """
-        Handle the include directive used for template inclusion.
-        """
-        token = self.stream.expect('include')
-        template = self.stream.expect('string').value
-        self.stream.expect('block_end')
-        return nodes.Include(template, token.lineno, self.filename)
-
-    def parse_trans_directive(self):
-        """
-        Handle translatable sections.
-        """
-        trans_token = self.stream.expect('trans')
-
-        # string based translations {% trans "foo" %}
-        if self.stream.current.type == 'string':
-            text = self.stream.expect('string')
-            self.stream.expect('block_end')
-            return nodes.Trans(text.value, None, None, None,
-                               trans_token.lineno, self.filename)
-
-        # block based translations
-        replacements = {}
-        plural_var = None
-
-        while self.stream.current.type != 'block_end':
-            if replacements:
-                self.stream.expect('comma')
-            name = self.stream.expect('name')
-            if self.stream.current.type == 'assign':
-                self.stream.next()
-                value = self.parse_expression()
-            else:
-                value = nodes.NameExpression(name.value, name.lineno,
-                                             self.filename)
-            if name.value in replacements:
-                raise TemplateSyntaxError('translation variable %r '
-                                          'is defined twice' % name.value,
-                                          name.lineno, self.filename)
-            replacements[name.value] = value
-            if plural_var is None:
-                plural_var = name.value
-        self.stream.expect('block_end')
-
-        def process_variable():
-            var_name = self.stream.expect('name')
-            if var_name.value not in replacements:
-                raise TemplateSyntaxError('unregistered translation variable'
-                                          " '%s'." % var_name.value,
-                                          var_name.lineno, self.filename)
-            buf.append('%%(%s)s' % var_name.value)
-
-        buf = singular = []
-        plural = None
-
-        while True:
-            token = self.stream.current
-            if token.type == 'data':
-                buf.append(token.value.replace('%', '%%'))
-                self.stream.next()
-            elif token.type == 'variable_begin':
-                self.stream.next()
-                process_variable()
-                self.stream.expect('variable_end')
-            elif token.type == 'block_begin':
-                self.stream.next()
-                if plural is None and self.stream.current.type == 'pluralize':
-                    self.stream.next()
-                    if self.stream.current.type == 'name':
-                        plural_var = self.stream.expect('name').value
-                    plural = buf = []
-                elif self.stream.current.type == 'endtrans':
-                    self.stream.next()
-                    self.stream.expect('block_end')
-                    break
-                else:
-                    if self.no_variable_block:
-                        process_variable()
-                    else:
-                        raise TemplateSyntaxError('blocks are not allowed '
-                                                  'in trans tags',
-                                                  self.stream.lineno,
-                                                  self.filename)
-                self.stream.expect('block_end')
-            else:
-                assert False, 'something very strange happened'
-
-        singular = u''.join(singular)
-        if plural is not None:
-            plural = u''.join(plural)
-        return nodes.Trans(singular, plural, plural_var, replacements,
-                           trans_token.lineno, self.filename)
-
-    def parse_expression(self):
-        """
-        Parse one expression from the stream.
-        """
-        return self.parse_conditional_expression()
-
-    def parse_subscribed_expression(self):
-        """
-        Like parse_expression but parses slices too. Because this
-        parsing function requires a border the two tokens rbracket
-        and comma mark the end of the expression in some situations.
-        """
-        lineno = self.stream.lineno
-
-        if self.stream.current.type == 'colon':
-            self.stream.next()
-            args = [None]
-        else:
-            node = self.parse_expression()
-            if self.stream.current.type != 'colon':
-                return node
-            self.stream.next()
-            args = [node]
-
-        if self.stream.current.type == 'colon':
-            args.append(None)
-        elif self.stream.current.type not in ('rbracket', 'comma'):
-            args.append(self.parse_expression())
-        else:
-            args.append(None)
-
-        if self.stream.current.type == 'colon':
-            self.stream.next()
-            if self.stream.current.type not in ('rbracket', 'comma'):
-                args.append(self.parse_expression())
-            else:
-                args.append(None)
-        else:
-            args.append(None)
-
-        return nodes.SliceExpression(*(args + [lineno, self.filename]))
-
-    def parse_conditional_expression(self):
-        """
-        Parse a conditional expression (foo if bar else baz)
-        """
-        lineno = self.stream.lineno
-        expr1 = self.parse_or_expression()
-        while self.stream.current.type == 'if':
-            self.stream.next()
-            expr2 = self.parse_or_expression()
-            self.stream.expect('else')
-            expr3 = self.parse_conditional_expression()
-            expr1 = nodes.ConditionalExpression(expr2, expr1, expr3,
-                                                lineno, self.filename)
-            lineno = self.stream.lineno
-        return expr1
-
-    def parse_or_expression(self):
-        """
-        Parse something like {{ foo or bar }}.
-        """
-        lineno = self.stream.lineno
-        left = self.parse_and_expression()
-        while self.stream.current.type == 'or':
-            self.stream.next()
-            right = self.parse_and_expression()
-            left = nodes.OrExpression(left, right, lineno, self.filename)
-            lineno = self.stream.lineno
-        return left
-
-    def parse_and_expression(self):
-        """
-        Parse something like {{ foo and bar }}.
-        """
-        lineno = self.stream.lineno
-        left = self.parse_compare_expression()
-        while self.stream.current.type == 'and':
-            self.stream.next()
-            right = self.parse_compare_expression()
-            left = nodes.AndExpression(left, right, lineno, self.filename)
-            lineno = self.stream.lineno
-        return left
-
-    def parse_compare_expression(self):
-        """
-        Parse something like {{ foo == bar }}.
-        """
-        known_operators = set(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq', 'in'])
-        lineno = self.stream.lineno
-        expr = self.parse_add_expression()
-        ops = []
-        while True:
-            if self.stream.current.type in known_operators:
-                op = self.stream.current.type
-                self.stream.next()
-                ops.append([op, self.parse_add_expression()])
-            elif self.stream.current.type == 'not' and \
-                 self.stream.look().type == 'in':
-                self.stream.skip(2)
-                ops.append(['not in', self.parse_add_expression()])
-            else:
-                break
-        if not ops:
-            return expr
-        return nodes.CompareExpression(expr, ops, lineno, self.filename)
-
-    def parse_add_expression(self):
-        """
-        Parse something like {{ foo + bar }}.
-        """
-        lineno = self.stream.lineno
-        left = self.parse_sub_expression()
-        while self.stream.current.type == 'add':
-            self.stream.next()
-            right = self.parse_sub_expression()
-            left = nodes.AddExpression(left, right, lineno, self.filename)
-            lineno = self.stream.lineno
-        return left
-
-    def parse_sub_expression(self):
-        """
-        Parse something like {{ foo - bar }}.
-        """
-        lineno = self.stream.lineno
-        left = self.parse_concat_expression()
-        while self.stream.current.type == 'sub':
-            self.stream.next()
-            right = self.parse_concat_expression()
-            left = nodes.SubExpression(left, right, lineno, self.filename)
-            lineno = self.stream.lineno
-        return left
-
-    def parse_concat_expression(self):
-        """
-        Parse something like {{ foo ~ bar }}.
-        """
-        lineno = self.stream.lineno
-        args = [self.parse_mul_expression()]
-        while self.stream.current.type == 'tilde':
-            self.stream.next()
-            args.append(self.parse_mul_expression())
-        if len(args) == 1:
-            return args[0]
-        return nodes.ConcatExpression(args, lineno, self.filename)
-
-    def parse_mul_expression(self):
-        """
-        Parse something like {{ foo * bar }}.
-        """
-        lineno = self.stream.lineno
-        left = self.parse_div_expression()
-        while self.stream.current.type == 'mul':
-            self.stream.next()
-            right = self.parse_div_expression()
-            left = nodes.MulExpression(left, right, lineno, self.filename)
-            lineno = self.stream.lineno
-        return left
-
-    def parse_div_expression(self):
-        """
-        Parse something like {{ foo / bar }}.
-        """
-        lineno = self.stream.lineno
-        left = self.parse_floor_div_expression()
-        while self.stream.current.type == 'div':
-            self.stream.next()
-            right = self.parse_floor_div_expression()
-            left = nodes.DivExpression(left, right, lineno, self.filename)
-            lineno = self.stream.lineno
-        return left
-
-    def parse_floor_div_expression(self):
-        """
-        Parse something like {{ foo // bar }}.
-        """
-        lineno = self.stream.lineno
-        left = self.parse_mod_expression()
-        while self.stream.current.type == 'floordiv':
-            self.stream.next()
-            right = self.parse_mod_expression()
-            left = nodes.FloorDivExpression(left, right, lineno, self.filename)
-            lineno = self.stream.lineno
-        return left
-
-    def parse_mod_expression(self):
-        """
-        Parse something like {{ foo % bar }}.
-        """
-        lineno = self.stream.lineno
-        left = self.parse_pow_expression()
-        while self.stream.current.type == 'mod':
-            self.stream.next()
-            right = self.parse_pow_expression()
-            left = nodes.ModExpression(left, right, lineno, self.filename)
-            lineno = self.stream.lineno
-        return left
-
-    def parse_pow_expression(self):
-        """
-        Parse something like {{ foo ** bar }}.
-        """
-        lineno = self.stream.lineno
-        left = self.parse_unary_expression()
-        while self.stream.current.type == 'pow':
-            self.stream.next()
-            right = self.parse_unary_expression()
-            left = nodes.PowExpression(left, right, lineno, self.filename)
-            lineno = self.stream.lineno
-        return left
-
-    def parse_unary_expression(self):
-        """
-        Parse all kinds of unary expressions.
-        """
-        if self.stream.current.type == 'not':
-            return self.parse_not_expression()
-        elif self.stream.current.type == 'sub':
-            return self.parse_neg_expression()
-        elif self.stream.current.type == 'add':
-            return self.parse_pos_expression()
-        return self.parse_primary_expression()
-
-    def parse_not_expression(self):
-        """
-        Parse something like {{ not foo }}.
-        """
-        token = self.stream.expect('not')
-        node = self.parse_unary_expression()
-        return nodes.NotExpression(node, token.lineno, self.filename)
-
-    def parse_neg_expression(self):
-        """
-        Parse something like {{ -foo }}.
-        """
-        token = self.stream.expect('sub')
-        node = self.parse_unary_expression()
-        return nodes.NegExpression(node, token.lineno, self.filename)
-
-    def parse_pos_expression(self):
-        """
-        Parse something like {{ +foo }}.
-        """
-        token = self.stream.expect('add')
-        node = self.parse_unary_expression()
-        return nodes.PosExpression(node, token.lineno, self.filename)
-
-    def parse_primary_expression(self, parse_postfix=True):
-        """
-        Parse a primary expression such as a name or literal.
-        """
-        current = self.stream.current
-        if current.type == 'name':
-            if current.value in ('true', 'false'):
-                node = self.parse_bool_expression()
-            elif current.value == 'none':
-                node = self.parse_none_expression()
-            elif current.value == 'undefined':
-                node = self.parse_undefined_expression()
-            elif current.value == '_':
-                node = self.parse_gettext_call()
-            else:
-                node = self.parse_name_expression()
-        elif current.type in ('integer', 'float'):
-            node = self.parse_number_expression()
-        elif current.type == 'string':
-            node = self.parse_string_expression()
-        elif current.type == 'regex':
-            node = self.parse_regex_expression()
-        elif current.type == 'lparen':
-            node = self.parse_paren_expression()
-        elif current.type == 'lbracket':
-            node = self.parse_list_expression()
-        elif current.type == 'lbrace':
-            node = self.parse_dict_expression()
-        elif current.type == 'at':
-            node = self.parse_set_expression()
-        else:
-            raise TemplateSyntaxError("unexpected token '%s'" %
-                                      self.stream.current,
-                                      self.stream.current.lineno,
-                                      self.filename)
-        if parse_postfix:
-            node = self.parse_postfix_expression(node)
-        return node
-
-    def parse_tuple_expression(self, enforce=False, simplified=False):
-        """
-        Parse multiple expressions into a tuple. This can also return
-        just one expression which is not a tuple. If you want to enforce
-        a tuple, pass it enforce=True.
-        """
-        lineno = self.stream.lineno
-        if simplified:
-            parse = self.parse_primary_expression
-        else:
-            parse = self.parse_expression
-        args = []
-        is_tuple = False
-        while True:
-            if args:
-                self.stream.expect('comma')
-            if self.stream.current.type in tuple_edge_tokens:
-                break
-            args.append(parse())
-            if self.stream.current.type == 'comma':
-                is_tuple = True
-            else:
-                break
-        if not is_tuple and args:
-            if enforce:
-                raise TemplateSyntaxError('tuple expected', lineno,
-                                          self.filename)
-            return args[0]
-        return nodes.TupleExpression(args, lineno, self.filename)
-
-    def parse_bool_expression(self):
-        """
-        Parse a boolean literal.
-        """
-        token = self.stream.expect('name')
-        if token.value == 'true':
-            value = True
-        elif token.value == 'false':
-            value = False
-        else:
-            raise TemplateSyntaxError("expected boolean literal",
-                                      token.lineno, self.filename)
-        return nodes.ConstantExpression(value, token.lineno, self.filename)
-
-    def parse_none_expression(self):
-        """
-        Parse a none literal.
-        """
-        token = self.stream.expect('name', 'none')
-        return nodes.ConstantExpression(None, token.lineno, self.filename)
-
-    def parse_undefined_expression(self):
-        """
-        Parse an undefined literal.
-        """
-        token = self.stream.expect('name', 'undefined')
-        return nodes.UndefinedExpression(token.lineno, self.filename)
-
-    def parse_gettext_call(self):
-        """
-        parse {{ _('foo') }}.
-        """
-        # XXX: check if only one argument was passed and if
-        # it is a string literal. Maybe that should become a special
-        # expression anyway.
-        token = self.stream.expect('name', '_')
-        node = nodes.NameExpression(token.value, token.lineno, self.filename)
-        return self.parse_call_expression(node)
-
-    def parse_name_expression(self):
-        """
-        Parse any name.
-        """
-        token = self.stream.expect('name')
-        self.test_name(token.value)
-        return nodes.NameExpression(token.value, token.lineno, self.filename)
-
-    def parse_number_expression(self):
-        """
-        Parse a number literal.
-        """
-        token = self.stream.current
-        if token.type not in ('integer', 'float'):
-            raise TemplateSyntaxError('integer or float literal expected',
-                                      token.lineno, self.filename)
-        self.stream.next()
-        return nodes.ConstantExpression(token.value, token.lineno, self.filename)
-
-    def parse_string_expression(self):
-        """
-        Parse a string literal.
-        """
-        token = self.stream.expect('string')
-        return nodes.ConstantExpression(token.value, token.lineno, self.filename)
-
-    def parse_regex_expression(self):
-        """
-        Parse a regex literal.
-        """
-        token = self.stream.expect('regex')
-        return nodes.RegexExpression(token.value, token.lineno, self.filename)
-
-    def parse_paren_expression(self):
-        """
-        Parse a parenthized expression.
-        """
-        self.stream.expect('lparen')
-        try:
-            return self.parse_tuple_expression()
-        finally:
-            self.stream.expect('rparen')
-
-    def parse_list_expression(self):
-        """
-        Parse something like {{ [1, 2, "three"] }}
-        """
-        token = self.stream.expect('lbracket')
-        items = []
-        while self.stream.current.type != 'rbracket':
-            if items:
-                self.stream.expect('comma')
-            if self.stream.current.type == 'rbracket':
-                break
-            items.append(self.parse_expression())
-        self.stream.expect('rbracket')
-
-        return nodes.ListExpression(items, token.lineno, self.filename)
-
-    def parse_dict_expression(self):
-        """
-        Parse something like {{ {1: 2, 3: 4} }}
-        """
-        token = self.stream.expect('lbrace')
-        items = []
-        while self.stream.current.type != 'rbrace':
-            if items:
-                self.stream.expect('comma')
-            if self.stream.current.type == 'rbrace':
-                break
-            key = self.parse_expression()
-            self.stream.expect('colon')
-            value = self.parse_expression()
-            items.append((key, value))
-        self.stream.expect('rbrace')
-
-        return nodes.DictExpression(items, token.lineno, self.filename)
-
-    def parse_set_expression(self):
-        """
-        Parse something like {{ @(1, 2, 3) }}.
-        """
-        token = self.stream.expect('at')
-        self.stream.expect('lparen')
-        items = []
-        while self.stream.current.type != 'rparen':
-            if items:
-                self.stream.expect('comma')
-            if self.stream.current.type == 'rparen':
-                break
-            items.append(self.parse_expression())
-        self.stream.expect('rparen')
-
-        return nodes.SetExpression(items, token.lineno, self.filename)
-
-    def parse_postfix_expression(self, node):
-        """
-        Parse a postfix expression such as a filter statement or a
-        function call.
-        """
-        while True:
-            current = self.stream.current.type
-            if current == 'dot' or current == 'lbracket':
-                node = self.parse_subscript_expression(node)
-            elif current == 'lparen':
-                node = self.parse_call_expression(node)
-            elif current == 'pipe':
-                node = self.parse_filter_expression(node)
-            elif current == 'is':
-                node = self.parse_test_expression(node)
-            else:
-                break
-        return node
-
-    def parse_subscript_expression(self, node):
-        """
-        Parse a subscript statement. Gets attributes and items from an
-        object.
-        """
-        lineno = self.stream.lineno
-        if self.stream.current.type == 'dot':
-            self.stream.next()
-            token = self.stream.current
-            if token.type in ('name', 'integer'):
-                arg = nodes.ConstantExpression(token.value, token.lineno,
-                                               self.filename)
-            else:
-                raise TemplateSyntaxError('expected name or number',
-                                          token.lineno, self.filename)
-            self.stream.next()
-        elif self.stream.current.type == 'lbracket':
-            self.stream.next()
-            args = []
-            while self.stream.current.type != 'rbracket':
-                if args:
-                    self.stream.expect('comma')
-                args.append(self.parse_subscribed_expression())
-            self.stream.expect('rbracket')
-            if len(args) == 1:
-                arg = args[0]
-            else:
-                arg = nodes.TupleExpression(args, lineno, self.filename)
-        else:
-            raise TemplateSyntaxError('expected subscript expression',
-                                      self.lineno, self.filename)
-        return nodes.SubscriptExpression(node, arg, lineno, self.filename)
-
-    def parse_call_expression(self, node=None):
-        """
-        Parse a call.
-        """
-        if node is None:
-            node = self.parse_primary_expression(parse_postfix=False)
-        token = self.stream.expect('lparen')
-        args = []
-        kwargs = []
-        dyn_args = None
-        dyn_kwargs = None
-        require_comma = False
-
-        def ensure(expr):
-            if not expr:
-                raise TemplateSyntaxError('invalid syntax for function '
-                                          'call expression', token.lineno,
-                                          self.filename)
-
-        while self.stream.current.type != 'rparen':
-            if require_comma:
-                self.stream.expect('comma')
-                # support for trailing comma
-                if self.stream.current.type == 'rparen':
-                    break
-            if self.stream.current.type == 'mul':
-                ensure(dyn_args is None and dyn_kwargs is None)
-                self.stream.next()
-                dyn_args = self.parse_expression()
-            elif self.stream.current.type == 'pow':
-                ensure(dyn_kwargs is None)
-                self.stream.next()
-                dyn_kwargs = self.parse_expression()
-            else:
-                ensure(dyn_args is None and dyn_kwargs is None)
-                if self.stream.current.type == 'name' and \
-                    self.stream.look().type == 'assign':
-                    key = self.stream.current.value
-                    self.stream.skip(2)
-                    kwargs.append((key, self.parse_expression()))
-                else:
-                    ensure(not kwargs)
-                    args.append(self.parse_expression())
-
-            require_comma = True
-        self.stream.expect('rparen')
-
-        return nodes.CallExpression(node, args, kwargs, dyn_args,
-                                    dyn_kwargs, token.lineno,
-                                    self.filename)
-
-    def parse_filter_expression(self, node):
-        """
-        Parse filter calls.
-        """
-        lineno = self.stream.lineno
-        filters = []
-        while self.stream.current.type == 'pipe':
-            self.stream.next()
-            token = self.stream.expect('name')
-            args = []
-            if self.stream.current.type == 'lparen':
-                self.stream.next()
-                while self.stream.current.type != 'rparen':
-                    if args:
-                        self.stream.expect('comma')
-                    args.append(self.parse_expression())
-                self.stream.expect('rparen')
-            filters.append((token.value, args))
-        return nodes.FilterExpression(node, filters, lineno, self.filename)
-
-    def parse_test_expression(self, node):
-        """
-        Parse test calls.
-        """
-        token = self.stream.expect('is')
-        if self.stream.current.type == 'not':
-            self.stream.next()
-            negated = True
-        else:
-            negated = False
-        name = self.stream.expect('name').value
-        args = []
-        if self.stream.current.type == 'lparen':
-            self.stream.next()
-            while self.stream.current.type != 'rparen':
-                if args:
-                    self.stream.expect('comma')
-                args.append(self.parse_expression())
-            self.stream.expect('rparen')
-        elif self.stream.current.type in ('name', 'string', 'integer',
-                                          'float', 'lparen', 'lbracket',
-                                          'lbrace', 'regex'):
-            args.append(self.parse_expression())
-        node = nodes.TestExpression(node, name, args, token.lineno,
-                                    self.filename)
-        if negated:
-            node = nodes.NotExpression(node, token.lineno, self.filename)
-        return node
-
-    def test_name(self, name):
-        """
-        Test if a name is not a special constant
-        """
-        if name in ('true', 'false', 'none', 'undefined', '_'):
-            raise TemplateSyntaxError('expected name not special constant',
-                                      self.stream.lineno, self.filename)
-
-    def subparse(self, test, drop_needle=False):
-        """
-        Helper function used to parse the sourcecode until the test
-        function which is passed a tuple in the form (lineno, token, data)
-        returns True. In that case the current token is pushed back to
-        the stream and the generator ends.
-
-        The test function is only called for the first token after a
-        block tag. Variable tags are *not* aliases for {% print %} in
-        that case.
-
-        If drop_needle is True the needle_token is removed from the
-        stream.
-        """
-        if self.closed:
-            raise RuntimeError('parser is closed')
-        result = []
-        buffer = []
-        next = self.stream.next
-        lineno = self.stream.lineno
-
-        def assemble_list():
-            push_buffer()
-            return nodes.NodeList(result, lineno, self.filename)
-
-        def push_variable():
-            buffer.append((True, self.parse_tuple_expression()))
-
-        def push_data():
-            buffer.append((False, self.stream.expect('data')))
-
-        def push_buffer():
-            if not buffer:
-                return
-            template = []
-            variables = []
-            for is_var, data in buffer:
-                if is_var:
-                    template.append('%s')
-                    variables.append(data)
-                else:
-                    template.append(data.value.replace('%', '%%'))
-            result.append(nodes.Text(u''.join(template), variables,
-                                     buffer[0][1].lineno, self.filename))
-            del buffer[:]
-
-        def push_node(node):
-            push_buffer()
-            result.append(node)
-
-        while self.stream:
-            token_type = self.stream.current.type
-            if token_type == 'variable_begin':
-                next()
-                push_variable()
-                self.stream.expect('variable_end')
-            elif token_type == 'raw_begin':
-                next()
-                push_data()
-                self.stream.expect('raw_end')
-            elif token_type == 'block_begin':
-                next()
-                if test is not None and test(self.stream.current):
-                    if drop_needle:
-                        next()
-                    return assemble_list()
-                handler = self.directives.get(self.stream.current.type)
-                if handler is None:
-                    if self.no_variable_block:
-                        push_variable()
-                        self.stream.expect('block_end')
-                    elif self.stream.current.type in self.context_directives:
-                        raise TemplateSyntaxError('unexpected directive %r.' %
-                                                  self.stream.current.type,
-                                                  lineno, self.filename)
-                    else:
-                        name = self.stream.current.value
-                        raise TemplateSyntaxError('unknown directive %r.' %
-                                                  name, lineno, self.filename)
-                else:
-                    node = handler()
-                    if node is not None:
-                        push_node(node)
-            elif token_type == 'data':
-                push_data()
-
-            # this should be unreachable code
-            else:
-                assert False, "unexpected token %r" % self.stream.current
-
-        if test is not None:
-            msg = isinstance(test, StateTest) and ': ' + test.msg or ''
-            raise TemplateSyntaxError('unexpected end of stream' + msg,
-                                      self.stream.lineno, self.filename)
-
-        return assemble_list()
-
-    def sanitize_tree(self, body, extends):
-        self._sanitize_tree([body], [body], extends, body)
-        return body
-
-    def _sanitize_tree(self, nodelist, stack, extends, body):
-        """
-        This is not a closure because python leaks memory if it is.  It's used
-        by `parse()` to make sure blocks do not trigger unexpected behavior.
-        """
-        for node in nodelist:
-            if extends is not None and \
-                 node.__class__ is nodes.Block and \
-                 stack[-1] is not body:
-                for n in stack:
-                    if n.__class__ is nodes.Block:
-                        break
-                else:
-                    raise TemplateSyntaxError('misplaced block %r, '
-                                              'blocks in child '
-                                              'templates must be '
-                                              'either top level or '
-                                              'located in a block '
-                                              'tag.' % node.name,
-                                              node.lineno,
-                                              self.filename)
-            stack.append(node)
-            self._sanitize_tree(node.get_child_nodes(), stack, extends, body)
-            stack.pop()
-
-    def parse(self):
-        """
-        Parse the template and return a Template node. This also does some
-        post processing sanitizing and parses for an extends tag.
-        """
-        if self.closed:
-            raise RuntimeError('parser is closed')
-
-        try:
-            # get the leading whitespace, if we are not in a child
-            # template we push that back to the stream later.
-            leading_whitespace = self.stream.read_whitespace()
-
-            # parse an optional extends which *must* be the first node
-            # of a template.
-            if self.stream.current.type == 'block_begin' and \
-               self.stream.look().type == 'extends':
-                self.stream.skip(2)
-                extends = self.stream.expect('string').value
-                self.stream.expect('block_end')
-            else:
-                extends = None
-                if leading_whitespace:
-                    self.stream.shift(leading_whitespace)
-
-            body = self.sanitize_tree(self.subparse(None), extends)
-            return nodes.Template(extends, body, 1, self.filename)
-        finally:
-            self.close()
-
-    def close(self):
-        """Clean up soon."""
-        self.closed = True
-        self.stream = self.directives = self.stream = self.blocks = \
-            self.environment = None
diff --git a/jinja/plugin.py b/jinja/plugin.py
deleted file mode 100644 (file)
index 19bc284..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    jinja.plugin
-    ~~~~~~~~~~~~
-
-    Support for the `GeneralTemplateInterface`__ and the Buffet interface.
-
-    Do not use this module on your own. We don't recommend those interfaces!
-    If you are able to, you should really use Jinja without those abstraction
-    layers.
-
-    __ http://trac.pocoo.org/wiki/GeneralTemplateInterface
-
-    :copyright: 2007 by Armin Ronacher.
-    :license: BSD, see LICENSE for more details.
-"""
-from warnings import warn
-from jinja.environment import Environment
-from jinja.loaders import FunctionLoader, FileSystemLoader, PackageLoader
-from jinja.exceptions import TemplateNotFound
-
-
-class BuffetPlugin(object):
-    """
-    Implements the Jinja buffet plugin. Well. It works for pylons and should
-    work for TurboGears too if their plugin system would work.
-    """
-
-    def __init__(self, extra_vars_func=None, options=None):
-        if 'jinja.environment' in options:
-            self.env = options['jinja.environment']
-        else:
-            opt = {}
-            for key, value in options.iteritems():
-                if key.startswith('jinja.') and key != 'jinja.extension':
-                    opt[key[6:]] = value
-            loader_func = opt.pop('loader_func', None)
-            getmtime_func = opt.pop('getmtime_func', None)
-            use_memcache = opt.pop('use_memcache', False)
-            memcache_size = opt.pop('memcache_size', 40)
-            cache_folder = opt.pop('cache_folder', None)
-            auto_reload = opt.pop('auto_reload', True)
-            if 'searchpath' in opt:
-                opt['loader'] = FileSystemLoader(opt.pop('searchpath'),
-                                                 use_memcache, memcache_size,
-                                                 cache_folder, auto_reload)
-            elif 'package' in opt:
-                opt['loader'] = PackageLoader(opt.pop('package'),
-                                              opt.pop('package_path', ''),
-                                              use_memcache, memcache_size,
-                                              cache_folder, auto_reload)
-            elif loader_func is not None:
-                opt['loader'] = FunctionLoader(loader_func, getmtime_func,
-                                               use_memcache, memcache_size,
-                                               cache_folder, auto_reload)
-            self.env = Environment(**opt)
-
-        self.extra_vars_func = extra_vars_func
-        self.extension = options.pop('jinja.extension', 'html')
-
-    def load_template(self, templatename, template_string=None):
-        if template_string is not None:
-            return self.env.from_string(template_string)
-        if templatename.startswith('!'):
-            jinja_name = templatename[1:]
-        else:
-            jinja_name = templatename.replace('.', '/') + '.' + self.extension
-        return self.env.get_template(jinja_name)
-
-    def render(self, info, format='html', fragment=False, template=None):
-        if isinstance(template, basestring):
-            template = self.load_template(template)
-        if self.extra_vars_func:
-            info.update(self.extra_vars_func())
-        return template.render(info)
-
-
-def jinja_plugin_factory(options):
-    """
-    Basic implementation of the `GeneralTemplateInterface`.
-
-    Supports ``loader_func`` and ``getmtime_func``, as well as
-    string and file loading but ignores ``mode`` since it's a
-    text based template engine.
-
-    All options passed to this function are forwarded to the
-    jinja environment. Exceptions are the following keys:
-
-    =================== =================================================
-    ``environment``     If this is provided it must be the only
-                        configuration value and it's used as jinja
-                        environment.
-    ``searchpath``      If provided a new file system loader with this
-                        search path is instanciated.
-    ``package``         Name of the python package containing the
-                        templates. If this and ``package_path`` is
-                        defined a `PackageLoader` is used.
-    ``package_path``    Path to the templates inside of a package.
-    ``loader_func``     Function that takes the name of the template to
-                        load. If it returns a string or unicode object
-                        it's used to load a template. If the return
-                        value is None it's considered missing.
-    ``getmtime_func``   Function used to check if templates requires
-                        reloading. Has to return the UNIX timestamp of
-                        the last template change or 0 if this template
-                        does not exist or requires updates at any cost.
-    ``use_memcache``    Set this to ``True`` to enable memory caching.
-                        This is usually a good idea in production mode,
-                        but disable it during development since it won't
-                        reload template changes automatically.
-                        This only works in persistent environments like
-                        FastCGI.
-    ``memcache_size``   Number of template instance you want to cache.
-                        Defaults to ``40``.
-    ``cache_folder``    Set this to an existing directory to enable
-                        caching of templates on the file system. Note
-                        that this only affects templates transformed
-                        into python code. Default is ``None`` which means
-                        that caching is disabled.
-    ``auto_reload``     Set this to `False` for a slightly better
-                        performance. In that case of `getmtime_func`
-                        not being provided this won't have an effect.
-    =================== =================================================
-    """
-    warn(DeprecationWarning('general plugin interface implementation '
-                            'deprecated because not an accepted '
-                            'standard.'))
-
-    if 'environment' in options:
-        env = options['environment']
-        if not len(options) == 1:
-            raise TypeError('if environment provided no other '
-                            'arguments are allowed')
-    else:
-        loader_func = options.pop('loader_func', None)
-        getmtime_func = options.pop('getmtime_func', None)
-        use_memcache = options.pop('use_memcache', False)
-        memcache_size = options.pop('memcache_size', 40)
-        cache_folder = options.pop('cache_folder', None)
-        auto_reload = options.pop('auto_reload', True)
-        if 'searchpath' in options:
-            options['loader'] = FileSystemLoader(options.pop('searchpath'),
-                                                 use_memcache, memcache_size,
-                                                 cache_folder, auto_reload)
-        elif 'package' in options:
-            options['loader'] = PackageLoader(options.pop('package'),
-                                              options.pop('package_path', ''),
-                                              use_memcache, memcache_size,
-                                              cache_folder, auto_reload)
-        elif loader_func is not None:
-            options['loader'] = FunctionLoader(loader_func, getmtime_func,
-                                               use_memcache, memcache_size,
-                                               cache_folder, auto_reload)
-        env = Environment(**options)
-
-    def render_function(template, values, options):
-        if options.get('is_string'):
-            tmpl = env.from_string(template)
-        else:
-            try:
-                tmpl = env.get_template(template)
-            except TemplateNotFound:
-                return
-        return tmpl.render(**values)
-
-    return render_function
diff --git a/jinja/utils.py b/jinja/utils.py
deleted file mode 100644 (file)
index acd2018..0000000
+++ /dev/null
@@ -1,645 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    jinja.utils
-    ~~~~~~~~~~~
-
-    Utility functions.
-
-    **license information**: some of the regular expressions and
-    the ``urlize`` function were taken from the django framework.
-
-    :copyright: 2007 by Armin Ronacher, Lawrence Journal-World.
-    :license: BSD, see LICENSE for more details.
-"""
-import re
-import sys
-import string
-from types import MethodType, FunctionType
-from jinja import nodes
-from jinja.exceptions import SecurityException, TemplateNotFound
-from jinja.datastructure import TemplateData
-
-# the python2.4 version of deque is missing the remove method
-# because a for loop with a lookup for the missing value written
-# in python is slower we just use deque if we have python2.5 or higher
-try:
-    from collections import deque
-    deque.remove
-except (ImportError, AttributeError):
-    class deque(list):
-        """
-        Minimal subclass of list that provides the deque
-        interface used by the native `BaseContext` and the
-        `CacheDict`
-        """
-        def appendleft(self, item):
-            list.insert(self, 0, item)
-        def popleft(self):
-            return list.pop(self, 0)
-        def clear(self):
-            del self[:]
-
-# support for a working reversed() in 2.3
-try:
-    reversed = reversed
-except NameError:
-    def reversed(iterable):
-        if hasattr(iterable, '__reversed__'):
-            return iterable.__reversed__()
-        try:
-            return iter(iterable[::-1])
-        except TypeError:
-            return iter(tuple(iterable)[::-1])
-
-# set support for python 2.3
-try:
-    set = set
-except NameError:
-    from sets import Set as set
-
-# sorted support (just a simplified version)
-try:
-    sorted = sorted
-except NameError:
-    _cmp = cmp
-    def sorted(seq, cmp=None, key=None, reverse=False):
-        rv = list(seq)
-        if key is not None:
-            cmp = lambda a, b: _cmp(key(a), key(b))
-        rv.sort(cmp)
-        if reverse:
-            rv.reverse()
-        return rv
-
-# group by support
-try:
-    from itertools import groupby
-except ImportError:
-    class groupby(object):
-
-        def __init__(self, iterable, key=lambda x: x):
-            self.keyfunc = key
-            self.it = iter(iterable)
-            self.tgtkey = self.currkey = self.currvalue = xrange(0)
-
-        def __iter__(self):
-            return self
-
-        def next(self):
-            while self.currkey == self.tgtkey:
-                self.currvalue = self.it.next()
-                self.currkey = self.keyfunc(self.currvalue)
-            self.tgtkey = self.currkey
-            return (self.currkey, self._grouper(self.tgtkey))
-
-        def _grouper(self, tgtkey):
-            while self.currkey == tgtkey:
-                yield self.currvalue
-                self.currvalue = self.it.next()
-                self.currkey = self.keyfunc(self.currvalue)
-
-#: function types
-callable_types = (FunctionType, MethodType)
-
-#: number of maximal range items
-MAX_RANGE = 1000000
-
-_word_split_re = re.compile(r'(\s+)')
-
-_punctuation_re = re.compile(
-    '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' %  (
-        '|'.join([re.escape(p) for p in ('(', '<', '&lt;')]),
-        '|'.join([re.escape(p) for p in ('.', ',', ')', '>', '\n', '&gt;')])
-    )
-)
-
-_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
-
-#: used by from_string as cache
-_from_string_env = None
-
-
-def escape(s, quote=None):
-    """
-    SGML/XML escape an unicode object.
-    """
-    s = s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
-    if not quote:
-        return s
-    return s.replace('"', "&quot;")
-
-
-def urlize(text, trim_url_limit=None, nofollow=False):
-    """
-    Converts any URLs in text into clickable links. Works on http://,
-    https:// and www. links. Links can have trailing punctuation (periods,
-    commas, close-parens) and leading punctuation (opening parens) and
-    it'll still do the right thing.
-
-    If trim_url_limit is not None, the URLs in link text will be limited
-    to trim_url_limit characters.
-
-    If nofollow is True, the URLs in link text will get a rel="nofollow"
-    attribute.
-    """
-    trim_url = lambda x, limit=trim_url_limit: limit is not None \
-                         and (x[:limit] + (len(x) >=limit and '...'
-                         or '')) or x
-    words = _word_split_re.split(text)
-    nofollow_attr = nofollow and ' rel="nofollow"' or ''
-    for i, word in enumerate(words):
-        match = _punctuation_re.match(word)
-        if match:
-            lead, middle, trail = match.groups()
-            if middle.startswith('www.') or (
-                '@' not in middle and
-                not middle.startswith('http://') and
-                len(middle) > 0 and
-                middle[0] in string.letters + string.digits and (
-                    middle.endswith('.org') or
-                    middle.endswith('.net') or
-                    middle.endswith('.com')
-                )):
-                middle = '<a href="http://%s"%s>%s</a>' % (middle,
-                    nofollow_attr, trim_url(middle))
-            if middle.startswith('http://') or \
-               middle.startswith('https://'):
-                middle = '<a href="%s"%s>%s</a>' % (middle,
-                    nofollow_attr, trim_url(middle))
-            if '@' in middle and not middle.startswith('www.') and \
-               not ':' in middle and _simple_email_re.match(middle):
-                middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
-            if lead + middle + trail != word:
-                words[i] = lead + middle + trail
-    return u''.join(words)
-
-
-def from_string(source):
-    """
-    Create a template from the template source.
-    """
-    global _from_string_env
-    if _from_string_env is None:
-        from jinja.environment import Environment
-        _from_string_env = Environment()
-    return _from_string_env.from_string(source)
-
-
-#: minor speedup
-_getattr = getattr
-
-def get_attribute(obj, name):
-    """
-    Return the attribute from name. Raise either `AttributeError`
-    or `SecurityException` if something goes wrong.
-    """
-    if not isinstance(name, basestring):
-        raise AttributeError(name)
-    if name[:2] == name[-2:] == '__':
-        raise SecurityException('not allowed to access internal attributes')
-    if getattr(obj, '__class__', None) in callable_types and \
-       name.startswith('func_') or name.startswith('im_'):
-        raise SecurityException('not allowed to access function attributes')
-
-    r = _getattr(obj, 'jinja_allowed_attributes', None)
-    if r is not None and name not in r:
-        raise SecurityException('disallowed attribute accessed')
-
-    # attribute lookups convert unicode strings to ascii bytestrings.
-    # this process could raise an UnicodeEncodeError.
-    try:
-        return _getattr(obj, name)
-    except UnicodeError:
-        raise AttributeError(name)
-
-
-def safe_range(start, stop=None, step=None):
-    """
-    "Safe" form of range that does not generate too large lists.
-    """
-    if step is None:
-        step = 1
-    if stop is None:
-        r = xrange(0, start, step)
-    else:
-        r = xrange(start, stop, step)
-    if len(r) > MAX_RANGE:
-        def limit():
-            i = 0
-            for item in r:
-                i += 1
-                yield item
-                if i >= MAX_RANGE:
-                    break
-        return list(limit())
-    return list(r)
-
-
-def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
-    """
-    Generate some lorem impsum for the template.
-    """
-    from jinja.constants import LOREM_IPSUM_WORDS
-    from random import choice, random, randrange
-    words = LOREM_IPSUM_WORDS.split()
-    result = []
-
-    for _ in xrange(n):
-        next_capitalized = True
-        last_comma = last_fullstop = 0
-        word = None
-        last = None
-        p = []
-
-        # each paragraph contains out of 20 to 100 words.
-        for idx, _ in enumerate(xrange(randrange(min, max))):
-            while True:
-                word = choice(words)
-                if word != last:
-                    last = word
-                    break
-            if next_capitalized:
-                word = word.capitalize()
-                next_capitalized = False
-            # add commas
-            if idx - randrange(3, 8) > last_comma:
-                last_comma = idx
-                last_fullstop += 2
-                word += ','
-            # add end of sentences
-            if idx - randrange(10, 20) > last_fullstop:
-                last_comma = last_fullstop = idx
-                word += '.'
-                next_capitalized = True
-            p.append(word)
-
-        # ensure that the paragraph ends with a dot.
-        p = u' '.join(p)
-        if p.endswith(','):
-            p = p[:-1] + '.'
-        elif not p.endswith('.'):
-            p += '.'
-        result.append(p)
-
-    if not html:
-        return u'\n\n'.join(result)
-    return u'\n'.join([u'<p>%s</p>' % escape(x) for x in result])
-
-
-def watch_changes(env, context, iterable, *attributes):
-    """
-    Wise replacement for ``{% ifchanged %}``.
-    """
-    # find the attributes to watch
-    if attributes:
-        tests = []
-        tmp = []
-        for attribute in attributes:
-            if isinstance(attribute, (str, unicode, int, long, bool)):
-                tmp.append(attribute)
-            else:
-                tests.append(tuple(attribute))
-        if tmp:
-            tests.append(tuple(attribute))
-        last = tuple([object() for x in tests])
-    # or no attributes if we watch the object itself
-    else:
-        tests = None
-        last = object()
-
-    # iterate trough it and keep check the attributes or values
-    for item in iterable:
-        if tests is None:
-            cur = item
-        else:
-            cur = tuple([env.get_attributes(item, x) for x in tests])
-        if cur != last:
-            changed = True
-            last = cur
-        else:
-            changed = False
-        yield changed, item
-watch_changes.jinja_context_callable = True
-
-
-def render_included(env, context, template_name):
-    """
-    Works like djangos {% include %} tag. It doesn't include the
-    template but load it independently and renders it to a string.
-    """
-    #XXX: ignores parent completely!
-    tmpl = env.get_template(template_name)
-    return tmpl.render(context.to_dict())
-render_included.jinja_context_callable = True
-
-
-# python2.4 and lower has a bug regarding joining of broken generators.
-# because of the runtime debugging system we have to keep track of the
-# number of frames to skip. that's what RUNTIME_EXCEPTION_OFFSET is for.
-try:
-    _test_singleton = object()
-    def _test_gen_bug():
-        raise TypeError(_test_singleton)
-        yield None
-    ''.join(_test_gen_bug())
-except TypeError, e:
-    if e.args and e.args[0] is _test_singleton:
-        capture_generator = u''.join
-        RUNTIME_EXCEPTION_OFFSET = 1
-    else:
-        capture_generator = lambda gen: u''.join(tuple(gen))
-        RUNTIME_EXCEPTION_OFFSET = 2
-del _test_singleton, _test_gen_bug
-
-
-def pformat(obj, verbose=False):
-    """
-    Prettyprint an object.  Either use the `pretty` library or the
-    builtin `pprint`.
-    """
-    try:
-        from pretty import pretty
-        return pretty(obj, verbose=verbose)
-    except ImportError:
-        from pprint import pformat
-        return pformat(obj)
-
-
-def buffereater(f, template_data=False):
-    """
-    Used by the python translator to capture output of substreams.
-    (macros, filter sections etc)
-    """
-    def wrapped(*a, **kw):
-        __traceback_hide__ = True
-        rv = capture_generator(f(*a, **kw))
-        if template_data:
-            rv = TemplateData(rv)
-        return rv
-    return wrapped
-
-
-def empty_block(context):
-    """
-    An empty callable that just returns an empty decorator.
-    Used to represent empty blocks.
-    """
-    if 0: yield None
-
-
-def collect_translations(ast):
-    """
-    Collect all translatable strings for the given ast. The
-    return value is a list of tuples in the form ``(lineno, singular,
-    plural)``. If a translation doesn't require a plural form the
-    third item is `None`.
-    """
-    todo = [ast]
-    result = []
-    while todo:
-        node = todo.pop()
-        if node.__class__ is nodes.Trans:
-            result.append((node.lineno, node.singular, node.plural))
-        elif node.__class__ is nodes.CallExpression and \
-             node.node.__class__ is nodes.NameExpression and \
-             node.node.name == '_':
-            if len(node.args) == 1 and not node.kwargs and not node.dyn_args \
-               and not node.dyn_kwargs and \
-               node.args[0].__class__ is nodes.ConstantExpression:
-                result.append((node.lineno, node.args[0].value, None))
-        todo.extend(node.get_child_nodes())
-    result.sort(lambda a, b: cmp(a[0], b[0]))
-    return result
-
-
-class DebugHelper(object):
-    """
-    Debugging Helper. Available in the template as "debug".
-    """
-    jinja_context_callable = True
-    jinja_allowed_attributes = ['filters']
-
-    def __init__(self):
-        raise TypeError('cannot create %r instances' %
-                        self.__class__.__name__)
-
-    def __call__(self, env, context):
-        """Print a nice representation of the context."""
-        return pformat(context.to_dict(), verbose=True)
-
-    def filters(self, env, context, builtins=True):
-        """List the filters."""
-        from inspect import getdoc
-        strip = set()
-        if not builtins:
-            from jinja.defaults import DEFAULT_FILTERS
-            strip = set(DEFAULT_FILTERS.values())
-        filters = env.filters.items()
-        filters.sort(lambda a, b: cmp(a[0].lower(), b[0].lower()))
-        result = []
-        for name, f in filters:
-            if f in strip:
-                continue
-            doc = '\n'.join(['    ' + x for x in (getdoc(f) or '').splitlines()])
-            result.append('`%s`\n\n%s' % (name, doc))
-        return '\n\n'.join(result)
-    filters.jinja_context_callable = True
-
-    def tests(self, env, context, builtins=True):
-        """List the tests."""
-        from inspect import getdoc
-        strip = set()
-        if not builtins:
-            from jinja.defaults import DEFAULT_TESTS
-            strip = set(DEFAULT_TESTS.values())
-        tests = env.tests.items()
-        tests.sort(lambda a, b: cmp(a[0].lower(), b[0].lower()))
-        result = []
-        for name, f in tests:
-            if f in strip:
-                continue
-            doc = '\n'.join(['    ' + x for x in (getdoc(f) or '').splitlines()])
-            result.append('`%s`\n\n%s' % (name, doc))
-        return '\n\n'.join(result)
-    tests.jinja_context_callable = True
-
-    def __str__(self):
-        print 'use debug() for debugging the context'
-
-
-#: the singleton instance of `DebugHelper`
-debug_helper = object.__new__(DebugHelper)
-
-
-class CacheDict(object):
-    """
-    A dict like object that stores a limited number of items and forgets
-    about the least recently used items::
-
-        >>> cache = CacheDict(3)
-        >>> cache['A'] = 0
-        >>> cache['B'] = 1
-        >>> cache['C'] = 2
-        >>> len(cache)
-        3
-
-    If we now access 'A' again it has a higher priority than B::
-
-        >>> cache['A']
-        0
-
-    If we add a new item 'D' now 'B' will disappear::
-
-        >>> cache['D'] = 3
-        >>> len(cache)
-        3
-        >>> 'B' in cache
-        False
-
-    If you iterate over the object the most recently used item will be
-    yielded First::
-
-        >>> for item in cache:
-        ...     print item
-        D
-        A
-        C
-
-    If you want to iterate the other way round use ``reverse(cache)``.
-
-    Implementation note: This is not a nice way to solve that problem but
-    for smaller capacities it's faster than a linked list.
-    Perfect for template environments where you don't expect too many
-    different keys.
-    """
-
-    def __init__(self, capacity):
-        self.capacity = capacity
-        self._mapping = {}
-        self._queue = deque()
-
-        # alias all queue methods for faster lookup
-        self._popleft = self._queue.popleft
-        self._pop = self._queue.pop
-        self._remove = self._queue.remove
-        self._append = self._queue.append
-
-    def copy(self):
-        """
-        Return an shallow copy of the instance.
-        """
-        rv = CacheDict(self.capacity)
-        rv._mapping.update(self._mapping)
-        rv._queue = self._queue[:]
-        return rv
-
-    def get(self, key, default=None):
-        """
-        Return an item from the cache dict or `default`
-        """
-        if key in self:
-            return self[key]
-        return default
-
-    def setdefault(self, key, default=None):
-        """
-        Set `default` if the key is not in the cache otherwise
-        leave unchanged. Return the value of this key.
-        """
-        if key in self:
-            return self[key]
-        self[key] = default
-        return default
-
-    def clear(self):
-        """
-        Clear the cache dict.
-        """
-        self._mapping.clear()
-        self._queue.clear()
-
-    def __contains__(self, key):
-        """
-        Check if a key exists in this cache dict.
-        """
-        return key in self._mapping
-
-    def __len__(self):
-        """
-        Return the current size of the cache dict.
-        """
-        return len(self._mapping)
-
-    def __repr__(self):
-        return '<%s %r>' % (
-            self.__class__.__name__,
-            self._mapping
-        )
-
-    def __getitem__(self, key):
-        """
-        Get an item from the cache dict. Moves the item up so that
-        it has the highest priority then.
-
-        Raise an `KeyError` if it does not exist.
-        """
-        rv = self._mapping[key]
-        if self._queue[-1] != key:
-            self._remove(key)
-            self._append(key)
-        return rv
-
-    def __setitem__(self, key, value):
-        """
-        Sets the value for an item. Moves the item up so that it
-        has the highest priority then.
-        """
-        if key in self._mapping:
-            self._remove(key)
-        elif len(self._mapping) == self.capacity:
-            del self._mapping[self._popleft()]
-        self._append(key)
-        self._mapping[key] = value
-
-    def __delitem__(self, key):
-        """
-        Remove an item from the cache dict.
-        Raise an `KeyError` if it does not exist.
-        """
-        del self._mapping[key]
-        self._remove(key)
-
-    def __iter__(self):
-        """
-        Iterate over all values in the cache dict, ordered by
-        the most recent usage.
-        """
-        return reversed(self._queue)
-
-    def __reversed__(self):
-        """
-        Iterate over the values in the cache dict, oldest items
-        coming first.
-        """
-        return iter(self._queue)
-
-    __copy__ = copy
-
-    def __deepcopy__(self):
-        """
-        Return a deep copy of the cache dict.
-        """
-        from copy import deepcopy
-        rv = CacheDict(self.capacity)
-        rv._mapping = deepcopy(self._mapping)
-        rv._queue = deepcopy(self._queue)
-        return rv
-
-
-NAMESPACE = {
-    'range':            safe_range,
-    'debug':            debug_helper,
-    'lipsum':           generate_lorem_ipsum,
-    'watchchanges':     watch_changes,
-    'rendertemplate':   render_included
-}
similarity index 71%
rename from jinja/__init__.py
rename to jinja2/__init__.py
index b7dd8f5bf8de3c7b066f2e51db64635de3772d36..94949c1bfcc19b83f8ef7a4bb7379940fbaf8691 100644 (file)
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 """
-    jinja
-    ~~~~~
+    jinja2
+    ~~~~~~
 
     Jinja is a `sandboxed`_ template engine written in pure Python. It
     provides a `Django`_ like non-XML syntax and compiles templates into
     .. _Jinja tip: http://dev.pocoo.org/hg/jinja-main/archive/tip.tar.gz#egg=Jinja-dev
 
 
-    :copyright: 2007 by Armin Ronacher.
+    :copyright: 2008 by Armin Ronacher.
     :license: BSD, see LICENSE for more details.
 """
-
-from jinja.environment import Environment
-from jinja.datastructure import Markup
-from jinja.plugin import jinja_plugin_factory as template_plugin_factory
-from jinja.loaders import FileSystemLoader, PackageLoader, DictLoader, \
-     ChoiceLoader, FunctionLoader, MemcachedFileSystemLoader
-from jinja.utils import from_string
-
-
-__all__ = ['Environment', 'Markup', 'FileSystemLoader', 'PackageLoader',
-           'DictLoader', 'ChoiceLoader', 'FunctionLoader',
-           'MemcachedFileSystemLoader', 'from_string']
-
-__version__ = '1.3'
-__author__ = 'Armin Ronacher'
-__url__ = 'http://jinja.pocoo.org/'
-__license__ = 'BSD'
-__docformat__ = 'restructuredtext'
similarity index 100%
rename from jinja/_debugger.c
rename to jinja2/_debugger.c
similarity index 100%
rename from jinja/_native.py
rename to jinja2/_native.py
similarity index 100%
rename from jinja/_speedups.c
rename to jinja2/_speedups.c
similarity index 100%
rename from jinja/constants.py
rename to jinja2/constants.py
diff --git a/jinja2/datastructure.py b/jinja2/datastructure.py
new file mode 100644 (file)
index 0000000..b20acd1
--- /dev/null
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.datastructure
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Module that helds several data types used in the template engine.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from operator import itemgetter
+from jinja.exceptions import TemplateSyntaxError, TemplateRuntimeError
+
+
+_missing = object()
+
+
+class Token(tuple):
+    """
+    Token class.
+    """
+    __slots__ = ()
+    lineno, type, value = map(itemgetter, range(3))
+
+    def __new__(cls, lineno, type, value):
+        return tuple.__new__(cls, (lineno, type, value))
+
+    def __str__(self):
+        from jinja.lexer import keywords, reverse_operators
+        if self.type in keywords:
+            return self.type
+        elif self.type in reverse_operators:
+            return reverse_operators[self.type]
+        return self.value
+
+    def __repr__(self):
+        return 'Token(%r, %r, %r)' % (
+            self.lineno,
+            self.type,
+            self.value
+        )
+
+
+class TokenStreamIterator(object):
+    """
+    The iterator for tokenstreams.  Iterate over the stream
+    until the eof token is reached.
+    """
+
+    def __init__(self, stream):
+        self._stream = stream
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        token = self._stream.current
+        if token.type == 'eof':
+            self._stream.close()
+            raise StopIteration()
+        self._stream.next()
+        return token
+
+
+class TokenStream(object):
+    """
+    A token stream wraps a generator and supports pushing tokens back.
+    It also provides some functions to expect tokens and similar stuff.
+
+    Important note: Do never push more than one token back to the
+                    stream.  Although the stream object won't stop you
+                    from doing so, the behavior is undefined. Multiple
+                    pushed tokens are only used internally!
+    """
+
+    def __init__(self, generator, filename):
+        self._next = generator.next
+        self._pushed = []
+        self.current = Token(1, 'initial', '')
+        self.filename = filename
+        self.next()
+
+    def __iter__(self):
+        return TokenStreamIterator(self)
+
+    def lineno(self):
+        """The current line number."""
+        return self.current.lineno
+    lineno = property(lineno, doc=lineno.__doc__)
+
+    def __nonzero__(self):
+        """Are we at the end of the tokenstream?"""
+        return bool(self._pushed) or self.current.type != 'eof'
+
+    eos = property(lambda x: not x.__nonzero__(), doc=__nonzero__.__doc__)
+
+    def push(self, token):
+        """Push a token back to the stream."""
+        self._pushed.append(token)
+
+    def skip(self, n):
+        """Got n tokens ahead."""
+        for x in xrange(n):
+            self.next()
+
+    def next(self):
+        """Go one token ahead."""
+        if self._pushed:
+            self.current = self._pushed.pop()
+        elif self.current.type != 'eof':
+            try:
+                self.current = self._next()
+            except StopIteration:
+                self.close()
+
+    def close(self):
+        """Close the stream."""
+        self.current = Token(self.current.lineno, 'eof', '')
+        self._next = None
+
+    def expect(self, token_type, token_value=_missing):
+        """Expect a given token type and return it"""
+        if self.current.type != token_type:
+            raise TemplateSyntaxError("expected token %r, got %r" %
+                                      (token_type, self.current.type),
+                                      self.current.lineno,
+                                      self.filename)
+        elif token_value is not _missing and \
+             self.current.value != token_value:
+            raise TemplateSyntaxError("expected %r, got %r" %
+                                      (token_value, self.current.value),
+                                      self.current.lineno,
+                                      self.filename)
+        try:
+            return self.current
+        finally:
+            self.next()
similarity index 86%
rename from jinja/defaults.py
rename to jinja2/defaults.py
index 8d5025d0ba6ae76e2d3e36077e1a24c728040ca6..37473ea8f895195e85525212648f9800c074b550 100644 (file)
@@ -10,7 +10,7 @@
 """
 from jinja.filters import FILTERS as DEFAULT_FILTERS
 from jinja.tests import TESTS as DEFAULT_TESTS
-from jinja.utils import NAMESPACE as DEFAULT_NAMESPACE
+DEFAULT_NAMESPACE = {}
 
 
 __all__ = ['DEFAULT_FILTERS', 'DEFAULT_TESTS', 'DEFAULT_NAMESPACE']
diff --git a/jinja2/environment.py b/jinja2/environment.py
new file mode 100644 (file)
index 0000000..a387ffa
--- /dev/null
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.environment
+    ~~~~~~~~~~~~~~~~~
+
+    Provides a class that holds runtime and parsing time options.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from jinja.lexer import Lexer
+from jinja.parser import Parser
+from jinja.loaders import LoaderWrapper
+from jinja.datastructure import SilentUndefined, Markup, Context, FakeTranslator
+from jinja.utils import collect_translations, get_attribute
+from jinja.exceptions import FilterNotFound, TestNotFound, \
+     SecurityException, TemplateSyntaxError
+from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
+
+
+__all__ = ['Environment']
+
+
+#: minor speedup
+_getattr = getattr
+
+
+class Environment(object):
+    """
+    The Jinja environment.
+
+    The core component of Jinja is the `Environment`. It contains
+    important shared variables like configuration, filters, tests,
+    globals and others.
+    """
+
+    def __init__(self,
+                 block_start_string='{%',
+                 block_end_string='%}',
+                 variable_start_string='{{',
+                 variable_end_string='}}',
+                 comment_start_string='{#',
+                 comment_end_string='#}',
+                 trim_blocks=False,
+                 loader=None):
+        """
+        Here the possible initialization parameters:
+
+        ========================= ============================================
+        `block_start_string`      the string marking the begin of a block.
+                                  this defaults to ``'{%'``.
+        `block_end_string`        the string marking the end of a block.
+                                  defaults to ``'%}'``.
+        `variable_start_string`   the string marking the begin of a print
+                                  statement. defaults to ``'{{'``.
+        `comment_start_string`    the string marking the begin of a
+                                  comment. defaults to ``'{#'``.
+        `comment_end_string`      the string marking the end of a comment.
+                                  defaults to ``'#}'``.
+        `trim_blocks`             If this is set to ``True`` the first newline
+                                  after a block is removed (block, not
+                                  variable tag!). Defaults to ``False``.
+        `loader`                  The loader for this environment.
+        ========================= ============================================
+        """
+
+        # lexer / parser information
+        self.block_start_string = block_start_string
+        self.block_end_string = block_end_string
+        self.variable_start_string = variable_start_string
+        self.variable_end_string = variable_end_string
+        self.comment_start_string = comment_start_string
+        self.comment_end_string = comment_end_string
+        self.trim_blocks = trim_blocks
+
+        # other stuff
+        self.template_charset = template_charset
+        self.loader = loader
+
+        # defaults
+        self.filters = DEFAULT_FILTERS.copy()
+        self.tests = DEFAULT_TESTS.copy()
+        self.globals = DEFAULT_NAMESPACE.copy()
+
+        # create lexer
+        self.lexer = Lexer(self)
+
+    def loader(self, value):
+        """
+        Get or set the template loader.
+        """
+        self._loader = LoaderWrapper(self, value)
+    loader = property(lambda s: s._loader, loader, doc=loader.__doc__)
+
+    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
+        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.
+        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)
+
+        The tuples are returned in the form ``(lineno, token, value)``.
+        """
+        return self.lexer.tokeniter(source, filename)
diff --git a/jinja2/exceptions.py b/jinja2/exceptions.py
new file mode 100644 (file)
index 0000000..9ec5c27
--- /dev/null
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.exceptions
+    ~~~~~~~~~~~~~~~~
+
+    Jinja exceptions.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+
+
+class TemplateError(RuntimeError):
+    pass
+
+
+class TemplateNotFound(IOError, LookupError, TemplateError):
+    """
+    Raised if a template does not exist.
+    """
+
+    def __init__(self, name):
+        IOError.__init__(self, name)
+        self.name = name
+
+
+class TemplateSyntaxError(SyntaxError, TemplateError):
+    """
+    Raised to tell the user that there is a problem with the template.
+    """
+
+    def __init__(self, message, lineno, filename):
+        SyntaxError.__init__(self, message)
+        self.lineno = lineno
+        self.filename = filename
+
+
+class TemplateRuntimeError(TemplateError):
+    """
+    Raised by the template engine if a tag encountered an error when
+    rendering.
+    """
similarity index 99%
rename from jinja/filters.py
rename to jinja2/filters.py
index 2902919e3ed11c3baaaeef4b721a6c13567e70e8..6098b6ef87e43b985be6377459458d9e4080bb1d 100644 (file)
@@ -15,10 +15,6 @@ try:
 except ImportError:
     itemgetter = lambda a: lambda b: b[a]
 from urllib import urlencode, quote
-from jinja.utils import urlize, escape, reversed, sorted, groupby, \
-     get_attribute, pformat
-from jinja.datastructure import TemplateData
-from jinja.exceptions import FilterArgumentError, SecurityException
 
 
 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
similarity index 94%
rename from jinja/lexer.py
rename to jinja2/lexer.py
index 947087f4db02eb92c0de4e759cfa1f16a102ceec..7752bee55c6c1b9b44123a87cb22823e73d864dc 100644 (file)
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 """
-    jinja.lexer
-    ~~~~~~~~~~~
+    jinja2.lexer
+    ~~~~~~~~~~~~
 
     This module implements a Jinja / Python combination lexer. The
     `Lexer` class provided by this module is used to do some preprocessing
     operators we don't allow in templates. On the other hand it separates
     template code and python code in expressions.
 
-    Because of some limitations in the compiler package which are just
-    natural but annoying for Jinja, the lexer also "escapes" non names that
-    are not keywords. The Jinja parser then removes those escaping marks
-    again.
-
-    This is required in order to make "class" and some other python keywords
-    we don't use valid identifiers.
-
     :copyright: 2007-2008 by Armin Ronacher.
     :license: BSD, see LICENSE for more details.
 """
@@ -26,7 +18,6 @@ import re
 import unicodedata
 from jinja.datastructure import TokenStream, Token
 from jinja.exceptions import TemplateSyntaxError
-from jinja.utils import set, sorted
 from weakref import WeakValueDictionary
 
 
@@ -40,20 +31,20 @@ _lexer_cache = WeakValueDictionary()
 
 # static regular expressions
 whitespace_re = re.compile(r'\s+(?um)')
-name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')
 string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
                        r'|"([^"\\]*(?:\\.[^"\\]*)*)")(?ms)')
 integer_re = re.compile(r'\d+')
+name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')
 float_re = re.compile(r'\d+\.\d+')
-regex_re = re.compile(r'\@/([^/\\]*(?:\\.[^/\\]*)*)*/[a-z]*(?ms)')
 
 
 # set of used keywords
-keywords = set(['and', 'block', 'cycle', 'elif', 'else', 'endblock',
+keywords = set(['and', 'block', 'elif', 'else', 'endblock',
                 'endfilter', 'endfor', 'endif', 'endmacro', 'endraw',
                 'endtrans', 'extends', 'filter', 'for', 'if', 'in',
                 'include', 'is', 'macro', 'not', 'or', 'pluralize', 'raw',
-                'recursive', 'set', 'trans', 'print', 'call', 'endcall'])
+                'recursive', 'set', 'trans', 'call', 'endcall',
+                'true', 'false', 'none'])
 
 # bind operators to token types
 operators = {
@@ -65,8 +56,6 @@ operators = {
     '%':            'mod',
     '**':           'pow',
     '~':            'tilde',
-    '!':            'bang',
-    '@':            'at',
     '[':            'lbracket',
     ']':            'rbracket',
     '(':            'lparen',
@@ -83,7 +72,8 @@ operators = {
     '.':            'dot',
     ':':            'colon',
     '|':            'pipe',
-    ',':            'comma'
+    ',':            'comma',
+    ';':            'semicolon'
 }
 
 reverse_operators = dict([(v, k) for k, v in operators.iteritems()])
@@ -242,9 +232,10 @@ class Lexer(object):
             (whitespace_re, None, None),
             (float_re, 'float', None),
             (integer_re, 'integer', None),
+            ('%s' % '|'.join(sorted(keywords, key=lambda x: -len(x))),
+             'keyword', None),
             (name_re, 'name', None),
             (string_re, 'string', None),
-            (regex_re, 'regex', None),
             (operator_re, 'operator', None)
         ]
 
@@ -351,22 +342,16 @@ class Lexer(object):
                         value = str(value)
                     except UnicodeError:
                         pass
+                elif token == 'keyword':
+                    token = str(value)
                 elif token == 'name':
                     value = str(value)
-                    if value in keywords:
-                        token = value
-                        value = ''
                 elif token == 'string':
                     value = unescape_string(lineno, filename, value[1:-1])
                     try:
                         value = str(value)
                     except UnicodeError:
                         pass
-                elif token == 'regex':
-                    args = value[value.rfind('/') + 1:]
-                    value = unescape_regex(value[2:-(len(args) + 1)])
-                    if args:
-                        value = '(?%s)%s' % (args, value)
                 elif token == 'integer':
                     value = int(value)
                 elif token == 'float':
diff --git a/jinja2/loaders.py b/jinja2/loaders.py
new file mode 100644 (file)
index 0000000..40bc1d7
--- /dev/null
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.loaders
+    ~~~~~~~~~~~~~
+
+    Jinja loader classes.
+
+    :copyright: 2007 by Armin Ronacher, Bryan McLemore.
+    :license: BSD, see LICENSE for more details.
+"""
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
new file mode 100644 (file)
index 0000000..1e20096
--- /dev/null
@@ -0,0 +1,494 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.nodes
+    ~~~~~~~~~~~~
+
+    This module implements additional nodes derived from the ast base node.
+
+    It also provides some node tree helper functions like `in_lineno` and
+    `get_nodes` used by the parser and translator in order to normalize
+    python and jinja nodes.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+import operator
+from itertools import chain, izip
+from copy import copy
+
+
+_binop_to_func = {
+    '*':        operator.mul,
+    '/':        operator.truediv,
+    '//':       operator.floordiv,
+    '**':       operator.pow,
+    '%':        operator.mod,
+    '+':        operator.add,
+    '-':        operator.sub
+}
+
+_uaop_to_func = {
+    'not':      operator.not_,
+    '+':        operator.pos,
+    '-':        operator.neg
+}
+
+
+class Impossible(Exception):
+    """
+    Raised if the node could not perform a requested action.
+    """
+
+
+class NodeType(type):
+
+    def __new__(cls, name, bases, d):
+        for attr in '_fields', '_attributes':
+            storage = []
+            for base in bases:
+                storage.extend(getattr(base, attr, ()))
+            storage.extend(d.get(attr, ()))
+            assert len(storage) == len(set(storage))
+            d[attr] = tuple(storage)
+        return type.__new__(cls, name, bases, d)
+
+
+class Node(object):
+    """
+    Base jinja node.
+    """
+    __metaclass__ = NodeType
+    _fields = ()
+    _attributes = ('lineno',)
+
+    def __init__(self, *args, **kw):
+        if args:
+            if len(args) != len(self._fields):
+                if not self._fields:
+                    raise TypeError('%r takes 0 arguments' %
+                                    self.__class__.__name__)
+                raise TypeError('%r takes 0 or %d argument%s' % (
+                    self.__class__.__name__,
+                    len(self._fields),
+                    len(self._fields) != 1 and 's' or ''
+                ))
+            for name, arg in izip(self._fields, args):
+                setattr(self, name, arg)
+        for attr in self._attributes:
+            setattr(self, attr, kw.pop(attr, None))
+        if kw:
+            raise TypeError('unknown keyword argument %r' %
+                            iter(kw).next())
+
+    def iter_fields(self):
+        for name in self._fields:
+            try:
+                yield name, getattr(self, name)
+            except AttributeError:
+                pass
+
+    def iter_child_nodes(self):
+        for field, item in self.iter_fields():
+            if isinstance(item, list):
+                for n in item:
+                    if isinstance(n, Node):
+                        yield n
+            elif isinstance(item, Node):
+                yield item
+
+    def __repr__(self):
+        return '%s(%s)' % (
+            self.__class__.__name__,
+            ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for
+                      arg in self._fields)
+        )
+
+
+class Stmt(Node):
+    """
+    Base node for all statements.
+    """
+
+
+class Helper(Node):
+    """
+    Nodes that exist in a specific context only.
+    """
+
+
+class Template(Node):
+    """
+    Node that represents a template.
+    """
+    _fields = ('extends', 'body')
+
+
+class Output(Stmt):
+    """
+    A node that holds multiple expressions which are then printed out.  This
+    is used both for the `print` statement and the regular template data.
+    """
+    _fields = ('nodes',)
+
+
+class Extends(Stmt):
+    """
+    Represents an extends statement.
+    """
+    _fields = ('extends',)
+
+
+class For(Stmt):
+    """
+    A node that represents a for loop
+    """
+    _fields = ('item', 'seq', 'body', 'else_', 'recursive')
+
+
+class If(Stmt):
+    """
+    A node that represents an if condition.
+    """
+    _fields = ('test', 'body', 'else_')
+
+
+class Macro(Stmt):
+    """
+    A node that represents a macro.
+    """
+    _fields = ('name', 'arguments', 'body')
+
+
+class CallBlock(Stmt):
+    """
+    A node that represents am extended macro call.
+    """
+    _fields = ('expr', 'body')
+
+
+class Set(Stmt):
+    """
+    Allows defining own variables.
+    """
+    _fields = ('name', 'expr')
+
+
+class FilterBlock(Stmt):
+    """
+    Node for filter sections.
+    """
+    _fields = ('body', 'filters')
+
+
+class Block(Stmt):
+    """
+    A node that represents a block.
+    """
+    _fields = ('name', 'body')
+
+
+class Include(Stmt):
+    """
+    A node that represents the include tag.
+    """
+    _fields = ('template',)
+
+
+class Trans(Stmt):
+    """
+    A node for translatable sections.
+    """
+    _fields = ('singular', 'plural', 'indicator', 'replacements')
+
+
+class ExprStmt(Stmt):
+    """
+    A statement that evaluates an expression to None.
+    """
+    _fields = ('node',)
+
+
+class Expr(Node):
+    """
+    Baseclass for all expressions.
+    """
+
+    def as_const(self):
+        """
+        Return the value of the expression as constant or raise `Impossible`
+        if this was not possible.
+        """
+        raise Impossible()
+
+    def can_assign(self):
+        """
+        Check if it's possible to assign something to this node.
+        """
+        return False
+
+
+class BinExpr(Expr):
+    """
+    Baseclass for all binary expressions.
+    """
+    _fields = ('left', 'right')
+    operator = None
+
+    def as_const(self):
+        f = _binop_to_func[self.operator]
+        try:
+            return f(self.left.as_const(), self.right.as_const())
+        except:
+            print self.left, f, self.right
+            raise Impossible()
+
+
+class UnaryExpr(Expr):
+    """
+    Baseclass for all unary expressions.
+    """
+    _fields = ('node',)
+    operator = None
+
+    def as_const(self):
+        f = _uaop_to_func[self.operator]
+        try:
+            return f(self.node.as_const())
+        except:
+            raise Impossible()
+
+
+class Name(Expr):
+    """
+    any name such as {{ foo }}
+    """
+    _fields = ('name',)
+
+    def can_assign(self):
+        return True
+
+
+class Literal(Expr):
+    """
+    Baseclass for literals.
+    """
+
+
+class Const(Literal):
+    """
+    any constat such as {{ "foo" }}
+    """
+    _fields = ('value',)
+
+    def as_const(self):
+        return self.value
+
+
+class Tuple(Literal):
+    """
+    For loop unpacking and some other things like multiple arguments
+    for subscripts.
+    """
+    _fields = ('items',)
+
+    def as_const(self):
+        return tuple(x.as_const() for x in self.items)
+
+    def can_assign(self):
+        for item in self.items:
+            if not item.can_assign():
+                return False
+        return True
+
+
+class List(Literal):
+    """
+    any list literal such as {{ [1, 2, 3] }}
+    """
+    _fields = ('items',)
+
+    def as_const(self):
+        return [x.as_const() for x in self.items]
+
+
+class Dict(Literal):
+    """
+    any dict literal such as {{ {1: 2, 3: 4} }}
+    """
+    _fields = ('items',)
+
+    def as_const(self):
+        return dict(x.as_const() for x in self.items)
+
+
+class Pair(Helper):
+    """
+    A key, value pair for dicts.
+    """
+    _fields = ('key', 'value')
+
+    def as_const(self):
+        return self.key.as_const(), self.value.as_const()
+
+
+class CondExpr(Expr):
+    """
+    {{ foo if bar else baz }}
+    """
+    _fields = ('test', 'expr1', 'expr2')
+
+    def as_const(self):
+        if self.test.as_const():
+            return self.expr1.as_const()
+        return self.expr2.as_const()
+
+
+class Filter(Expr):
+    """
+    {{ foo|bar|baz }}
+    """
+    _fields = ('node', 'filters')
+
+
+class Test(Expr):
+    """
+    {{ foo is lower }}
+    """
+    _fields = ('node', 'name', 'args')
+
+
+class Call(Expr):
+    """
+    {{ foo(bar) }}
+    """
+    _fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+
+
+class Subscript(Expr):
+    """
+    {{ foo.bar }} and {{ foo['bar'] }} etc.
+    """
+    _fields = ('node', 'arg')
+
+    def as_const(self):
+        try:
+            return self.node.as_const()[self.node.as_const()]
+        except:
+            raise Impossible()
+
+    def can_assign(self):
+        return True
+
+
+class Slice(Expr):
+    """
+    1:2:3 etc.
+    """
+    _fields = ('start', 'stop', 'step')
+
+
+class Concat(Expr):
+    """
+    For {{ foo ~ bar }}.  Concatenates strings.
+    """
+    _fields = ('nodes',)
+
+    def as_const(self):
+        return ''.join(unicode(x.as_const()) for x in self.nodes)
+
+
+class Compare(Expr):
+    """
+    {{ foo == bar }}, {{ foo >= bar }} etc.
+    """
+    _fields = ('expr', 'ops')
+
+
+class Mul(BinExpr):
+    """
+    {{ foo * bar }}
+    """
+    operator = '*'
+
+
+class Div(BinExpr):
+    """
+    {{ foo / bar }}
+    """
+    operator = '/'
+
+
+class FloorDiv(BinExpr):
+    """
+    {{ foo // bar }}
+    """
+    operator = '//'
+
+
+class Add(BinExpr):
+    """
+    {{ foo + bar }}
+    """
+    operator = '+'
+
+
+class Sub(BinExpr):
+    """
+    {{ foo - bar }}
+    """
+    operator = '-'
+
+
+class Mod(BinExpr):
+    """
+    {{ foo % bar }}
+    """
+    operator = '%'
+
+
+class Pow(BinExpr):
+    """
+    {{ foo ** bar }}
+    """
+    operator = '**'
+
+
+class And(BinExpr):
+    """
+    {{ foo and bar }}
+    """
+    operator = 'and'
+
+    def as_const(self):
+        return self.left.as_const() and self.right.as_const()
+
+
+class Or(BinExpr):
+    """
+    {{ foo or bar }}
+    """
+    operator = 'or'
+
+    def as_const(self):
+        return self.left.as_const() or self.right.as_const()
+
+
+class Not(UnaryExpr):
+    """
+    {{ not foo }}
+    """
+    operator = 'not'
+
+
+class NegExpr(UnaryExpr):
+    """
+    {{ -foo }}
+    """
+    operator = '-'
+
+
+class PosExpr(UnaryExpr):
+    """
+    {{ +foo }}
+    """
+    operator = '+'
diff --git a/jinja2/parser.py b/jinja2/parser.py
new file mode 100644 (file)
index 0000000..d981c75
--- /dev/null
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.parser
+    ~~~~~~~~~~~~~
+
+    Implements the template parser.
+
+    :copyright: 2008 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from jinja import nodes
+from jinja.exceptions import TemplateSyntaxError
+
+
+__all__ = ['Parser']
+
+
+class Parser(object):
+    """
+    The template parser class.
+
+    Transforms sourcecode into an abstract syntax tree.
+    """
+
+    def __init__(self, environment, source, filename=None):
+        self.environment = environment
+        if isinstance(source, str):
+            source = source.decode(environment.template_charset, 'ignore')
+        if isinstance(filename, unicode):
+            filename = filename.encode('utf-8')
+        self.source = source
+        self.filename = filename
+        self.closed = False
+        self.blocks = set()
+        self.no_variable_block = self.environment.lexer.no_variable_block
+        self.stream = environment.lexer.tokenize(source, filename)
+
+    def parse(self):
+        pass
similarity index 100%
rename from jinja/tests.py
rename to jinja2/tests.py
diff --git a/jinja2/utils.py b/jinja2/utils.py
new file mode 100644 (file)
index 0000000..3f6395e
--- /dev/null
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.utils
+    ~~~~~~~~~~~~
+
+    Utility functions.
+
+    :copyright: 2008 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""