+++ /dev/null
-# -*- 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()
+++ /dev/null
-# -*- 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
+++ /dev/null
-# -*- 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
+++ /dev/null
-# -*- 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.
- """
+++ /dev/null
-# -*- 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)
+++ /dev/null
-# -*- 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 }}
- """
+++ /dev/null
-# -*- 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
+++ /dev/null
-# -*- 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
+++ /dev/null
-# -*- 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 ('(', '<', '<')]),
- '|'.join([re.escape(p) for p in ('.', ',', ')', '>', '\n', '>')])
- )
-)
-
-_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("&", "&").replace("<", "<").replace(">", ">")
- if not quote:
- return s
- return s.replace('"', """)
-
-
-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
-}
# -*- 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'
--- /dev/null
+# -*- 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()
"""
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']
--- /dev/null
+# -*- 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)
--- /dev/null
+# -*- 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.
+ """
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'(<!--.*?-->|<[^>]*>)')
# -*- 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.
"""
import unicodedata
from jinja.datastructure import TokenStream, Token
from jinja.exceptions import TemplateSyntaxError
-from jinja.utils import set, sorted
from weakref import 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 = {
'%': 'mod',
'**': 'pow',
'~': 'tilde',
- '!': 'bang',
- '@': 'at',
'[': 'lbracket',
']': 'rbracket',
'(': 'lparen',
'.': 'dot',
':': 'colon',
'|': 'pipe',
- ',': 'comma'
+ ',': 'comma',
+ ';': 'semicolon'
}
reverse_operators = dict([(v, k) for k, v in operators.iteritems()])
(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)
]
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':
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+ jinja.loaders
+ ~~~~~~~~~~~~~
+
+ Jinja loader classes.
+
+ :copyright: 2007 by Armin Ronacher, Bryan McLemore.
+ :license: BSD, see LICENSE for more details.
+"""
--- /dev/null
+# -*- 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 = '+'
--- /dev/null
+# -*- 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
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+ jinja2.utils
+ ~~~~~~~~~~~~
+
+ Utility functions.
+
+ :copyright: 2008 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""