PK5w6@U]4''jinja/datastructure.py# -*- 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. """ # sets try: set except NameError: from sets import Set as set from jinja.exceptions import TemplateSyntaxError, TemplateRuntimeError 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 class UndefinedType(object): """ An object that does not exist. """ __slots__ = () def __init__(self): try: Undefined except NameError: pass else: raise TypeError('cannot create %r instances' % self.__class__.__name__) __sub__ = __mul__ = __div__ = __rsub__ = __rmul__ = __div__ = __radd__ = \ __add__ = lambda self, other: other def __getitem__(self, arg): return self def __iter__(self): if False: yield None def __getattr__(self, arg): return self def __nonzero__(self): return False def __len__(self): return 0 def __str__(self): return '' def __unicode__(self): return u'' def __int__(self): return 0 def __float__(self): return 0.0 def __eq__(self, other): return False def __ne__(self, other): return True def __call__(self, *args, **kwargs): return self Undefined = UndefinedType() class FakeTranslator(object): """ Default null translator. """ def gettext(self, s): return s def ngettext(self, s, p, n): 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): """ Mark a string as safe for XML. If the environment uses the auto_escape option values marked as `Markup` aren't escaped. """ def __repr__(self): return 'Markup(%s)' % unicode.__repr__(self) class Context(object): """ Dict like object. """ def __init__(self, _environment_, *args, **kwargs): self.environment = _environment_ self._stack = [_environment_.globals, dict(*args, **kwargs), {}] self.globals, self.initial, self.current = self._stack # cache object used for filters and tests self.cache = {} def pop(self): """Pop the last layer from the stack and return it.""" rv = self._stack.pop() self.current = self._stack[-1] return rv def push(self, data=None): """Push a new dict or empty layer to the stack and return that layer""" data = data or {} self._stack.append(data) self.current = self._stack[-1] return data 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 __getitem__(self, name): # don't give access to jinja internal variables if name.startswith('::'): return Undefined # because the stack is usually quite small we better use [::-1] # which is faster than reversed() somehow. for d in self._stack[::-1]: if name in d: rv = d[name] if isinstance(rv, Deferred): rv = rv(self, name) # never touch the globals! if d is self.globals: self.initial[name] = rv else: d[name] = rv return rv return Undefined def __setitem__(self, name, value): self.current[name] = value def __delitem__(self, name): if name in self.current: del self.current[name] def __contains__(self, name): for layer in self._stack: if name in layer: return True return False def __repr__(self): tmp = {} for d in self._stack: for key, value in d.iteritems(): tmp[key] = value return 'Context(%s)' % repr(tmp) 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 seq is not None: self.push(seq) def push(self, seq): """ Push a sequence to the loop stack. This is used by the recursive for loop. """ if seq in (Undefined, None): seq = () self._stack.append({ 'index': -1, 'seq': seq, 'length': len(seq) }) 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 '' % ( self.index, self.length, self.loop_function is not None and ' recursive' or '' ) return '' class CycleContext(object): """ Helper class used for cycling. """ def __init__(self, seq=None): self.lineno = -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.lineno = (self.lineno + 1) % self.length return self.seq[self.lineno] def cycle_dynamic(self, seq): """Helper function for dynamic cycling.""" self.lineno = (self.lineno + 1) % len(seq) return seq[self.lineno] class TokenStream(object): """ A token stream works like a normal generator just that it supports pushing tokens back to the stream. """ def __init__(self, generator): self._generator = generator self._pushed = [] self.last = (1, 'initial', '') def __iter__(self): return self def __nonzero__(self): """Are we at the end of the tokenstream?""" if self._pushed: return True try: self.push(self.next()) except StopIteration: return False return True eos = property(lambda x: not x.__nonzero__(), doc=__nonzero__.__doc__) def next(self): """Return the next token from the stream.""" if self._pushed: rv = self._pushed.pop() else: rv = self._generator.next() self.last = rv return rv def look(self): """Pop and push a token, return it.""" token = self.next() self.push(*token) return token def fetch_until(self, test, drop_needle=False): """Fetch tokens until a function matches.""" try: while True: token = self.next() if test(*token): if not drop_needle: self.push(*token) return else: yield token except StopIteration: raise TemplateSyntaxError('end of stream reached') def drop_until(self, test, drop_needle=False): """Fetch tokens until a function matches and drop all tokens.""" for token in self.fetch_until(test, drop_needle): pass def push(self, lineno, token, data): """Push an yielded token back to the stream.""" self._pushed.append((lineno, token, data)) PK5w6 EEjinja/filters.py# -*- coding: utf-8 -*- """ jinja.filters ~~~~~~~~~~~~~ Bundled jinja filters. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from random import choice from urllib import urlencode, quote from jinja.utils import escape, urlize from jinja.datastructure import Undefined from jinja.exceptions import FilterArgumentError try: _reversed = reversed except NameError: # python2.3 compatibility hack for the do_reverse function def _reversed(seq): try: return seq[::-1] except: try: return list(seq)[::-1] except: raise TypeError('argument to _reversed must ' 'be a sequence') def stringfilter(f): """ Decorator for filters that just work on unicode objects. """ def decorator(*args): def wrapped(env, context, value): nargs = list(args) for idx, var in enumerate(nargs): if isinstance(var, str): nargs[idx] = env.to_unicode(var) return f(env.to_unicode(value), *nargs) return wrapped try: decorator.__doc__ = f.__doc__ decorator.__name__ = f.__name__ except: pass return decorator def do_replace(s, old, new, count=None): """ Return a copy of the value with all occurrences of a substring replaced with a new one. The first argument is the substring that should be replaced, the second is the replacement string. If the optional third argument ``count`` is given, only the first ``count`` occurrences are replaced: .. sourcecode:: jinja {{ "Hello World"|replace("Hello", "Goodbye") }} -> Goodbye World {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} -> d'oh, d'oh, aaargh """ if not isinstance(old, basestring) or \ not isinstance(new, basestring): raise FilterArgumentException('the replace filter requires ' 'string replacement arguments') elif not isinstance(count, (int, long)): raise FilterArgumentException('the count parameter of the ' 'replace filter requires ' 'an integer') if count is None: return s.replace(old, new) return s.replace(old, new, count) do_replace = stringfilter(do_replace) def do_upper(s): """ Convert a value to uppercase. """ return s.upper() do_upper = stringfilter(do_upper) def do_lower(s): """ Convert a value to lowercase. """ return s.lower() do_lower = stringfilter(do_lower) def do_escape(s, attribute=False): """ XML escape ``&``, ``<``, and ``>`` in a string of data. If the optional parameter is `true` this filter will also convert ``"`` to ``"``. This filter is just used if the environment was configured with disabled `auto_escape`. This method will have no effect it the value is already escaped. """ return escape(s, attribute) do_escape = stringfilter(do_escape) def do_capitalize(s): """ Capitalize a value. The first character will be uppercase, all others lowercase. """ return s.capitalize() do_capitalize = stringfilter(do_capitalize) def do_title(s): """ Return a titlecased version of the value. I.e. words will start with uppercase letters, all remaining characters are lowercase. """ return s.title() do_title = stringfilter(do_title) def do_dictsort(case_sensitive=False, by='key'): """ Sort a dict and yield (key, value) pairs. Because python dicts are unsorted you may want to use this function to order them by either key or value: .. sourcecode:: jinja {% for item in mydict|dictsort %} sort the dict by key, case insensitive {% for item in mydict|dicsort(true) %} sort the dict by key, case sensitive {% for item in mydict|dictsort(false, 'value') %} sort the dict by key, case insensitive, sorted normally and ordered by value. """ if by == 'key': pos = 0 elif by == 'value': pos = 1 else: raise FilterArgumentError('You can only sort by either ' '"key" or "value"') def sort_func(value, env): if isinstance(value, basestring): value = env.to_unicode(value) if not case_sensitive: value = value.lower() return value def wrapped(env, context, value): items = value.items() items.sort(lambda a, b: cmp(sort_func(a[pos], env), sort_func(b[pos], env))) return items return wrapped def do_default(default_value=u'', boolean=False): """ If the value is undefined it will return the passed default value, otherwise the value of the variable: .. sourcecode:: jinja {{ my_variable|default('my_variable is not defined') }} This will output the value of ``my_variable`` if the variable was defined, otherwise ``'my_variable is not defined'``. If you want to use default with variables that evaluate to false you have to set the second parameter to `true`: .. sourcecode:: jinja {{ ''|default('the string was empty', true) }} """ def wrapped(env, context, value): if (boolean and not value) or value in (Undefined, None): return default_value return value return wrapped def do_join(d=u''): """ Return a string which is the concatenation of the strings in the sequence. The separator between elements is an empty string per default, you can define ith with the optional parameter: .. sourcecode:: jinja {{ [1, 2, 3]|join('|') }} -> 1|2|3 {{ [1, 2, 3]|join }} -> 123 """ def wrapped(env, context, value): return env.to_unicode(d).join([env.to_unicode(x) for x in value]) return wrapped def do_count(): """ Return the length of the value. In case if getting an integer or float it will convert it into a string an return the length of the new string. If the object has no length it will of corse return 0. """ def wrapped(env, context, value): try: if type(value) in (int, float, long): return len(str(value)) return len(value) except TypeError: return 0 return wrapped def do_reverse(): """ Return a reversed list of the sequence filtered. You can use this for example for reverse iteration: .. sourcecode:: jinja {% for item in seq|reverse %} {{ item|e }} {% endfor %} """ def wrapped(env, context, value): try: return value[::-1] except: l = list(value) l.reverse() return l return wrapped def do_center(value, width=80): """ Centers the value in a field of a given width. """ return value.center(width) do_center = stringfilter(do_center) def do_first(): """ Return the frist item of a sequence. """ def wrapped(env, context, seq): try: return iter(seq).next() except StopIteration: return Undefined return wrapped def do_last(): """ Return the last item of a sequence. """ def wrapped(env, context, seq): try: return iter(_reversed(seq)).next() except (TypeError, StopIteration): return Undefined return wrapped def do_random(): """ Return a random item from the sequence. """ def wrapped(env, context, seq): try: return choice(seq) except: return Undefined return wrapped def do_urlencode(): """ urlencode a string or directory. .. sourcecode:: jinja {{ {'foo': 'bar', 'blub': 'blah'}|urlencode }} -> foo=bar&blub=blah {{ 'Hello World' }} -> Hello%20World """ def wrapped(env, context, value): if isinstance(value, dict): tmp = {} for key, value in value.iteritems(): tmp[env.to_unicode(key)] = env.to_unicode(value) return urlencode(tmp) else: return quote(env.to_unicode(value)) return wrapped def do_jsonencode(): """ JSON dump a variable. just works if simplejson is installed. .. sourcecode:: jinja {{ 'Hello World'|jsonencode }} -> "Hello World" """ global simplejson try: simplejson except NameError: import simplejson return lambda e, c, v: simplejson.dumps(v) def do_filesizeformat(): """ Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102 bytes, etc). """ def wrapped(env, context, value): # fail silently try: bytes = float(value) except TypeError: bytes = 0 if bytes < 1024: return "%d Byte%s" % (bytes, bytes != 1 and 's' or '') elif bytes < 1024 * 1024: return "%.1f KB" % (bytes / 1024) elif bytes < 1024 * 1024 * 1024: return "%.1f MB" % (bytes / (1024 * 1024)) return "%.1f GB" % (bytes / (1024 * 1024 * 1024)) return wrapped def do_pprint(): """ Pretty print a variable. Useful for debugging. """ def wrapped(env, context, value): from pprint import pformat return pformat(value) return wrapped def do_urlize(value, trim_url_limit=None, nofollow=False): """ Converts URLs in plain text into clickable links. If you pass the filter an additional integer it will shorten the urls to that number. Also a third argument exists that makes the urls "nofollow": .. sourcecode:: jinja {{ mytext|urlize(40, True) }} links are shortened to 40 chars and defined with rel="nofollow" """ return urlize(value, trim_url_limit, nofollow) do_urlize = stringfilter(do_urlize) def do_indent(s, width=4, indentfirst=False): """ {{ s|indent[ width[ indentfirst[ usetab]]] }} Return a copy of the passed string, each line indented by 4 spaces. The first line is not indented. If you want to change the number of spaces or indent the first line too you can pass additional parameters to the filter: .. sourcecode:: jinja {{ mytext|indent(2, True) }} indent by two spaces and indent the first line too. """ indention = ' ' * width if indentfirst: return u'\n'.join([indention + line for line in s.splitlines()]) return s.replace('\n', '\n' + indention) do_indent = stringfilter(do_indent) def do_truncate(s, length=255, killwords=False, end='...'): """ Return a truncated copy of the string. The length is specified with the first parameter which defaults to ``255``. If the second parameter is ``true`` the filter will cut the text at length. Otherwise it will try to save the last word. If the text was in fact truncated it will append an ellipsis sign (``"..."``). If you want a different ellipsis sign than ``"..."`` you can specify it using the third parameter. .. sourcecode jinja:: {{ mytext|truncate(300, false, '»') }} truncate mytext to 300 chars, don't split up words, use a right pointing double arrow as ellipsis sign. """ if len(s) <= length: return s elif killwords: return s[:length] + end words = s.split(' ') result = [] m = 0 for word in words: m += len(word) + 1 if m > length: break result.append(word) result.append(end) return u' '.join(result) do_truncate = stringfilter(do_truncate) def do_wordwrap(s, pos=79, hard=False): """ Return a copy of the string passed to the filter wrapped after ``79`` characters. You can override this default using the first parameter. If you set the second parameter to `true` Jinja will also split words apart (usually a bad idea because it makes reading hard). """ if len(s) < pos: return s if hard: return u'\n'.join([s[idx:idx + pos] for idx in xrange(0, len(s), pos)]) # code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061 return reduce(lambda line, word, pos=pos: u'%s%s%s' % (line, u' \n'[(len(line)-line.rfind('\n') - 1 + len(word.split('\n', 1)[0]) >= pos)], word), s.split(' ')) do_wordwrap = stringfilter(do_wordwrap) def do_wordcount(s): """ Count the words in that string. """ return len([x for x in s.split() if x]) do_wordcount = stringfilter(do_wordcount) def do_textile(s): """ Prase the string using textile. requires the `PyTextile`_ library. .. _PyTextile: http://dealmeida.net/projects/textile/ """ from textile import textile return textile(s) do_textile = stringfilter(do_textile) def do_markdown(s): """ Parse the string using markdown. requires the `Python-markdown`_ library. .. _Python-markdown: http://www.freewisdom.org/projects/python-markdown/ """ from markdown import markdown return markdown(s) do_markdown = stringfilter(do_markdown) def do_rst(s): """ Parse the string using the reStructuredText parser from the docutils package. requires `docutils`_. .. _docutils: from http://docutils.sourceforge.net/ """ try: from docutils.core import publish_parts parts = publish_parts(source=s, writer_name='html4css1') return parts['fragment'] except: return s do_rst = stringfilter(do_rst) def do_int(default=0): """ Convert the value into an integer. If the conversion doesn't work it will return ``0``. You can override this default using the first parameter. """ def wrapped(env, context, value): try: return int(value) except (TypeError, ValueError): try: return int(float(value)) except (TypeError, ValueError): return default return wrapped def do_float(default=0.0): """ Convert the value into a floating point number. If the conversion doesn't work it will return ``0.0``. You can override this default using the first parameter. """ def wrapped(env, context, value): try: return float(value) except (TypeError, ValueError): return default return wrapped def do_string(): """ Convert the value into an string. """ return lambda e, c, v: e.to_unicode(v) def do_format(*args): """ Apply python string formatting on an object: .. sourcecode:: jinja {{ "%s - %s"|format("Hello?", "Foo!") }} -> Hello? - Foo! Note that you cannot use the mapping syntax (``%(name)s``) like in python. """ def wrapped(env, context, value): return env.to_unicode(value) % args return wrapped def do_trim(value): """ Strip leading and trailing whitespace. """ return value.strip() do_trim = stringfilter(do_trim) def do_capture(name='captured', clean=False): """ Store the value in a variable called ``captured`` or a variable with the name provided. Useful for filter blocks: .. sourcecode:: jinja {% filter capture('foo') %} ... {% endfilter %} {{ foo }} This will output "..." two times. One time from the filter block and one time from the variable. If you don't want the filter to output something you can use it in `clean` mode: .. sourcecode:: jinja {% filter capture('foo', True) %} ... {% endfilter %} {{ foo }} """ if not isinstance(name, unicode): raise FilterArgumentError('You can only capture into variables') def wrapped(env, context, value): context[name] = value if clean: return Undefined return value return wrapped FILTERS = { 'replace': do_replace, 'upper': do_upper, 'lower': do_lower, 'escape': do_escape, 'e': do_escape, 'capitalize': do_capitalize, 'title': do_title, 'default': do_default, 'join': do_join, 'count': do_count, 'dictsort': do_dictsort, 'length': do_count, 'reverse': do_reverse, 'center': do_center, 'title': do_title, 'capitalize': do_capitalize, 'first': do_first, 'last': do_last, 'random': do_random, 'urlencode': do_urlencode, 'jsonencode': do_jsonencode, 'filesizeformat': do_filesizeformat, 'pprint': do_pprint, 'indent': do_indent, 'truncate': do_truncate, 'wordwrap': do_wordwrap, 'wordcount': do_wordcount, 'textile': do_textile, 'markdown': do_markdown, 'rst': do_rst, 'int': do_int, 'float': do_float, 'string': do_string, 'urlize': do_urlize, 'format': do_format, 'capture': do_capture, 'trim': do_trim } PK5w6™jinja/nodes.py# -*- coding: utf-8 -*- """ jinja.nodes ~~~~~~~~~~~ Additional nodes for Jinja. Look like nodes from the ast. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from compiler import ast def inc_lineno(offset, tree): """ Increment the linenumbers of all nodes in tree with offset. """ todo = [tree] while todo: node = todo.pop() if node.lineno: node.lineno += offset - 1 else: node.lineno = offset todo.extend(node.getChildNodes()) def get_nodes(nodetype, tree): """ Get all nodes from nodetype in the tree. """ todo = [tree] while todo: node = todo.pop() if node.__class__ is nodetype: yield node todo.extend(node.getChildNodes()) class Node(ast.Node): """ Jinja node. """ def get_items(self): return [] def getChildren(self): return self.get_items() def getChildNodes(self): return [x for x in self.get_items() if isinstance(x, ast.Node)] class Text(Node): """ Node that represents normal text. """ def __init__(self, lineno, text): self.lineno = lineno self.text = text def get_items(self): return [self.text] def __repr__(self): return 'Text(%r)' % (self.text,) class NodeList(list, Node): """ A node that stores multiple childnodes. """ def __init__(self, lineno, data=None): self.lineno = lineno list.__init__(self, data or ()) getChildren = getChildNodes = lambda s: list(s) + s.get_items() def __repr__(self): return 'NodeList(%s)' % list.__repr__(self) class Template(NodeList): """ Node that represents a template. """ def __init__(self, filename, body, extends): if body.__class__ is not NodeList: body = (body,) NodeList.__init__(self, 1, body) self.extends = extends self.filename = filename def get_items(self): return self.extends is not None and [self.extends] or [] def __repr__(self): return 'Template(%r, %r, %r)' % ( self.filename, self.extends, NodeList.__repr__(self) ) class ForLoop(Node): """ A node that represents a for loop """ def __init__(self, lineno, item, seq, body, else_, recursive): self.lineno = lineno 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, lineno, tests, else_): self.lineno = lineno 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, lineno, seq): self.lineno = lineno 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, lineno, variable): self.lineno = lineno self.variable = variable def get_items(self): return [self.variable] def __repr__(self): return 'Print(%r)' % (self.variable,) class Macro(Node): """ A node that represents a macro. """ def __init__(self, lineno, name, arguments, body): self.lineno = lineno self.name = name self.arguments = arguments self.body = body def get_items(self): result = [self.name] if self.arguments: for item in self.arguments: result.extend(item) result.append(self.body) return result def __repr__(self): return 'Macro(%r, %r, %r)' % ( self.name, self.arguments, self.body ) class Set(Node): """ Allow defining own variables. """ def __init__(self, lineno, name, expr): self.lineno = lineno self.name = name self.expr = expr def get_items(self): return [self.name, self.expr] def __repr__(self): return 'Set(%r, %r)' % ( self.name, self.expr ) class Filter(Node): """ Node for filter sections. """ def __init__(self, lineno, body, filters): self.lineno = lineno 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, lineno, name, body): self.lineno = lineno self.name = name self.body = body def replace(self, node): """ Replace the current data with the data of another block node. """ assert node.__class__ is Block self.__dict__.update(node.__dict__) def get_items(self): return [self.name, self.body] def __repr__(self): return 'Block(%r, %r)' % ( self.name, self.body ) class Extends(Node): """ A node that represents the extends tag. """ def __init__(self, lineno, template): self.lineno = lineno self.template = template def get_items(self): return [self.template] def __repr__(self): return 'Extends(%r)' % self.template class Include(Node): """ A node that represents the include tag. """ def __init__(self, lineno, template): self.lineno = lineno 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, lineno, singular, plural, indicator, replacements): self.lineno = lineno 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 ) PK5w69`KHKHjinja/loaders.py# -*- coding: utf-8 -*- """ jinja.loaders ~~~~~~~~~~~~~ Jinja loader classes. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import codecs import sha 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 from jinja.utils import CacheDict, raise_syntax_error try: from pkg_resources import resource_exists, resource_string, \ resource_filename except ImportError: resource_exists = resource_string = resource_filename = None __all__ = ['FileSystemLoader', 'PackageLoader', 'DictLoader', 'ChoiceLoader', 'FunctionLoader'] def get_template_filename(searchpath, name): """ Return the filesystem filename wanted. """ return path.join(searchpath, path.sep.join([p for p in name.split('/') if p and p[0] != '.'])) def get_cachename(cachepath, name): """ Return the filename for a cached file. """ return path.join(cachepath, 'jinja_%s.cache' % sha.new('jinja(%s)tmpl' % name).hexdigest()) 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 = self._loader_missing self.available = False else: self.available = True 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: __traceback_hide__ = True raise_syntax_error(e, self.environment) 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.loader is not None 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) class CachedLoaderMixin(object): """ Mixin this class to implement simple memory and disk caching. """ def __init__(self, use_memcache, cache_size, cache_folder, auto_reload): 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.__times = {} 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 temlate using the `LoaderMixin.load` function, cache and return it. """ self.__lock.acquire() try: # caching is only possible for the python translator. skip # all other translators if translator is PythonTranslator: tmpl = None # 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 and name in self.__memcache: tmpl = self.__memcache[name] if last_change is not None and \ last_change > self.__times[name]: tmpl = None # if diskcache is enabled look for an already compiled # template. if self.__cache_folder is not None: cache_fn = get_cachename(self.__cache_folder, name) # there is an up to date compiled template if tmpl is not None and last_change is None: try: cache_time = path.getmtime(cache_fn) except OSError: cache_time = -1 if cache_time == -1 or last_change >= cache_time: f = file(cache_fn, 'rb') try: tmpl = Template.load(environment, f) finally: f.close() # no template so far, parse, translate and compile it elif tmpl is None: tmpl = super(CachedLoaderMixin, self).load( environment, name, translator) # save the compiled template f = file(cache_fn, 'wb') try: tmpl.dump(f) finally: f.close() # if memcaching is enabled push the template if tmpl is not None: if self.__memcache is not None: self.__times[name] = time.time() self.__memcache[name] = tmpl return tmpl # if we reach this point we don't have caching enabled or translate # to something else than python return super(CachedLoaderMixin, self).load( environment, name, translator) finally: self.__lock.release() class FileSystemLoader(CachedLoaderMixin, BaseLoader): """ 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 initialisation: =================== ================================================= ``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. =================== ================================================= """ def __init__(self, searchpath, use_memcache=False, memcache_size=40, cache_folder=None, auto_reload=True): self.searchpath = searchpath CachedLoaderMixin.__init__(self, use_memcache, memcache_size, cache_folder, auto_reload) def get_source(self, environment, name, parent): filename = get_template_filename(self.searchpath, name) if path.exists(filename): f = codecs.open(filename, 'r', environment.template_charset) try: return f.read() finally: f.close() else: raise TemplateNotFound(name) def check_source_changed(self, environment, name): filename = get_template_filename(self.searchpath, name) if path.exists(filename): return path.getmtime(filename) return -1 class PackageLoader(CachedLoaderMixin, BaseLoader): """ 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 initialisation: =================== ================================================= ``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. =================== ================================================= """ def __init__(self, package_name, package_path, use_memcache=False, memcache_size=40, cache_folder=None, auto_reload=True): if resource_filename is None: raise ImportError('setuptools not found') self.package_name = package_name self.package_path = package_path # if we have an loader we probably retrieved it from an egg # file. In that case don't use the auto_reload! if auto_reload and getattr(__import__(package_name, '', '', ['']), '__loader__', None) is not None: auto_reload = False CachedLoaderMixin.__init__(self, use_memcache, memcache_size, cache_folder, auto_reload) def get_source(self, environment, name, parent): name = '/'.join([self.package_path] + [p for p in name.split('/') if p != '..']) if not resource_exists(self.package_name, name): raise TemplateNotFound(name) contents = resource_string(self.package_name, name) return contents.decode(environment.template_charset) def check_source_changed(self, environment, name): 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 FunctionLoader(CachedLoaderMixin, BaseLoader): """ 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 initialisation: =================== ================================================= ``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. =================== ================================================= """ def __init__(self, loader_func, getmtime_func=None, use_memcache=False, memcache_size=40, cache_folder=None, auto_reload=True): # when changing the signature also check the jinja.plugin function # loader instantiation. self.loader_func = loader_func 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) 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 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' %}...' ))) """ 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: continue raise TemplateNotFound(name) def parse(self, environment, name, parent): for loader in self.loaders: try: return loader.parse(environment, name, parent) except TemplateNotFound: continue raise TemplateNotFound(name) def load(self, environment, name, translator): for loader in self.loaders: try: return loader.load(environment, name, translator) except TemplateNotFound: continue raise TemplateNotFound(name) PK5w6+3jinja/exceptions.py# -*- 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, TemplateError): """ Raised if a template does not exist. """ def __init__(self, message): IOError.__init__(self, message) 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. """ PKv~w6>4`0nnjinja/parser.py# -*- coding: utf-8 -*- """ jinja.parser ~~~~~~~~~~~~ Implements the template parser. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import re from compiler import ast, parse from compiler.misc import set_filename from jinja import nodes from jinja.datastructure import TokenStream from jinja.exceptions import TemplateSyntaxError try: set except NameError: from sets import Set as set # callback functions for the subparse method end_of_block = lambda p, t, d: t == 'block_end' end_of_variable = lambda p, t, d: t == 'variable_end' end_of_comment = lambda p, t, d: t == 'comment_end' switch_for = lambda p, t, d: t == 'name' and d in ('else', 'endfor') end_of_for = lambda p, t, d: t == 'name' and d == 'endfor' switch_if = lambda p, t, d: t == 'name' and d in ('else', 'elif', 'endif') end_of_if = lambda p, t, d: t == 'name' and d == 'endif' end_of_filter = lambda p, t, d: t == 'name' and d == 'endfilter' end_of_macro = lambda p, t, d: t == 'name' and d == 'endmacro' end_of_block_tag = lambda p, t, d: t == 'name' and d == 'endblock' end_of_trans = lambda p, t, d: t == 'name' and d == 'endtrans' string_inc_re = re.compile(r'(?:[^\d]*(\d+)[^\d]*)+') def inc_string(s): """ Increment a string """ m = string_inc_re.search(s) if m: next = str(int(m.group(1)) + 1) start, end = m.span(1) s = s[:max(end - len(next), start)] + next + s[end:] else: name, ext = s.rsplit('.', 1) return '%s2.%s' % (name, ext) return s 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') self.source = source self.filename = filename self.tokenstream = environment.lexer.tokenize(source) self.extends = None self.blocks = set() self.directives = { 'raw': self.handle_raw_directive, 'for': self.handle_for_directive, 'if': self.handle_if_directive, 'cycle': self.handle_cycle_directive, 'set': self.handle_set_directive, 'filter': self.handle_filter_directive, 'print': self.handle_print_directive, 'macro': self.handle_macro_directive, 'block': self.handle_block_directive, 'extends': self.handle_extends_directive, 'include': self.handle_include_directive, 'trans': self.handle_trans_directive } def handle_raw_directive(self, lineno, gen): """ 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. """ args = list(gen) if args: raise TemplateSyntaxError('raw directive does not support ' 'any arguments.', lineno, self.filename) raise TemplateSyntaxError('missing end tag for raw directive.', lineno, self.filename) def handle_for_directive(self, lineno, gen): """ Handle a for directive and return a ForLoop node """ #XXX: maybe we could make the "recurse" part optional by using # a static analysis later. recursive = [] def wrapgen(): """Wrap the generator to check if we have a recursive for loop.""" for token in gen: if token[1:] == ('name', 'recursive'): try: item = gen.next() except StopIteration: recursive.append(True) return yield token yield item else: yield token ast = self.parse_python(lineno, wrapgen(), 'for %s:pass') body = self.subparse(switch_for) # do we have an else section? if self.tokenstream.next()[2] == 'else': self.close_remaining_block() else_ = self.subparse(end_of_for, True) else: else_ = None self.close_remaining_block() return nodes.ForLoop(lineno, ast.assign, ast.list, body, else_, bool(recursive)) def handle_if_directive(self, lineno, gen): """ Handle if/else blocks. """ ast = self.parse_python(lineno, gen, 'if %s:pass') tests = [(ast.tests[0][0], self.subparse(switch_if))] # do we have an else section? while True: lineno, token, needle = self.tokenstream.next() if needle == 'else': self.close_remaining_block() else_ = self.subparse(end_of_if, True) break elif needle == 'elif': gen = self.tokenstream.fetch_until(end_of_block, True) ast = self.parse_python(lineno, gen, 'if %s:pass') tests.append((ast.tests[0][0], self.subparse(switch_if))) else: else_ = None break self.close_remaining_block() return nodes.IfCondition(lineno, tests, else_) def handle_cycle_directive(self, lineno, gen): """ Handle {% cycle foo, bar, baz %}. """ ast = self.parse_python(lineno, gen, '_cycle((%s))') # ast is something like Discard(CallFunc(Name('_cycle'), ...)) # skip that. return nodes.Cycle(lineno, ast.expr.args[0]) def handle_set_directive(self, lineno, gen): """ Handle {% set foo = 'value of foo' %}. """ try: name = gen.next() if name[1] != 'name' or gen.next()[1:] != ('operator', '='): raise ValueError() except (StopIteration, ValueError): raise TemplateSyntaxError('invalid syntax for set', lineno, self.filename) ast = self.parse_python(lineno, gen, '(%s)') # disallow keywords if not name[2].endswith('_'): raise TemplateSyntaxError('illegal use of keyword %r ' 'as identifier in set statement.' % name[2], lineno, self.filename) return nodes.Set(lineno, str(name[2][:-1]), ast.expr) def handle_filter_directive(self, lineno, gen): """ Handle {% filter foo|bar %} directives. """ ast = self.parse_python(lineno, gen, '_filter(dummy|%s)') body = self.subparse(end_of_filter, True) self.close_remaining_block() return nodes.Filter(lineno, body, ast.expr.args[0].nodes[1:]) def handle_print_directive(self, lineno, gen): """ Handle {{ foo }} and {% print foo %}. """ ast = self.parse_python(lineno, gen, 'print_(%s)') # ast is something like Discard(CallFunc(Name('print_'), ...)) # so just use the args arguments = ast.expr.args # we only accept one argument if len(arguments) != 1: raise TemplateSyntaxError('invalid argument count for print; ' 'print requires exactly one argument, ' 'got %d.' % len(arguments), lineno, self.filename) return nodes.Print(lineno, arguments[0]) def handle_macro_directive(self, lineno, gen): """ Handle {% macro foo bar, baz %}. """ try: macro_name = gen.next() except StopIteration: raise TemplateSyntaxError('macro requires a name', lineno, self.filename) if macro_name[1] != 'name': raise TemplateSyntaxError('expected \'name\', got %r' % macro_name[1], lineno, self.filename) # disallow keywords as identifiers elif not macro_name[2].endswith('_'): raise TemplateSyntaxError('illegal use of keyword %r ' 'as macro name.' % macro_name[2], lineno, self.filename) ast = self.parse_python(lineno, gen, 'def %s(%%s):pass' % str(macro_name[2][:-1])) body = self.subparse(end_of_macro, True) self.close_remaining_block() if ast.varargs or ast.kwargs: raise TemplateSyntaxError('variable length macro signature ' 'not allowed.', lineno, self.filename) if ast.argnames: defaults = [None] * (len(ast.argnames) - len(ast.defaults)) + \ ast.defaults args = [] for idx, argname in enumerate(ast.argnames): # disallow keywords as argument names if not argname.endswith('_'): raise TemplateSyntaxError('illegal use of keyword %r ' 'as macro argument.' % argname, lineno, self.filename) args.append((argname[:-1], defaults[idx])) else: args = None return nodes.Macro(lineno, ast.name, args, body) def handle_block_directive(self, lineno, gen): """ Handle block directives used for inheritance. """ tokens = list(gen) if not tokens: raise TemplateSyntaxError('block requires a name', lineno, self.filename) block_name = tokens.pop(0) if block_name[1] != 'name': raise TemplateSyntaxError('expected \'name\', got %r' % block_name[1], lineno, seilf.filename) # disallow keywords if not block_name[2].endswith('_'): raise TemplateSyntaxError('illegal use of keyword %r ' 'as block name.' % block_name[2], lineno, self.filename) name = block_name[2][:-1] if tokens: raise TemplateSyntaxError('block got too many arguments, ' 'requires one.', lineno, self.filename) # check if this block does not exist by now. if name in self.blocks: raise TemplateSyntaxError('block %r defined twice' % name, lineno, self.filename) self.blocks.add(name) # now parse the body and attach it to the block body = self.subparse(end_of_block_tag, True) self.close_remaining_block() return nodes.Block(lineno, name, body) def handle_extends_directive(self, lineno, gen): """ Handle the extends directive used for inheritance. """ tokens = list(gen) if len(tokens) != 1 or tokens[0][1] != 'string': raise TemplateSyntaxError('extends requires a string', lineno, self.filename) if self.extends is not None: raise TemplateSyntaxError('extends called twice', lineno) self.extends = nodes.Extends(lineno, tokens[0][2][1:-1]) def handle_include_directive(self, lineno, gen): """ Handle the include directive used for template inclusion. """ tokens = list(gen) if len(tokens) != 1 or tokens[0][1] != 'string': raise TemplateSyntaxError('include requires a string', lineno, self.filename) return nodes.Include(lineno, tokens[0][2][1:-1]) def handle_trans_directive(self, lineno, gen): """ Handle translatable sections. """ # save the initial line number for the resulting node flineno = lineno try: # check for string translations try: lineno, token, data = gen.next() except StopIteration: # no dynamic replacements replacements = {} first_var = None else: if token == 'string': # check that there are not any more elements try: gen.next() except StopIteration: #XXX: what about escapes? return nodes.Trans(lineno, data[1:-1], None, None, None) raise TemplateSyntaxError('string based translations ' 'require at most one argument.', lineno, self.filename) # create a new generator with the popped item as first one def wrapgen(oldgen): yield lineno, token, data for item in oldgen: yield item gen = wrapgen(gen) # block based translations first_var = None replacements = {} for arg in self.parse_python(lineno, gen, '_trans(%s)').expr.args: if arg.__class__ is not ast.Keyword: raise TemplateSyntaxError('translation tags need expl' 'icit names for values.', lineno, self.filename) # disallow keywords if not arg.name.endswith('_'): raise TemplateSyntaxError('illegal use of keyword %r ' 'as identifier.' % arg.name, lineno, self.filename) # remove the last "_" before writing if first_var is None: first_var = arg.name[:-1] replacements[arg.name[:-1]] = arg.expr # look for endtrans/pluralize buf = singular = [] plural = indicator = None while True: lineno, token, data = self.tokenstream.next() # comments if token == 'comment_begin': self.tokenstream.drop_until(end_of_comment, True) # nested variables elif token == 'variable_begin': _, variable_token, variable_name = self.tokenstream.next() if variable_token != 'name': raise TemplateSyntaxError('can only use variable not ' 'constants or expressions ' 'in translation variable ' 'blocks.', lineno, self.filename) # plural name without trailing "_"? that's a keyword if not variable_name.endswith('_'): raise TemplateSyntaxError('illegal use of keyword ' '%r as identifier in trans ' 'block.' % variable_name, lineno, self.filename) variable_name = variable_name[:-1] if variable_name not in replacements: raise TemplateSyntaxError('unregistered translation ' 'variable %r.' % variable_name, lineno, self.filename) if self.tokenstream.next()[1] != 'variable_end': raise TemplateSyntaxError('you cannot use variable ' 'expressions inside trans ' 'tags. apply filters ' 'in the trans header.', lineno, self.filename) buf.append('%%(%s)s' % variable_name) # nested blocks are not supported, just look for end blocks elif token == 'block_begin': _, block_token, block_name = self.tokenstream.next() if block_token != 'name' or \ block_name not in ('pluralize', 'endtrans'): raise TemplateSyntaxError('blocks in translatable ' 'sections are not ' 'supported', lineno, self.filename) # pluralize if block_name == 'pluralize': if plural is not None: raise TemplateSyntaxError('translation blocks ' 'support at most one ' 'plural block', lineno, self.filename) _, plural_token, plural_name = self.tokenstream.next() if plural_token == 'block_end': indicator = first_var elif plural_token == 'name': # disallow keywords if not plural_name.endswith('_'): raise TemplateSyntaxError('illegal use of ' 'keyword %r as ' 'identifier.' % plural_name, lineno, self.filename) plural_name = plural_name[:-1] if plural_name not in replacements: raise TemplateSyntaxError('unregistered ' 'translation ' 'variable %r' % plural_name, lineno, self.filename) elif self.tokenstream.next()[1] != 'block_end': raise TemplateSyntaxError('pluralize takes ' 'at most one ' 'argument', lineno, self.filename) indicator = plural_name else: raise TemplateSyntaxError('pluralize requires no ' 'argument or a variable' ' name.', lineno, self.filename) plural = buf = [] # end translation elif block_name == 'endtrans': self.close_remaining_block() break # normal data else: buf.append(data.replace('%', '%%')) except StopIteration: raise TemplateSyntaxError('unexpected end of translation section', self.tokenstream.last[0], self.filename) singular = u''.join(singular) if plural is not None: plural = u''.join(plural) return nodes.Trans(flineno, singular, plural, indicator, replacements or None) def parse_python(self, lineno, gen, template): """ Convert the passed generator into a flat string representing python sourcecode and return an ast node or raise a TemplateSyntaxError. """ tokens = [] for t_lineno, t_token, t_data in gen: if t_token == 'string': tokens.append('u' + t_data) else: tokens.append(t_data) source = '\xef\xbb\xbf' + (template % (u' '.join(tokens)). encode('utf-8')) try: ast = parse(source, 'exec') except SyntaxError, e: raise TemplateSyntaxError('invalid syntax', lineno + e.lineno, self.filename) assert len(ast.node.nodes) == 1, 'get %d nodes, 1 expected' %\ len(ast.node.nodes) result = ast.node.nodes[0] nodes.inc_lineno(lineno, result) return result def parse(self): """ Parse the template and return a Template node. Also unescape the names escaped by the lexer (unused python keywords) and set the filename attributes for all nodes in the tree. """ body = self.subparse(None) todo = [body] while todo: node = todo.pop() # all names excluding keywords have an trailing underline. # if we find a name without trailing underline that's a keyword # and this code raises an error. else strip the underline again if node.__class__ in (ast.AssName, ast.Name): if not node.name.endswith('_'): raise TemplateSyntaxError('illegal use of keyword %r ' 'as identifier.' % node.name, node.lineno, self.filename) node.name = node.name[:-1] elif node.__class__ is ast.Getattr: if not node.attrname.endswith('_'): raise TemplateSyntaxError('illegal use of keyword %r ' 'as attribute name.' % node.name, node.lineno, self.filename) node.attrname = node.attrname[:-1] node.filename = self.filename todo.extend(node.getChildNodes()) return nodes.Template(self.filename, body, self.extends) 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 tokenstream 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 tokenstream. """ def finish(): """Helper function to remove unused nodelists.""" if len(result) == 1: return result[0] return result lineno = self.tokenstream.last[0] result = nodes.NodeList(lineno) for lineno, token, data in self.tokenstream: # comments if token == 'comment_begin': self.tokenstream.drop_until(end_of_comment, True) # this token marks the begin or a variable section. # parse everything till the end of it. elif token == 'variable_begin': gen = self.tokenstream.fetch_until(end_of_variable, True) result.append(self.directives['print'](lineno, gen)) # this token marks the start of a block. like for variables # just parse everything until the end of the block elif token == 'block_begin': gen = self.tokenstream.fetch_until(end_of_block, True) try: lineno, token, data = gen.next() except StopIteration: raise TemplateSyntaxError('unexpected end of block', lineno, self.filename) # first token *must* be a name token if token != 'name': raise TemplateSyntaxError('unexpected %r token (%r)' % ( token, data), lineno, self.filename) # if a test function is passed to subparse we check if we # reached the end of such a requested block. if test is not None and test(lineno, token, data): if not drop_needle: self.tokenstream.push(lineno, token, data) return finish() # the first token tells us which directive we want to call. # if if doesn't match any existing directive it's like a # template syntax error. if data in self.directives: node = self.directives[data](lineno, gen) # directive or endtag found, give a proper error message elif data in self.directives or \ not data.endswith('_') and data.startswith('end'): raise TemplateSyntaxError('unexpected directive %r' % str(data), lineno, self.filename) # keyword or unknown name with trailing slash else: if data.endswith('_'): data = data[:-1] raise TemplateSyntaxError('unknown directive %r' % str(data), lineno, self.filename) # some tags like the extends tag do not output nodes. # so just skip that. if node is not None: result.append(node) # here the only token we should get is "data". all other # tokens just exist in block or variable sections. (if the # tokenizer is not brocken) elif token in 'data': result.append(nodes.Text(lineno, data)) # so this should be unreachable code else: raise AssertionError('unexpected token %r (%r)' % (token, data)) # still here and a test function is provided? raise and error if test is not None: raise TemplateSyntaxError('unexpected end of template', lineno, self.filename) return finish() def close_remaining_block(self): """ If we opened a block tag because one of our tags requires an end tag we can use this method to drop the rest of the block from the stream. If the next token isn't the block end we throw an error. """ lineno = self.tokenstream.last[0] try: lineno, token, data = self.tokenstream.next() except StopIteration: raise TemplateSyntaxError('missing closing tag', lineno, self.filename) if token != 'block_end': raise TemplateSyntaxError('expected close tag, found %r' % token, lineno, self.filename) PK5w6a;;jinja/plugin.py# -*- coding: utf-8 -*- """ jinja.plugin ~~~~~~~~~~~~ Support for the `GeneralTemplateInterface`__. __ http://trac.pocoo.org/wiki/GeneralTemplateInterface :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from jinja.environment import Environment from jinja.loaders import FunctionLoader, FileSystemLoader, PackageLoader from jinja.exceptions import TemplateNotFound 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. =================== ================================================= """ 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 PK5w6 Mfyyjinja/__init__.py# -*- coding: utf-8 -*- """ Jinja Sandboxed Template Engine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from jinja.environment import Environment from jinja.plugin import jinja_plugin_factory as template_plugin_factory from jinja.loaders import * from_string = Environment().from_string PKAw6|&&jinja/lexer.py# -*- coding: utf-8 -*- """ jinja.lexer ~~~~~~~~~~~ :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import re from jinja.datastructure import TokenStream from jinja.exceptions import TemplateSyntaxError try: set except NameError: from sets import Set as set # static regular expressions whitespace_re = re.compile(r'\s+(?m)') name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*') string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")(?ms)') number_re = re.compile(r'\d+(\.\d+)*') operator_re = re.compile('(%s)' % '|'.join([ isinstance(x, unicode) and str(x) or re.escape(x) for x in [ # math operators '+', '-', '*', '/', '%', # braces and parenthesis '[', ']', '(', ')', '{', '}', # attribute access and comparison / logical operators '.', ':', ',', '|', '==', '<', '>', '<=', '>=', '!=', '=', ur'or\b', ur'and\b', ur'not\b', ur'in\b', ur'is' ]])) # set of used keywords keywords = set(['and', 'block', 'cycle', 'elif', 'else', 'endblock', 'endfilter', 'endfor', 'endif', 'endmacro', 'endraw', 'endtrans', 'extends', 'filter', 'for', 'if', 'in', 'include', 'is', 'macro', 'not', 'or', 'pluralize', 'raw', 'recursive', 'set', 'trans']) class Failure(object): """ Class that raises a `TemplateSyntaxError` if called. Used by the `Lexer` to specify known errors. """ def __init__(self, message, cls=TemplateSyntaxError): self.message = message self.error_class = cls def __call__(self, lineno): raise self.error_class(self.message, lineno) class Lexer(object): """ Class that implements a lexer for a given environment. Automatically created by the environment class, usually you don't have to do that. """ def __init__(self, environment): # shortcuts c = lambda x: re.compile(x, re.M | re.S) e = re.escape # parsing rules for tags tag_rules = [ (whitespace_re, None, None), (number_re, 'number', None), (name_re, 'name', None), (operator_re, 'operator', None), (string_re, 'string', None) ] # assamble the root lexing rule. because "|" is ungreedy # we have to sort by length so that the lexer continues working # as expected when we have parsing rules like <% for block and # <%= for variables. (if someone wants asp like syntax) root_tag_rules = [ ('comment', environment.comment_start_string), ('block', environment.block_start_string), ('variable', environment.variable_start_string) ] root_tag_rules.sort(lambda a, b: cmp(len(b[1]), len(a[1]))) # global parsing rules self.rules = { 'root': [ (c('(%s\s*raw\s%s)(.*?)(%s\s*endraw\s*%s)' % ( (e(environment.block_start_string), e(environment.block_end_string)) * 2)), (None, 'data', None), None), (c('(.*?)(?:%s)' % '|'.join([ '(?P<%s_begin>%s)' % (n, e(r)) for n, r in root_tag_rules ])), ('data', '#bygroup'), '#bygroup'), (c('.+'), 'data', None) ], 'comment_begin': [ (c(r'(.*?)(%s)' % e(environment.comment_end_string)), ('comment', 'comment_end'), '#pop'), (c('(.)'), (Failure('Missing end of comment tag'),), None) ], 'block_begin': [ (c(e(environment.block_end_string) + (environment.trim_blocks and '\\n?' or '')), 'block_end', '#pop') ] + tag_rules, 'variable_begin': [ (c(e(environment.variable_end_string)), 'variable_end', '#pop') ] + tag_rules } def tokenize(self, source): """ Simple tokenize function that yields ``(position, type, contents)`` tuples. Wrap the generator returned by this function in a `TokenStream` to get real token instances and be able to push tokens back to the stream. That's for example done by the parser. Additionally non keywords are escaped. """ def filter(): for lineno, token, value in self.tokeniter(source): if token == 'name' and value not in keywords: value += '_' yield lineno, token, value return TokenStream(filter()) def tokeniter(self, source): """ This method tokenizes the text and returns the tokens in a generator. Use this method if you just want to tokenize a template. The output you get is not compatible with the input the jinja parser wants. The parser uses the `tokenize` function with returns a `TokenStream` with some escaped tokens. """ source = type(source)('\n').join(source.splitlines()) pos = 0 lineno = 1 stack = ['root'] statetokens = self.rules['root'] source_length = len(source) while True: # tokenizer loop for regex, tokens, new_state in statetokens: m = regex.match(source, pos) if m: # tuples support more options if isinstance(tokens, tuple): for idx, token in enumerate(tokens): # hidden group if token is None: g = m.group(idx) if g: lineno += g.count('\n') continue # failure group elif isinstance(token, Failure): raise token(m.start(idx + 1)) # bygroup is a bit more complex, in that case we # yield for the current token the first named # group that matched elif token == '#bygroup': for key, value in m.groupdict().iteritems(): if value is not None: yield lineno, key, value lineno += value.count('\n') break else: raise RuntimeError('%r wanted to resolve ' 'the token dynamically' ' but no group matched' % regex) # normal group else: data = m.group(idx + 1) if data: yield lineno, token, data lineno += data.count('\n') # strings as token just are yielded as it, but just # if the data is not empty else: data = m.group() if tokens is not None: if data: yield lineno, tokens, data lineno += data.count('\n') # fetch new position into new variable so that we can check # if there is a internal parsing error which would result # in an infinite loop pos2 = m.end() # handle state changes if new_state is not None: # remove the uppermost state if new_state == '#pop': stack.pop() # resolve the new state by group checking elif new_state == '#bygroup': for key, value in m.groupdict().iteritems(): if value is not None: stack.append(key) break else: raise RuntimeError('%r wanted to resolve the ' 'new state dynamically but' ' no group matched' % regex) # direct state name given else: stack.append(new_state) statetokens = self.rules[stack[-1]] # we are still at the same position and no stack change. # this means a loop without break condition, avoid that and # raise error elif pos2 == pos: raise RuntimeError('%r yielded empty string without ' 'stack change' % regex) # publish new function and start again pos = pos2 break # if loop terminated without break we havn't found a single match # either we are at the end of the file or we have a problem else: # end of text if pos >= source_length: return # something went wrong raise TemplateSyntaxError('unexpected char %r at %d' % (source[pos], pos), lineno) PK5w6g5--jinja/utils.py# -*- 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 compiler.ast import CallFunc, Name, Const from jinja.nodes import Trans from jinja.datastructure import Markup, Context try: from collections import deque except ImportError: deque = None #: number of maximal range items MAX_RANGE = 1000000 _debug_info_re = re.compile(r'^\s*\# DEBUG\(filename=(.*?), lineno=(.*?)\)$') _escape_pairs = { '&': '&', '<': '<', '>': '>', '"': '"' } _escape_res = ( re.compile('(&|<|>|")'), re.compile('(&|<|>)') ) _integer_re = re.compile('^(\d+)$') _word_split_re = re.compile(r'(\s+)') _punctuation_re = re.compile( '^(?P(?:%s)*)(?P.*?)(?P(?:%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._-]+$') def escape(x, attribute=False): """ Escape an object x. """ return Markup(_escape_res[not attribute].sub(lambda m: _escape_pairs[m.group()], x)) 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 = '%s' % (middle, nofollow_attr, trim_url(middle)) if middle.startswith('http://') or \ middle.startswith('https://'): middle = '%s' % (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 = '%s' % (middle, middle) if lead + middle + trail != word: words[i] = lead + middle + trail return u''.join(words) def debug_context(env, context): """ Use this function in templates to get a printed context. """ from pprint import pformat return pformat(context.to_dict()) debug_context.jinja_context_callable = True def safe_range(start, stop=None, step=None): """ "Safe" form of range that does not generate too large lists. """ # this also works with None since None is always smaller than # any other value. if start > MAX_RANGE: start = MAX_RANGE if stop > MAX_RANGE: stop = MAX_RANGE if step is None: step = 1 if stop is None: return range(0, start, step) return range(start, stop, step) # python2.4 and lower has a bug regarding joining of broken generators if sys.hexversion < (2, 5): def capture_generator(gen): """ Concatenate the generator output. """ return u''.join(tuple(gen)) # this should be faster and used in python2.5 and higher else: def capture_generator(gen): """ Concatenate the generator output """ return u''.join(gen) def buffereater(f): """ Used by the python translator to capture output of substreams. (macros, filter sections etc) """ def wrapped(*args, **kwargs): return capture_generator(f(*args, **kwargs)) return wrapped def fake_template_exception(exception, filename, lineno, context_or_env): """ 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 if isinstance(context_or_env, Context): env = context_or_env.environment namespace = context_or_env.to_dict() else: env = context_or_env namespace = {} offset = '\n' * (lineno - 1) code = compile(offset + 'raise __exception_to_raise__', filename, 'exec') globals = { '__name__': filename, '__file__': filename, '__loader__': TracebackLoader(env, filename), '__exception_to_raise__': exception } try: exec code in globals, namespace except: return sys.exc_info() def translate_exception(template, exc_type, exc_value, traceback, context): """ Translate an exception and return the new traceback. """ sourcelines = template.translated_source.splitlines() startpos = traceback.tb_lineno - 1 filename = None # looks like we loaded the template from string. we cannot # do anything here. if startpos > len(sourcelines): return traceback while startpos > 0: m = _debug_info_re.search(sourcelines[startpos]) if m is not None: filename, lineno = m.groups() if filename == 'None': filename = None if lineno != 'None': lineno = int(lineno) break startpos -= 1 # no traceback information found, reraise unchanged if not filename: return traceback return fake_template_exception(exc_value, filename, lineno, context)[2] def raise_syntax_error(exception, env): """ 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, exception.filename, exception.lineno, env) raise exc_info[0], exc_info[1], exc_info[2] def collect_translations(ast): """ Collect all translatable strings for the given ast. """ todo = [ast] result = [] while todo: node = todo.pop() if node.__class__ is Trans: result.append((node.lineno, node.singular, node.plural)) elif node.__class__ is CallFunc and \ node.node.__class__ is Name and \ node.node.name == '_': if len(node.args) in (1, 3): args = [] for arg in node.args: if not arg.__class__ is Const: break args.append(arg.value) else: if len(args) == 1: singular = args[0] plural = None else: singular, plural, _ = args result.append((node.lineno, singular, plural)) todo.extend(node.getChildNodes()) result.sort(lambda a, b: cmp(a[0], b[0])) return result class TracebackLoader(object): """ Fake importer that just returns the source of a template. """ def __init__(self, environment, filename): self.loader = environment.loader self.filename = filename def get_source(self, impname): return self.loader.get_source(self.filename) 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 = {} # use a deque here if possible if deque is not None: self._queue = deque() self._popleft = self._queue.popleft # python2.3, just use a list else: self._queue = [] pop = self._queue.pop self._popleft = lambda: pop(0) # alias all queue methods for faster lookup self._pop = self._queue.pop self._remove = self._queue.remove self._append = self._queue.append def copy(self): rv = CacheDict(self.capacity) rv._mapping.update(self._mapping) rv._queue = self._queue[:] return rv def get(self, key, default=None): if key in self: return self[key] return default def setdefault(self, key, default=None): if key in self: return self[key] self[key] = default return default def clear(self): self._mapping.clear() try: self._queue.clear() except AttributeError: del self._queue[:] def __contains__(self, key): return key in self._mapping def __len__(self): return len(self._mapping) def __repr__(self): return '<%s %r>' % ( self.__class__.__name__, self._mapping ) def __getitem__(self, key): rv = self._mapping[key] if self._queue[-1] != key: self._remove(key) self._append(key) return rv def __setitem__(self, key, value): 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): del self._mapping[key] self._remove(key) def __iter__(self): try: return reversed(self._queue) except NameError: return iter(self._queue[::-1]) def __reversed__(self): return iter(self._queue) __copy__ = copy PK5w6rjinja/defaults.py# -*- coding: utf-8 -*- """ jinja.defaults ~~~~~~~~~~~~~~ Jinja default filters and tags. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from jinja.filters import FILTERS as DEFAULT_FILTERS from jinja.tests import TESTS as DEFAULT_TESTS from jinja.utils import debug_context, safe_range DEFAULT_NAMESPACE = { 'range': safe_range, 'debug': debug_context } PK5w6QL L jinja/tests.py# -*- coding: utf-8 -*- """ jinja.tests ~~~~~~~~~~~ Jinja test functions. Used with the "is" operator. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import re from jinja.datastructure import Undefined number_re = re.compile(r'^-?\d+(\.\d+)?$') regex_type = type(number_re) def test_odd(): """ Return true if the variable is odd. """ return lambda e, c, v: v % 2 == 1 def test_even(): """ Return true of the variable is even. """ return lambda e, c, v: v % 2 == 0 def test_defined(): """ Return true if the variable is defined: .. sourcecode:: jinja {% if variable is defined %} value of variable: {{ variable }} {% else %} variable is not defined {% endif %} See also the ``default`` filter. """ return lambda e, c, v: v is not Undefined def test_lower(): """ Return true if the variable is lowercase. """ return lambda e, c, v: isinstance(v, basestring) and v.islower() def test_upper(): """ Return true if the variable is uppercase. """ return lambda e, c, v: isinstance(v, basestring) and v.isupper() def test_numeric(): """ Return true if the variable is numeric. """ return lambda e, c, v: isinstance(v, (int, long, float)) or ( isinstance(v, basestring) and number_re.match(v) is not None) def test_sequence(): """ Return true if the variable is a sequence. Sequences are variables that are iterable. """ def wrapped(environment, context, value): try: len(value) value.__getitem__ except: return False return True return wrapped def test_matching(regex): r""" Test if the variable matches the regular expression given. Note that you have to escape special chars using *two* backslashes, these are *not* raw strings. .. sourcecode:: jinja {% if var is matching('^\\d+$') %} var looks like a number {% else %} var doesn't really look like a number {% endif %} """ if isinstance(regex, unicode): regex = re.compile(regex, re.U) elif isinstance(regex, str): regex = re.compile(regex) elif type(regex) is not regex_type: regex = None def wrapped(environment, context, value): if regex is None: return False return regex.search(value) is not None return wrapped TESTS = { 'odd': test_odd, 'even': test_even, 'defined': test_defined, 'lower': test_lower, 'upper': test_upper, 'numeric': test_numeric, 'sequence': test_sequence, 'matching': test_matching } PK5w6K0/jinja/environment.py# -*- 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. """ import re from jinja.lexer import Lexer from jinja.parser import Parser from jinja.loaders import LoaderWrapper from jinja.datastructure import Undefined, Context, Markup, FakeTranslator from jinja.utils import escape, collect_translations from jinja.exceptions import FilterNotFound, TestNotFound, SecurityException from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE class Environment(object): """ The jinja environment. """ 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, template_charset='utf-8', charset='utf-8', namespace=None, loader=None, filters=None, tests=None, context_class=Context): # 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 self.filters = filters is None and DEFAULT_FILTERS.copy() or filters self.tests = tests is None and DEFAULT_TESTS.copy() or tests self.auto_escape = auto_escape self.context_class = context_class # global namespace self.globals = namespace is None and DEFAULT_NAMESPACE.copy() \ or namespace # 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, loader.__doc__) def parse(self, source, filename=None): """Function that creates a new parser and parses the source.""" parser = Parser(self, source, filename) return parser.parse() def from_string(self, source): """Load a template from a string.""" from jinja.parser import Parser from jinja.translators.python import PythonTranslator return PythonTranslator.process(self, Parser(self, source).parse()) def get_template(self, filename): """Load a template from a filename. Only works if a proper loader is set.""" return self._loader.load(filename) def to_unicode(self, value): """ Convert a value to unicode with the rules defined on the environment. """ if value in (None, Undefined): return u'' elif isinstance(value, unicode): return value else: 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! """ 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 apply_filters(self, value, context, filters): """ Apply a list of filters on the variable. """ for key in filters: if key in context.cache: func = context.cache[key] else: filtername, args = key if filtername not in self.filters: raise FilterNotFound(filtername) context.cache[key] = func = self.filters[filtername](*args) value = func(self, context, value) return value def perform_test(self, context, testname, args, value, invert): """ Perform a test on a variable. """ 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) rv = func(self, context, value) if invert: return not rv return bool(rv) def get_attribute(self, obj, attributes): """ Get some attributes from an object. """ node = obj for name in attributes: try: node = node[name] except (TypeError, KeyError, IndexError): if not hasattr(node, name): return Undefined r = getattr(obj, 'jinja_allowed_attributes', None) if r is not None and name not in r: raise SecurityException('unsafe attributed %r accessed' % name) node = getattr(node, name) return node def call_function(self, f, context, args, kwargs, dyn_args, dyn_kwargs): """ Function call helper. Called for all functions that are passed any arguments. """ if dyn_args is not None: args += tuple(dyn_args) elif dyn_kwargs is not None: kwargs.update(dyn_kwargs) if getattr(f, 'jinja_unsafe_call', False) or \ getattr(f, 'alters_data', False): raise SecurityException('unsafe function %r called' % f.__name__) 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. """ if getattr(f, 'jinja_unsafe_call', False) or \ getattr(f, 'alters_data', False): raise SecurityException('unsafe function %r called' % f.__name__) if getattr(f, 'jinja_context_callable', False): return f(self, context) return f() def finish_var(self, value): """ 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. """ if value is Undefined or value is None: return u'' elif isinstance(value, (int, float, Markup, bool)): return unicode(value) elif not isinstance(value, unicode): value = self.to_unicode(value) if self.auto_escape: return escape(value, True) return value PK݉w6H@ M Mjinja/datastructure.pyc; iFc@sdZyeWn ej odklZnXdklZlZdZdZ de fdYZ e Z de fdYZ d e fd YZd efd YZd e fdYZde fdYZde fdYZde fdYZdS(s 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. (sSet(sTemplateSyntaxErrorsTemplateRuntimeErrorcCst|_|SdS(s+ Mark a function context callable. N(sTruesfsjinja_context_callable(sf((s1build/bdist.linux-i686/egg/jinja/datastructure.pyscontextcallables cCst|_|SdS(s" Mark function as unsafe. N(sTruesfsjinja_unsafe_call(sf((s1build/bdist.linux-i686/egg/jinja/datastructure.pysunsafes s UndefinedTypecBstZdZfZdZdZZZZZ ZZ Z dZ dZ dZdZdZdZd Zd Zd Zd Zd ZdZRS(s( An object that does not exist. cCs8ytWntj onXtd|iidS(Nscannot create %r instances(s Undefineds NameErrors TypeErrorsselfs __class__s__name__(sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__init__+s cCs|S(N(sother(sselfsother((s1build/bdist.linux-i686/egg/jinja/datastructure.pys5scCs|SdS(N(sself(sselfsarg((s1build/bdist.linux-i686/egg/jinja/datastructure.pys __getitem__7sccstotVndS(N(sFalsesNone(sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__iter__:scCs|SdS(N(sself(sselfsarg((s1build/bdist.linux-i686/egg/jinja/datastructure.pys __getattr__>scCstSdS(N(sFalse(sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys __nonzero__AscCsdSdS(Ni((sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__len__DscCsdSdS(Ns((sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__str__GscCsdSdS(Nu((sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys __unicode__JscCsdSdS(Ni((sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__int__MscCsdSdS(Nf0.0((sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys __float__PscCstSdS(N(sFalse(sselfsother((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__eq__SscCstSdS(N(sTrue(sselfsother((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__ne__VscOs|SdS(N(sself(sselfsargsskwargs((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__call__Ys(s__name__s __module__s__doc__s __slots__s__init__s__sub__s__mul__s__div__s__rsub__s__rmul__s__radd__s__add__s __getitem__s__iter__s __getattr__s __nonzero__s__len__s__str__s __unicode__s__int__s __float__s__eq__s__ne__s__call__(((s1build/bdist.linux-i686/egg/jinja/datastructure.pys UndefinedType%s  %           sFakeTranslatorcBs tZdZdZdZRS(s" Default null translator. cCs|SdS(N(ss(sselfss((s1build/bdist.linux-i686/egg/jinja/datastructure.pysgettextescCs|djo|Sn|SdS(Ni(snsssp(sselfssspsn((s1build/bdist.linux-i686/egg/jinja/datastructure.pysngettexths (s__name__s __module__s__doc__sgettextsngettext(((s1build/bdist.linux-i686/egg/jinja/datastructure.pysFakeTranslator`s  sDeferredcBs tZdZdZdZRS(sy Object marking an deferred value. Deferred objects are objects that are called first access in the context. cCs ||_dS(N(sfactorysself(sselfsfactory((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__init__tscCs|i|i||SdS(N(sselfsfactoryscontexts environmentsname(sselfscontextsname((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__call__ws(s__name__s __module__s__doc__s__init__s__call__(((s1build/bdist.linux-i686/egg/jinja/datastructure.pysDeferredns  sMarkupcBstZdZdZRS(s Mark a string as safe for XML. If the environment uses the auto_escape option values marked as `Markup` aren't escaped. cCsdti|SdS(Ns Markup(%s)(sunicodes__repr__sself(sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__repr__s(s__name__s __module__s__doc__s__repr__(((s1build/bdist.linux-i686/egg/jinja/datastructure.pysMarkup{s sContextcBsbtZdZdZdZedZdZdZdZ dZ dZ d Z RS( s Dict like object. cOsO||_|it||hg|_|i\|_|_|_ h|_ dS(N( s _environment_sselfs environmentsglobalssdictsargsskwargss_stacksinitialscurrentscache(sselfs _environment_sargsskwargs((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__init__s cCs'|ii}|id|_|SdS(s0Pop the last layer from the stack and return it.iN(sselfs_stackspopsrvscurrent(sselfsrv((s1build/bdist.linux-i686/egg/jinja/datastructure.pyspopscCs5|ph}|ii||id|_|SdS(sAPush a new dict or empty layer to the stack and return that layeriN(sdatasselfs_stacksappendscurrent(sselfsdata((s1build/bdist.linux-i686/egg/jinja/datastructure.pyspushs  cCsdh}xS|idD]D}x;|iD]-\}}|idoq'n||||||}||i jo||i |scCs|iddS(Nisindex(sss_stack(ss((s1build/bdist.linux-i686/egg/jinja/datastructure.pysscCs|idddS(Nisindexi(sss_stack(ss((s1build/bdist.linux-i686/egg/jinja/datastructure.pysscCs"|idd|idddS(Nislengthsindexi(sss_stack(ss((s1build/bdist.linux-i686/egg/jinja/datastructure.pysscCs|idd|iddS(Nislengthsindex(sss_stack(ss((s1build/bdist.linux-i686/egg/jinja/datastructure.pysscCs|iddS(Nislength(sss_stack(ss((s1build/bdist.linux-i686/egg/jinja/datastructure.pysscCs|iddddjS(Nisindexii(sss_stack(ss((s1build/bdist.linux-i686/egg/jinja/datastructure.pysscCs|iddddjS(Nisindexii(sss_stack(ss((s1build/bdist.linux-i686/egg/jinja/datastructure.pysscCs|idddjS(Nisindexi(sss_stack(ss((s1build/bdist.linux-i686/egg/jinja/datastructure.pysscCs$|idd|idddjS(Nisindexslengthi(sss_stack(ss((s1build/bdist.linux-i686/egg/jinja/datastructure.pyssccs@|id}x,t|dD]\}}||d<|VqWdS(Nisseqsindex(sselfs_stacksss enumeratesidxsitem(sselfsitemsssidx((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__iter__s    cCs|iddSdS(Nislength(sselfs_stack(sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__len__scCs1|itj o|i|SntddS(NsVIn order to make loops callable you have to define them with the "recursive" modifier.(sselfs loop_functionsNonesseqsTemplateRuntimeError(sselfsseq((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__call__ scCsD|io2d|i|i|itj odpdfSndSdS(Nss recursivess(sselfs_stacksindexslengths loop_functionsNone(sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__repr__s 2(s__name__s __module__s__doc__sjinja_allowed_attributess__init__spushspopspropertysiteratedsindex0sindexs revindex0srevindexslengthsevensoddsfirstslasts__iter__s__len__s__call__s__repr__(((s1build/bdist.linux-i686/egg/jinja/datastructure.pys LoopContexts& $      s CycleContextcBs,tZdZedZdZdZRS(s( Helper class used for cycling. cCsNd|_|tj o(||_t||_|i|_n |i|_dS(Ni( sselfslinenosseqsNoneslenslengths cycle_staticscycles cycle_dynamic(sselfsseq((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__init__!s    cCs)|id|i|_|i|iSdS(s#Helper function for static cycling.iN(sselfslinenoslengthsseq(sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys cycle_static+scCs)|idt||_||iSdS(s$Helper function for dynamic cycling.iN(sselfslinenoslensseq(sselfsseq((s1build/bdist.linux-i686/egg/jinja/datastructure.pys cycle_dynamic0s(s__name__s __module__s__doc__sNones__init__s cycle_statics cycle_dynamic(((s1build/bdist.linux-i686/egg/jinja/datastructure.pys CycleContexts  s TokenStreamcBsttZdZdZdZdZeddeiZdZdZ e dZ e d Z d Z RS( ss A token stream works like a normal generator just that it supports pushing tokens back to the stream. cCs(||_g|_dddf|_dS(Nisinitials(s generatorsselfs _generators_pushedslast(sselfs generator((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__init__<s  cCs|SdS(N(sself(sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys__iter__AscCsK|iotSny|i|iWntj o tSnXtSdS(s%Are we at the end of the tokenstream?N(sselfs_pushedsTruespushsnexts StopIterationsFalse(sself((s1build/bdist.linux-i686/egg/jinja/datastructure.pys __nonzero__Ds  cCs |i S(N(sxs __nonzero__(sx((s1build/bdist.linux-i686/egg/jinja/datastructure.pysNssdoccCs=|io|ii}n|ii}||_|SdS(s&Return the next token from the stream.N(sselfs_pushedspopsrvs _generatorsnextslast(sselfsrv((s1build/bdist.linux-i686/egg/jinja/datastructure.pysnextPs   cCs!|i}|i||SdS(s Pop and push a token, return it.N(sselfsnextstokenspush(sselfstoken((s1build/bdist.linux-i686/egg/jinja/datastructure.pyslookYs  ccswyQxJtoB|i}||o!| o|i|ndSq|VqWWntj otdnXdS(s&Fetch tokens until a function matches.Nsend of stream reached( sTruesselfsnextstokenstests drop_needlespushs StopIterationsTemplateSyntaxError(sselfstests drop_needlestoken((s1build/bdist.linux-i686/egg/jinja/datastructure.pys fetch_until_s   cCs!x|i||D]}qWdS(sBFetch tokens until a function matches and drop all tokens.N(sselfs fetch_untilstests drop_needlestoken(sselfstests drop_needlestoken((s1build/bdist.linux-i686/egg/jinja/datastructure.pys drop_untilmscCs|ii|||fdS(s)Push an yielded token back to the stream.N(sselfs_pushedsappendslinenostokensdata(sselfslinenostokensdata((s1build/bdist.linux-i686/egg/jinja/datastructure.pyspushss(s__name__s __module__s__doc__s__init__s__iter__s __nonzero__spropertyseossnextslooksFalses fetch_untils drop_untilspush(((s1build/bdist.linux-i686/egg/jinja/datastructure.pys TokenStream6s       N(s__doc__ssets NameErrorssetssSetsjinja.exceptionssTemplateSyntaxErrorsTemplateRuntimeErrorscontextcallablesunsafesobjects UndefinedTypes UndefinedsFakeTranslatorsDeferredsunicodesMarkupsContexts LoopContexts CycleContexts TokenStream(sContextssets UndefinedsTemplateSyntaxErrors UndefinedTypesMarkupsunsafesDeferredsFakeTranslatorsTemplateRuntimeErrorscontextcallables TokenStreams LoopContexts CycleContext((s1build/bdist.linux-i686/egg/jinja/datastructure.pys? s   8   LKPK݉w6gdgdjinja/filters.pyc; iFc@sdZdklZdklZlZdklZlZdk l Z dk l Z y e ZWnej odZnXdZedZeeZd ZeeZd ZeeZed ZeeZd ZeeZd ZeeZeddZdedZddZdZdZddZeeZdZdZ dZ!dZ"dZ#dZ$dZ%eedZ&ee&Z&ded Z'ee'Z'd!ed"d#Z(ee(Z(d$ed%Z)ee)Z)d&Z*ee*Z*d'Z+ee+Z+d(Z,ee,Z,d)Z-ee-Z-d*d+Z.d,d-Z/d.Z0d/Z1d0Z2ee2Z2d1ed2Z3hd3e<d4e<d5e<d6e<d7e<d8e<d9e<d:e<d;e<d<e<d=e<d>e<d?e<d@e<d9e<d8e<dAe<dBe <dCe!<dDe"<dEe#<dFe$<dGe%<dHe'<dIe(<dJe)<dKe*<dLe+<dMe,<dNe-<dOe.<dPe/<dQe0<dRe&<dSe1<dTe3<dUe2 Goodbye World {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} -> d'oh, d'oh, aaargh s8the replace filter requires string replacement argumentss=the count parameter of the replace filter requires an integerN( s isinstancesolds basestringsnewsFilterArgumentExceptionscountsintslongsNonesssreplace(sssoldsnewscount((s+build/bdist.linux-i686/egg/jinja/filters.pys do_replace5s" cCs|iSdS(s' Convert a value to uppercase. N(sssupper(ss((s+build/bdist.linux-i686/egg/jinja/filters.pysdo_upperSscCs|iSdS(s' Convert a value to lowercase. N(ssslower(ss((s+build/bdist.linux-i686/egg/jinja/filters.pysdo_lower[scCst||SdS(sB XML escape ``&``, ``<``, and ``>`` in a string of data. If the optional parameter is `true` this filter will also convert ``"`` to ``"``. This filter is just used if the environment was configured with disabled `auto_escape`. This method will have no effect it the value is already escaped. N(sescapesss attribute(sss attribute((s+build/bdist.linux-i686/egg/jinja/filters.pys do_escapecscCs|iSdS(s^ Capitalize a value. The first character will be uppercase, all others lowercase. N(sss capitalize(ss((s+build/bdist.linux-i686/egg/jinja/filters.pys do_capitalizepscCs|iSdS(s Return a titlecased version of the value. I.e. words will start with uppercase letters, all remaining characters are lowercase. N(ssstitle(ss((s+build/bdist.linux-i686/egg/jinja/filters.pysdo_titleysskeycs]|djo dn$|djo dn tddd}|SdS( s  Sort a dict and yield (key, value) pairs. Because python dicts are unsorted you may want to use this function to order them by either key or value: .. sourcecode:: jinja {% for item in mydict|dictsort %} sort the dict by key, case insensitive {% for item in mydict|dicsort(true) %} sort the dict by key, case sensitive {% for item in mydict|dictsort(false, 'value') %} sort the dict by key, case insensitive, sorted normally and ordered by value. skeyisvalueis,You can only sort by either "key" or "value"csCt|to+|i|} o|i}q;n|SdS(N(s isinstancesvalues basestringsenvs to_unicodescase_sensitiveslower(svaluesenv(scase_sensitive(s+build/bdist.linux-i686/egg/jinja/filters.pys sort_funcs cs-|i}|id|SdS(Ncs't||S(N(scmps sort_funcsaspossenvsb(sasb(s sort_funcspossenv(s+build/bdist.linux-i686/egg/jinja/filters.pyss(svaluesitemsssort(senvscontextsvaluesitems(s sort_funcspos(senvs+build/bdist.linux-i686/egg/jinja/filters.pyswrappeds N(sbyspossFilterArgumentErrors sort_funcswrapped(scase_sensitivesbys sort_funcsposswrapped((scase_sensitives sort_funcsposs+build/bdist.linux-i686/egg/jinja/filters.pys do_dictsorts      ucsd}|SdS(s If the value is undefined it will return the passed default value, otherwise the value of the variable: .. sourcecode:: jinja {{ my_variable|default('my_variable is not defined') }} This will output the value of ``my_variable`` if the variable was defined, otherwise ``'my_variable is not defined'``. If you want to use default with variables that evaluate to false you have to set the second parameter to `true`: .. sourcecode:: jinja {{ ''|default('the string was empty', true) }} cs2o| p|ttfjoSn|SdS(N(sbooleansvalues UndefinedsNones default_value(senvscontextsvalue(s default_valuesboolean(s+build/bdist.linux-i686/egg/jinja/filters.pyswrappeds"N(swrapped(s default_valuesbooleanswrapped((s default_valuesbooleans+build/bdist.linux-i686/egg/jinja/filters.pys do_defaultscsd}|SdS(sO Return a string which is the concatenation of the strings in the sequence. The separator between elements is an empty string per default, you can define ith with the optional parameter: .. sourcecode:: jinja {{ [1, 2, 3]|join('|') }} -> 1|2|3 {{ [1, 2, 3]|join }} -> 123 csD|iigi}|D]}||i|q~SdS(N(senvs to_unicodesdsjoinsappends_[1]svaluesx(senvscontextsvalues_[1]sx(sd(s+build/bdist.linux-i686/egg/jinja/filters.pyswrappedsN(swrapped(sdswrapped((sds+build/bdist.linux-i686/egg/jinja/filters.pysdo_joins  cCsd}|SdS(s Return the length of the value. In case if getting an integer or float it will convert it into a string an return the length of the new string. If the object has no length it will of corse return 0. cCs\y>t|tttfjott|Snt|SWntj o dSnXdS(Ni(stypesvaluesintsfloatslongslensstrs TypeError(senvscontextsvalue((s+build/bdist.linux-i686/egg/jinja/filters.pyswrappeds N(swrapped(swrapped((s+build/bdist.linux-i686/egg/jinja/filters.pysdo_counts cCsd}|SdS(s Return a reversed list of the sequence filtered. You can use this for example for reverse iteration: .. sourcecode:: jinja {% for item in seq|reverse %} {{ item|e }} {% endfor %} cCs=y|dddSWn!t|}|i|SnXdS(Ni(svalueslistslsreverse(senvscontextsvaluesl((s+build/bdist.linux-i686/egg/jinja/filters.pyswrappeds   N(swrapped(swrapped((s+build/bdist.linux-i686/egg/jinja/filters.pys do_reverses  iPcCs|i|SdS(s8 Centers the value in a field of a given width. N(svaluescenterswidth(svalueswidth((s+build/bdist.linux-i686/egg/jinja/filters.pys do_centerscCsd}|SdS(s. Return the frist item of a sequence. cCs2yt|iSWntj o tSnXdS(N(sitersseqsnexts StopIterations Undefined(senvscontextsseq((s+build/bdist.linux-i686/egg/jinja/filters.pyswrappedsN(swrapped(swrapped((s+build/bdist.linux-i686/egg/jinja/filters.pysdo_firsts cCsd}|SdS(s- Return the last item of a sequence. cCs>ytt|iSWnttfj o tSnXdS(N(siters _reversedsseqsnexts TypeErrors StopIterations Undefined(senvscontextsseq((s+build/bdist.linux-i686/egg/jinja/filters.pyswrappedsN(swrapped(swrapped((s+build/bdist.linux-i686/egg/jinja/filters.pysdo_lasts cCsd}|SdS(s1 Return a random item from the sequence. cCs yt|SWn tSnXdS(N(schoicesseqs Undefined(senvscontextsseq((s+build/bdist.linux-i686/egg/jinja/filters.pyswrappedsN(swrapped(swrapped((s+build/bdist.linux-i686/egg/jinja/filters.pys do_randoms cCsd}|SdS(s urlencode a string or directory. .. sourcecode:: jinja {{ {'foo': 'bar', 'blub': 'blah'}|urlencode }} -> foo=bar&blub=blah {{ 'Hello World' }} -> Hello%20World cCstt|toMh}x6|iD](\}}|i|||i| "Hello World" NcCs ti|S(N(s simplejsonsdumpssv(sescsv((s+build/bdist.linux-i686/egg/jinja/filters.pysLs(s simplejsons NameError(((s+build/bdist.linux-i686/egg/jinja/filters.pys do_jsonencode>scCsd}|SdS(sf Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102 bytes, etc). cCsyt|}Wntj o d}nX|djo&d||djodpdfSnK|ddjod|dSn*|dddjod|ddSnd |dddSdS( Niis %d Byte%sissss%.1f KBs%.1f MBs%.1f GB(sfloatsvaluesbytess TypeError(senvscontextsvaluesbytes((s+build/bdist.linux-i686/egg/jinja/filters.pyswrappedTs  &N(swrapped(swrapped((s+build/bdist.linux-i686/egg/jinja/filters.pysdo_filesizeformatOs cCsd}|SdS(s8 Pretty print a variable. Useful for debugging. cCsdkl}||SdS(N(spformat(spprintspformatsvalue(senvscontextsvaluespformat((s+build/bdist.linux-i686/egg/jinja/filters.pyswrappedis N(swrapped(swrapped((s+build/bdist.linux-i686/egg/jinja/filters.pys do_pprintes cCst|||SdS(si Converts URLs in plain text into clickable links. If you pass the filter an additional integer it will shorten the urls to that number. Also a third argument exists that makes the urls "nofollow": .. sourcecode:: jinja {{ mytext|urlize(40, True) }} links are shortened to 40 chars and defined with rel="nofollow" N(surlizesvaluestrim_url_limitsnofollow(svaluestrim_url_limitsnofollow((s+build/bdist.linux-i686/egg/jinja/filters.pys do_urlizeos icCsed|}|o<digi}|iD]}|||q+~Sn|i dd|SdS(s {{ s|indent[ width[ indentfirst[ usetab]]] }} Return a copy of the passed string, each line indented by 4 spaces. The first line is not indented. If you want to change the number of spaces or indent the first line too you can pass additional parameters to the filter: .. sourcecode:: jinja {{ mytext|indent(2, True) }} indent by two spaces and indent the first line too. s u s N( swidths indentions indentfirstsjoinsappends_[1]sss splitlinesslinesreplace(ssswidths indentfirsts indentions_[1]sline((s+build/bdist.linux-i686/egg/jinja/filters.pys do_indents  <is...cCst||jo|Sn|o|| |Sn|id}g}d}xA|D]9}|t|d7}||joPn|i |qTW|i |di |SdS(s Return a truncated copy of the string. The length is specified with the first parameter which defaults to ``255``. If the second parameter is ``true`` the filter will cut the text at length. Otherwise it will try to save the last word. If the text was in fact truncated it will append an ellipsis sign (``"..."``). If you want a different ellipsis sign than ``"..."`` you can specify it using the third parameter. .. sourcecode jinja:: {{ mytext|truncate(300, false, '»') }} truncate mytext to 300 chars, don't split up words, use a right pointing double arrow as ellipsis sign. s iiu N( slenssslengths killwordssendssplitswordssresultsmswordsappendsjoin(ssslengths killwordssendsmsresultswordssword((s+build/bdist.linux-i686/egg/jinja/filters.pys do_truncates   iOcCst||jo|Sn|oOdigi}tdt||D]}|||||!qH~Snt |d|i dSdS(s$ Return a copy of the string passed to the filter wrapped after ``79`` characters. You can override this default using the first parameter. If you set the second parameter to `true` Jinja will also split words apart (usually a bad idea because it makes reading hard). u icCsLd|dt||iddt|iddd|j|fS(Nu%s%s%su s ii(slineslensrfindswordssplitspos(slineswordspos((s+build/bdist.linux-i686/egg/jinja/filters.pysss N( slensssposshardsjoinsappends_[1]sxrangesidxsreducessplit(sssposshardsidxs_[1]((s+build/bdist.linux-i686/egg/jinja/filters.pys do_wordwrapsO cCs@tgi}|iD]}|o||qq~SdS(s) Count the words in that string. N(slensappends_[1]ssssplitsx(sss_[1]sx((s+build/bdist.linux-i686/egg/jinja/filters.pys do_wordcountscCsdkl}||SdS(s Prase the string using textile. requires the `PyTextile`_ library. .. _PyTextile: http://dealmeida.net/projects/textile/ (stextileN(stextiless(ssstextile((s+build/bdist.linux-i686/egg/jinja/filters.pys do_textiles cCsdkl}||SdS(s Parse the string using markdown. requires the `Python-markdown`_ library. .. _Python-markdown: http://www.freewisdom.org/projects/python-markdown/ (smarkdownN(smarkdownss(sssmarkdown((s+build/bdist.linux-i686/egg/jinja/filters.pys do_markdowns cCs@y.dkl}|d|dd}|dSWn |SnXdS(s Parse the string using the reStructuredText parser from the docutils package. requires `docutils`_. .. _docutils: from http://docutils.sourceforge.net/ (s publish_partsssources writer_names html4css1sfragmentN(s docutils.cores publish_partssssparts(ssspartss publish_parts((s+build/bdist.linux-i686/egg/jinja/filters.pysdo_rsts  icsd}|SdS(s Convert the value into an integer. If the conversion doesn't work it will return ``0``. You can override this default using the first parameter. csbyt|SWnMttfj o;ytt|SWq^ttfj o Sq^XnXdS(N(sintsvalues TypeErrors ValueErrorsfloatsdefault(senvscontextsvalue(sdefault(s+build/bdist.linux-i686/egg/jinja/filters.pyswrappedsN(swrapped(sdefaultswrapped((sdefaults+build/bdist.linux-i686/egg/jinja/filters.pysdo_ints f0.0csd}|SdS(s Convert the value into a floating point number. If the conversion doesn't work it will return ``0.0``. You can override this default using the first parameter. cs2yt|SWnttfj o SnXdS(N(sfloatsvalues TypeErrors ValueErrorsdefault(senvscontextsvalue(sdefault(s+build/bdist.linux-i686/egg/jinja/filters.pyswrappedsN(swrapped(sdefaultswrapped((sdefaults+build/bdist.linux-i686/egg/jinja/filters.pysdo_floats cCs dSdS(s+ Convert the value into an string. cCs |i|S(N(ses to_unicodesv(sescsv((s+build/bdist.linux-i686/egg/jinja/filters.pys"sN((((s+build/bdist.linux-i686/egg/jinja/filters.pys do_stringscsd}|SdS(s Apply python string formatting on an object: .. sourcecode:: jinja {{ "%s - %s"|format("Hello?", "Foo!") }} -> Hello? - Foo! Note that you cannot use the mapping syntax (``%(name)s``) like in python. cs|i|SdS(N(senvs to_unicodesvaluesargs(senvscontextsvalue(sargs(s+build/bdist.linux-i686/egg/jinja/filters.pyswrapped1sN(swrapped(sargsswrapped((sargss+build/bdist.linux-i686/egg/jinja/filters.pys do_format%s  cCs|iSdS(s0 Strip leading and trailing whitespace. N(svaluesstrip(svalue((s+build/bdist.linux-i686/egg/jinja/filters.pysdo_trim6sscapturedcs8tt otdnd}|SdS(s8 Store the value in a variable called ``captured`` or a variable with the name provided. Useful for filter blocks: .. sourcecode:: jinja {% filter capture('foo') %} ... {% endfilter %} {{ foo }} This will output "..." two times. One time from the filter block and one time from the variable. If you don't want the filter to output something you can use it in `clean` mode: .. sourcecode:: jinja {% filter capture('foo', True) %} ... {% endfilter %} {{ foo }} s#You can only capture into variablescs!||<otSn|SdS(N(svaluescontextsnamescleans Undefined(senvscontextsvalue(scleansname(s+build/bdist.linux-i686/egg/jinja/filters.pyswrappedWs N(s isinstancesnamesunicodesFilterArgumentErrorswrapped(snamescleanswrapped((snamescleans+build/bdist.linux-i686/egg/jinja/filters.pys do_capture>s sreplacesupperslowersescapeses capitalizestitlesdefaultsjoinscountsdictsortslengthsreversescentersfirstslastsrandoms urlencodes jsonencodesfilesizeformatspprintsindentstruncateswordwraps wordcountstextilesmarkdownsrstsintsfloatsstringsurlizesformatscapturestrimN(5s__doc__srandomschoicesurllibs urlencodesquotes jinja.utilssescapesurlizesjinja.datastructures Undefinedsjinja.exceptionssFilterArgumentErrorsreverseds _reverseds NameErrors stringfiltersNones do_replacesdo_uppersdo_lowersFalses do_escapes do_capitalizesdo_titles do_dictsorts do_defaultsdo_joinsdo_counts do_reverses do_centersdo_firstsdo_lasts do_randoms do_urlencodes do_jsonencodesdo_filesizeformats do_pprints do_urlizes do_indents do_truncates do_wordwraps do_wordcounts do_textiles do_markdownsdo_rstsdo_intsdo_floats do_strings do_formatsdo_trims do_capturesFILTERS(+s urlencodes do_pprints do_wordcounts do_jsonencodes do_markdownsdo_titlesescapes do_textiles do_escapes do_urlencodesdo_uppersurlizes do_randomsdo_countsFilterArgumentErrors do_centersFILTERSs do_capitalizes do_defaults do_replaces do_strings Undefinedsdo_trims do_urlizesquotes do_reverseschoices stringfiltersdo_rsts _reverseds do_indents do_formatsdo_lastsdo_firsts do_captures do_truncatesdo_joins do_wordwrapsdo_floats do_dictsortsdo_intsdo_lowersdo_filesizeformat((s+build/bdist.linux-i686/egg/jinja/filters.pys? sx                (                         !PK݉w6KFwAwAjinja/nodes.pyc; iFc@sydZdklZdZdZdeifdYZdefdYZdeefd YZd efd YZ d efd YZ defdYZ defdYZ defdYZ defdYZdefdYZdefdYZdefdYZdefdYZdefdYZd efd!YZd"S(#s jinja.nodes ~~~~~~~~~~~ Additional nodes for Jinja. Look like nodes from the ast. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. (sastcCse|g}xU|oM|i}|io|i|d7_n ||_|i|iq WdS(sE Increment the linenumbers of all nodes in tree with offset. iN(streestodospopsnodeslinenosoffsetsextends getChildNodes(soffsetstreesnodestodo((s)build/bdist.linux-i686/egg/jinja/nodes.pys inc_linenos    ccsS|g}xC|o;|i}|i|jo|Vn|i|iq WdS(s2 Get all nodes from nodetype in the tree. N(streestodospopsnodes __class__snodetypesextends getChildNodes(snodetypestreesnodestodo((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_nodess  sNodecBs)tZdZdZdZdZRS(s Jinja node. cCsgSdS(N((sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_items-scCs|iSdS(N(sselfs get_items(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys getChildren0scCsFgi}|iD]'}t|tio||qq~SdS(N(sappends_[1]sselfs get_itemssxs isinstancesastsNode(sselfs_[1]sx((s)build/bdist.linux-i686/egg/jinja/nodes.pys getChildNodes3s(s__name__s __module__s__doc__s get_itemss getChildrens getChildNodes(((s)build/bdist.linux-i686/egg/jinja/nodes.pysNode(s   sTextcBs)tZdZdZdZdZRS(s+ Node that represents normal text. cCs||_||_dS(N(slinenosselfstext(sselfslinenostext((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__<s cCs|igSdS(N(sselfstext(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_items@scCsd|ifSdS(NsText(%r)(sselfstext(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__Cs(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysText7s   sNodeListcBs0tZdZedZdZZdZRS(s1 A node that stores multiple childnodes. cCs$||_ti||pfdS(N(slinenosselfslists__init__sdata(sselfslinenosdata((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__Ls cCst||iS(N(slistsss get_items(ss((s)build/bdist.linux-i686/egg/jinja/nodes.pysPscCsdti|SdS(Ns NodeList(%s)(slists__repr__sself(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__Rs(s__name__s __module__s__doc__sNones__init__s getChildrens getChildNodess__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysNodeListGs   sTemplatecBs)tZdZdZdZdZRS(s* Node that represents a template. cCsF|itj o |f}nti|d|||_||_dS(Ni(sbodys __class__sNodeLists__init__sselfsextendssfilename(sselfsfilenamesbodysextends((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__[s   cCs%|itj o |igpgSdS(N(sselfsextendssNone(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemsbscCs$d|i|iti|fSdS(NsTemplate(%r, %r, %r)(sselfsfilenamesextendssNodeLists__repr__(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__es(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysTemplateVs   sForLoopcBs)tZdZdZdZdZRS(s+ A node that represents a for loop cCs:||_||_||_||_||_||_dS(N(slinenosselfsitemsseqsbodyselse_s recursive(sselfslinenositemsseqsbodyselse_s recursive((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__rs      cCs&|i|i|i|i|igSdS(N(sselfsitemsseqsbodyselse_s recursive(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemszscCs*d|i|i|i|i|ifSdS(NsForLoop(%r, %r, %r, %r, %r)(sselfsitemsseqsbodyselse_s recursive(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__}s(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysForLoopms   s IfConditioncBs)tZdZdZdZdZRS(s1 A node that represents an if condition. cCs||_||_||_dS(N(slinenosselfstestsselse_(sselfslinenostestsselse_((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__s  cCs?g}x|iD]}|i|qW|i|i|SdS(N(sresultsselfstestsstestsextendsappendselse_(sselfstestsresult((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemss  cCsd|i|ifSdS(NsIfCondition(%r, %r)(sselfstestsselse_(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__s(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pys IfConditions   sCyclecBs)tZdZdZdZdZRS(s5 A node that represents the cycle statement. cCs||_||_dS(N(slinenosselfsseq(sselfslinenosseq((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__s cCs|igSdS(N(sselfsseq(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemsscCsd|ifSdS(Ns Cycle(%r)(sselfsseq(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__s(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysCycles   sPrintcBs)tZdZdZdZdZRS(s? A node that represents variable tags and print calls. cCs||_||_dS(N(slinenosselfsvariable(sselfslinenosvariable((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__s cCs|igSdS(N(sselfsvariable(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemsscCsd|ifSdS(Ns Print(%r)(sselfsvariable(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__s(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysPrints   sMacrocBs)tZdZdZdZdZRS(s) A node that represents a macro. cCs(||_||_||_||_dS(N(slinenosselfsnames argumentssbody(sselfslinenosnames argumentssbody((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__s   cCsS|ig}|io%x"|iD]}|i|q Wn|i|i|SdS(N(sselfsnamesresults argumentssitemsextendsappendsbody(sselfsitemsresult((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemss   cCsd|i|i|ifSdS(NsMacro(%r, %r, %r)(sselfsnames argumentssbody(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__s(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysMacros   sSetcBs)tZdZdZdZdZRS(s' Allow defining own variables. cCs||_||_||_dS(N(slinenosselfsnamesexpr(sselfslinenosnamesexpr((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__s  cCs|i|igSdS(N(sselfsnamesexpr(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemsscCsd|i|ifSdS(Ns Set(%r, %r)(sselfsnamesexpr(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__s(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysSets   sFiltercBs)tZdZdZdZdZRS(s# Node for filter sections. cCs||_||_||_dS(N(slinenosselfsbodysfilters(sselfslinenosbodysfilters((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__s  cCs|igt|iSdS(N(sselfsbodyslistsfilters(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemsscCsd|i|ifSdS(NsFilter(%r, %r)(sselfsbodysfilters(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__s(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysFilters   sBlockcBs2tZdZdZdZdZdZRS(s) A node that represents a block. cCs||_||_||_dS(N(slinenosselfsnamesbody(sselfslinenosnamesbody((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__s  cCs.|itjpt|ii|idS(sO Replace the current data with the data of another block node. N(snodes __class__sBlocksAssertionErrorsselfs__dict__supdate(sselfsnode((s)build/bdist.linux-i686/egg/jinja/nodes.pysreplace scCs|i|igSdS(N(sselfsnamesbody(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemsscCsd|i|ifSdS(Ns Block(%r, %r)(sselfsnamesbody(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__s(s__name__s __module__s__doc__s__init__sreplaces get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysBlocks    sExtendscBs)tZdZdZdZdZRS(s1 A node that represents the extends tag. cCs||_||_dS(N(slinenosselfstemplate(sselfslinenostemplate((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__"s cCs|igSdS(N(sselfstemplate(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_items&scCsd|iSdS(Ns Extends(%r)(sselfstemplate(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__)s(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysExtendss   sIncludecBs)tZdZdZdZdZRS(s1 A node that represents the include tag. cCs||_||_dS(N(slinenosselfstemplate(sselfslinenostemplate((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__2s cCs|igSdS(N(sselfstemplate(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_items6scCsd|iSdS(Ns Include(%r)(sselfstemplate(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__9s(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysInclude-s   sTranscBs)tZdZdZdZdZRS(s+ A node for translatable sections. cCs1||_||_||_||_||_dS(N(slinenosselfssingularsplurals indicators replacements(sselfslinenossingularsplurals indicators replacements((s)build/bdist.linux-i686/egg/jinja/nodes.pys__init__Bs     cCsZ|i|i|ig}|io0|i|ii|i|iin|SdS(N( sselfssingularsplurals indicatorsrvs replacementssextendsvaluesskeys(sselfsrv((s)build/bdist.linux-i686/egg/jinja/nodes.pys get_itemsIs  cCs$d|i|i|i|ifSdS(NsTrans(%r, %r, %r, %r)(sselfssingularsplurals indicators replacements(sself((s)build/bdist.linux-i686/egg/jinja/nodes.pys__repr__Ps(s__name__s __module__s__doc__s__init__s get_itemss__repr__(((s)build/bdist.linux-i686/egg/jinja/nodes.pysTrans=s   N(s__doc__scompilersasts inc_linenos get_nodessNodesTextslistsNodeListsTemplatesForLoops IfConditionsCyclesPrintsMacrosSetsFiltersBlocksExtendssIncludesTrans(sNodesForLoops IfConditionsNodeListsastsTextsTranssFiltersSetsExtendssTemplatesMacrosPrints inc_linenos get_nodessIncludesBlocksCycle((s)build/bdist.linux-i686/egg/jinja/nodes.pys? s$   PK݉w6lZlZjinja/loaders.pyc; iFc@sdZdkZdkZdkZdklZdklZdkl Z dk l Z l Z dk lZlZdklZlZydklZlZlZWn!ej oeZZZnXd d d d d gZdZdZdefdYZdefdYZdefdYZd eefdYZ d eefdYZ!d eefdYZ"d efdYZ#d efdYZ$dS(s jinja.loaders ~~~~~~~~~~~~~ Jinja loader classes. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. N(spath(sLock(sParser(sPythonTranslatorsTemplate(sTemplateNotFoundsTemplateSyntaxError(s CacheDictsraise_syntax_error(sresource_existssresource_stringsresource_filenamesFileSystemLoaders PackageLoaders DictLoaders ChoiceLoadersFunctionLoadercCsfti|tiigi}|idD],}|o|ddjo||q)q)~SdS(s0 Return the filesystem filename wanted. s/is.N( spathsjoins searchpathssepsappends_[1]snamessplitsp(s searchpathsnames_[1]sp((s+build/bdist.linux-i686/egg/jinja/loaders.pysget_template_filename scCs+ti|dtid|iSdS(s0 Return the filename for a cached file. sjinja_%s.caches jinja(%s)tmplN(spathsjoins cachepathsshasnewsnames hexdigest(s cachepathsname((s+build/bdist.linux-i686/egg/jinja/loaders.pys get_cachename(ss LoaderWrappercBsMtZdZdZedZedZedZdZ dZ RS(sg Wraps a loader so that it's bound to an environment. Also handles template syntax errors. cCsV||_||_|itjo'|i|_|_|_t|_ n t |_ dS(N( s environmentsselfsloadersNones_loader_missings get_sourcesparsesloadsFalses availablesTrue(sselfs environmentsloader((s+build/bdist.linux-i686/egg/jinja/loaders.pys__init__6s    cCs)t|}|ii|i||SdS(s&Retrieve the sourcecode of a template.N(sstrsnamesselfsloaders get_sources environmentsparent(sselfsnamesparent((s+build/bdist.linux-i686/egg/jinja/loaders.pys get_source?s cCs)t|}|ii|i||SdS(s!Retreive a template and parse it.N(sstrsnamesselfsloadersparses environmentsparent(sselfsnamesparent((s+build/bdist.linux-i686/egg/jinja/loaders.pysparseEs cCs[t|}y|ii|i||SWn+tj o}t }t ||inXdS(s 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. N( sstrsnamesselfsloadersloads environments translatorsTemplateSyntaxErrorsesTrues__traceback_hide__sraise_syntax_error(sselfsnames translatorses__traceback_hide__((s+build/bdist.linux-i686/egg/jinja/loaders.pysloadKs cOstddS(sOHelper method that overrides all other methods if no loader is defined.sno loader definedN(s RuntimeError(sselfsargsskwargs((s+build/bdist.linux-i686/egg/jinja/loaders.pys_loader_missingYscCs|itj SdS(N(sselfsloadersNone(sself((s+build/bdist.linux-i686/egg/jinja/loaders.pys __nonzero__^s( s__name__s __module__s__doc__s__init__sNones get_sourcesparsesPythonTranslatorsloads_loader_missings __nonzero__(((s+build/bdist.linux-i686/egg/jinja/loaders.pys LoaderWrapper0s     s BaseLoadercBs tZdZdZdZRS(sP 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. cCs/|i|||}t|||iSdS(s+ Load and parse a template N(sselfs get_sources environmentsnamesparentssourcesParsersparse(sselfs environmentsnamesparentssource((s+build/bdist.linux-i686/egg/jinja/loaders.pysparsenscCs)|i||t}|i||SdS(s/ Load and translate a template N(sselfsparses environmentsnamesNonesasts translatorsprocess(sselfs environmentsnames translatorsast((s+build/bdist.linux-i686/egg/jinja/loaders.pysloadus(s__name__s __module__s__doc__sparsesload(((s+build/bdist.linux-i686/egg/jinja/loaders.pys BaseLoaderbs  sCachedLoaderMixincBs tZdZdZdZRS(sG Mixin this class to implement simple memory and disk caching. cCsl|ot||_n t|_||_t|d o t |_ n ||_ h|_ t |_dS(Nscheck_source_changed(s use_memcaches CacheDicts cache_sizesselfs_CachedLoaderMixin__memcachesNones cache_folders _CachedLoaderMixin__cache_foldershasattrsFalses_CachedLoaderMixin__auto_reloads auto_reloads_CachedLoaderMixin__timessLocks_CachedLoaderMixin__lock(sselfs use_memcaches cache_sizes cache_folders auto_reload((s+build/bdist.linux-i686/egg/jinja/loaders.pys__init__s     c CsE|iiz#|tjot}|io|i||}nt}|i tj o ||i jo<|i |}|tj o||i |jo t}qn|itj ot|i|}|tj o |tjoyti|}Wntj o d}nX|djp ||jo7t|d}zti||}Wd|iXqn0|tjo"tt|i|||}nt|d}z|i|Wd|iXn|tj o<|i tj o$ti|i |<||i |scCs |djS(Ns variable_end(st(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pysscCs |djS(Ns comment_end(st(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pysscCs|djo|ddfjS(Nsnameselsesendfor(stsd(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pysscCs|djo |djS(Nsnamesendfor(stsd(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pysscCs |djo|dddfjS(Nsnameselseselifsendif(stsd(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pysscCs|djo |djS(Nsnamesendif(stsd(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pysscCs|djo |djS(Nsnames endfilter(stsd(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pysscCs|djo |djS(Nsnamesendmacro(stsd(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pys scCs|djo |djS(Nsnamesendblock(stsd(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pys!scCs|djo |djS(Nsnamesendtrans(stsd(spstsd((s*build/bdist.linux-i686/egg/jinja/parser.pys"ss(?:[^\d]*(\d+)[^\d]*)+cCsti|}|oatt|idd}|id\}}|t |t || |||}n'|i dd\}}d||fS|SdS(s Increment a string is.s%s2.%sN(s string_inc_ressearchsssmsstrsintsgroupsnextsspansstartsendsmaxslensrsplitsnamesext(sssendsnamesmsextsnextsstart((s*build/bdist.linux-i686/egg/jinja/parser.pys inc_string(s-sParsercBstZdZedZdZdZdZdZdZ dZ dZ d Z d Z d Zd Zd ZdZdZedZdZRS(s] The template parser class. Transforms sourcecode into an abstract syntax tree. cCs ||_t|to|i|id}n||_||_|ii ||_ t |_ t |_hd|i<d|i<d|i<d|i<d|i<d|i<d|i<d |i<d |i<d |i<d |i<d |i<|_dS(Nsignoresrawsforsifscyclessetsfiltersprintsmacrosblocksextendssincludestrans(s environmentsselfs isinstancessourcesstrsdecodestemplate_charsetsfilenameslexerstokenizes tokenstreamsNonesextendsssetsblocksshandle_raw_directiveshandle_for_directiveshandle_if_directiveshandle_cycle_directiveshandle_set_directiveshandle_filter_directiveshandle_print_directiveshandle_macro_directiveshandle_block_directiveshandle_extends_directiveshandle_include_directiveshandle_trans_directives directives(sselfs environmentssourcesfilename((s*build/bdist.linux-i686/egg/jinja/parser.pys__init__>s     cCsEt|}|otd||intd||idS(s 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. s-raw directive does not support any arguments.s"missing end tag for raw directive.N(slistsgensargssTemplateSyntaxErrorslinenosselfsfilename(sselfslinenosgensargs((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_raw_directiveXs c sgd}|i||d}|it}|i i ddjo |i |it t }nt}|i ti||i|i||tSdS(sB Handle a for directive and return a ForLoop node c#ssxlD]d}|dddfjoCyi}Wn$tj oitdSnX|V|Vq|VqWdS(s<Wrap the generator to check if we have a recursive for loop.isnames recursiveN(sgenstokensnextsitems StopIterations recursivesappendsTrue(sitemstoken(s recursivesgen(s*build/bdist.linux-i686/egg/jinja/parser.pyswrapgenns  s for %s:passiselseN(s recursiveswrapgensselfs parse_pythonslinenosastssubparses switch_forsbodys tokenstreamsnextsclose_remaining_blocks end_of_forsTrueselse_sNonesnodessForLoopsassignslistsbool(sselfslinenosgensbodys recursivesastselse_swrapgen((sgens recursives*build/bdist.linux-i686/egg/jinja/parser.pyshandle_for_directivegs   cCs|i||d}|idd|itfg}xto|i i \}}}|djo!|i |itt}Pq;|djoU|i itt}|i||d}|i|idd|itfq;t}Pq;W|i ti|||SdS(s( Handle if/else blocks. s if %s:passiselseselifN(sselfs parse_pythonslinenosgensaststestsssubparses switch_ifsTrues tokenstreamsnextstokensneedlesclose_remaining_blocks end_of_ifselse_s fetch_untils end_of_blocksappendsNonesnodess IfCondition(sselfslinenosgenstestssastsneedlestokenselse_((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_if_directives$#   + cCs3|i||d}ti||iidSdS(s3 Handle {% cycle foo, bar, baz %}. s _cycle((%s))iN( sselfs parse_pythonslinenosgensastsnodessCyclesexprsargs(sselfslinenosgensast((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_cycle_directivescCsyK|i}|ddjp|idddfjo tnWn.ttfj otd||inX|i ||d}|di d o!td |d||int i |t|dd |iSd S( s8 Handle {% set foo = 'value of foo' %}. isnamesoperators=sinvalid syntax for sets(%s)is_s9illegal use of keyword %r as identifier in set statement.iN(sgensnextsnames ValueErrors StopIterationsTemplateSyntaxErrorslinenosselfsfilenames parse_pythonsastsendswithsnodessSetsstrsexpr(sselfslinenosgensnamesast((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_set_directives . cCsY|i||d}|itt}|i t i |||i i di dSdS(s9 Handle {% filter foo|bar %} directives. s_filter(dummy|%s)iiN(sselfs parse_pythonslinenosgensastssubparses end_of_filtersTruesbodysclose_remaining_blocksnodessFiltersexprsargs(sselfslinenosgensbodysast((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_filter_directives  cCso|i||d}|ii}t|djo#t dt|||i nt i ||dSdS(s7 Handle {{ foo }} and {% print foo %}. s print_(%s)isNinvalid argument count for print; print requires exactly one argument, got %d.iN( sselfs parse_pythonslinenosgensastsexprsargss argumentsslensTemplateSyntaxErrorsfilenamesnodessPrint(sselfslinenosgensasts arguments((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_print_directives c Csy|i}Wn(tj otd||inX|ddjo!td|d||in7|did o!td|d||in|i ||dt |dd }|i t t}|i|ip|iotd ||in|iotgt|it|i|i}g}xpt|iD]U\}} | id otd | ||in|i| d ||fq]Wnt}ti||i||Sd S( s2 Handle {% macro foo bar, baz %}. smacro requires a nameisnamesexpected 'name', got %ris_s(illegal use of keyword %r as macro name.sdef %s(%%s):passis,variable length macro signature not allowed.s,illegal use of keyword %r as macro argument.N(sgensnexts macro_names StopIterationsTemplateSyntaxErrorslinenosselfsfilenamesendswiths parse_pythonsstrsastssubparses end_of_macrosTruesbodysclose_remaining_blocksvarargsskwargssargnamessNoneslensdefaultssargss enumeratesidxsargnamesappendsnodessMacrosname( sselfslinenosgensbodys macro_namesdefaultssidxsastsargssargname((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_macro_directives> '  *  #cCsBt|}| otd||in|id}|ddjo!td|d|t in|di d o!td|d||in|dd }|otd ||in||i jotd |||in|i i ||itt}|iti|||Sd S( s? Handle block directives used for inheritance. sblock requires a nameiisnamesexpected 'name', got %ris_s(illegal use of keyword %r as block name.is+block got too many arguments, requires one.sblock %r defined twiceN(slistsgenstokenssTemplateSyntaxErrorslinenosselfsfilenamespops block_namesseilfsendswithsnamesblockssaddssubparsesend_of_block_tagsTruesbodysclose_remaining_blocksnodessBlock(sselfslinenosgensbodysnamestokenss block_name((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_block_directives0    cCst|}t|djp|dddjotd||in|it j otd|nt i ||dddd!|_dS( sD Handle the extends directive used for inheritance. iisstringsextends requires a stringsextends called twiceiiN( slistsgenstokensslensTemplateSyntaxErrorslinenosselfsfilenamesextendssNonesnodessExtends(sselfslinenosgenstokens((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_extends_directive$s ( cCspt|}t|djp|dddjotd||inti ||dddd!SdS(sK Handle the include directive used for template inclusion. iisstringsinclude requires a stringiiN( slistsgenstokensslensTemplateSyntaxErrorslinenosselfsfilenamesnodessInclude(sselfslinenosgenstokens((s*build/bdist.linux-i686/egg/jinja/parser.pyshandle_include_directive0s  ( c s5}yy|i\Wntj oh}t}nNXdjo]y|iWn3tj o't i dd!tttSnXt d|ind} | |}t}h}x|i|diiD]}|itij ot d|in|iid o t d |i|in|tjo|id }n|i||id \}}} |djo| id| q | i| q Wd|di| id} yt | d}Wn1t j o%}td||i|inXt|iidjptd t|ii|iid }ti|||Sd S( s Convert the passed generator into a flat string representing python sourcecode and return an ast node or raise a TemplateSyntaxError. sstringsusu sutf-8sexecsinvalid syntaxisget %d nodes, 1 expectediN(stokenssgenst_linenost_tokenst_datasappendstemplatesjoinsencodessourcesparsesasts SyntaxErrorsesTemplateSyntaxErrorslinenosselfsfilenameslensnodesnodessAssertionErrorsresults inc_lineno( sselfslinenosgenstemplatesresultst_linenosesastst_tokenstokensssourcest_data((s*build/bdist.linux-i686/egg/jinja/parser.pys parse_pythons"  3cCs4|it}|g}x|o|i}|iti ti fjoK|i i d o#t d|i |i|in|i d |_ n_|itijoK|ii d o#t d|i |i|in|id |_n|i|_|i|iqWti|i||iSdS(s Parse the template and return a Template node. Also unescape the names escaped by the lexer (unused python keywords) and set the filename attributes for all nodes in the tree. s_s(illegal use of keyword %r as identifier.is,illegal use of keyword %r as attribute name.N(sselfssubparsesNonesbodystodospopsnodes __class__sastsAssNamesNamesnamesendswithsTemplateSyntaxErrorslinenosfilenamesGetattrsattrnamesextends getChildNodessnodessTemplatesextends(sselfsbodysnodestodo((s*build/bdist.linux-i686/egg/jinja/parser.pysparses(     c sd}|iid}ti|x|iD]v\}}}|djo|ii t t q5|djo6|ii tt }i|id||q5|djo|ii tt }y|i\}}}Wn(tj otd||inX|djo#td ||f||in|tj o||||o-| o|ii|||n|Sn||ijo|i|||}n||ijp|id  o |id o#td t|||in>|id o|d }ntdt|||i|tj oi|qq5|djoiti||q5t d||fq5W|tj otd||in|SdS(s 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 tokenstream 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 tokenstream. cs'tdjo dSnSdS(s+Helper function to remove unused nodelists.iiN(slensresult((sresult(s*build/bdist.linux-i686/egg/jinja/parser.pysfinishs is comment_beginsvariable_beginsprints block_beginsunexpected end of blocksnamesunexpected %r token (%r)s_sendsunexpected directive %risunknown directive %rsdatasunexpected token %r (%r)sunexpected end of templateN(!sfinishsselfs tokenstreamslastslinenosnodessNodeListsresultstokensdatas drop_untilsend_of_commentsTrues fetch_untilsend_of_variablesgensappends directivess end_of_blocksnexts StopIterationsTemplateSyntaxErrorsfilenamestestsNones drop_needlespushsnodesendswiths startswithsstrsTextsAssertionError( sselfstests drop_needlesnodesfinishstokenslinenosdatasgensresult((sresults*build/bdist.linux-i686/egg/jinja/parser.pyssubparse s\     !    1     cCs|iid}y|ii\}}}Wn(tj otd||i nX|djotd|||i ndS(s If we opened a block tag because one of our tags requires an end tag we can use this method to drop the rest of the block from the stream. If the next token isn't the block end we throw an error. ismissing closing tags block_endsexpected close tag, found %rN( sselfs tokenstreamslastslinenosnextstokensdatas StopIterationsTemplateSyntaxErrorsfilename(sselfstokenslinenosdata((s*build/bdist.linux-i686/egg/jinja/parser.pysclose_remaining_blockls   (s__name__s __module__s__doc__sNones__init__shandle_raw_directiveshandle_for_directiveshandle_if_directiveshandle_cycle_directiveshandle_set_directiveshandle_filter_directiveshandle_print_directiveshandle_macro_directiveshandle_block_directiveshandle_extends_directiveshandle_include_directiveshandle_trans_directives parse_pythonsparsesFalsessubparsesclose_remaining_block(((s*build/bdist.linux-i686/egg/jinja/parser.pysParser7s$    "    + "   `(!s__doc__srescompilersastsparses compiler.miscs set_filenamesjinjasnodessjinja.datastructures TokenStreamsjinja.exceptionssTemplateSyntaxErrorssets NameErrorssetssSets end_of_blocksend_of_variablesend_of_comments switch_fors end_of_fors switch_ifs end_of_ifs end_of_filters end_of_macrosend_of_block_tags end_of_transscompiles string_inc_res inc_stringsobjectsParser(ssets end_of_forsparses switch_forsTemplateSyntaxErrors set_filenamesresnodess TokenStreamsend_of_variables end_of_macrosend_of_block_tagsasts end_of_transs end_of_blocks switch_ifs end_of_filtersend_of_commentsParsers string_inc_res end_of_ifs inc_string((s*build/bdist.linux-i686/egg/jinja/parser.pys? s0                 PK݉w6*$$jinja/plugin.pyc; iFc@sFdZdklZdklZlZlZdklZdZ dS(s jinja.plugin ~~~~~~~~~~~~ Support for the `GeneralTemplateInterface`__. __ http://trac.pocoo.org/wiki/GeneralTemplateInterface :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. (s Environment(sFunctionLoadersFileSystemLoaders PackageLoader(sTemplateNotFoundc svd|jo2|dt|dj otdqbn$|idt}|idt}|idt}|idd}|id t}|id t }d |jo)t|id |||||d d?gZd@efdAYZdBefdCYZdS(Ds{ jinja.lexer ~~~~~~~~~~~ :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. N(s TokenStream(sTemplateSyntaxError(sSets\s+(?m)s[a-zA-Z_][a-zA-Z0-9_]*s<('([^'\\]*(?:\\.[^'\\]*)*)'|"([^"\\]*(?:\\.[^"\\]*)*)")(?ms)s \d+(\.\d+)*s(%s)s|s+s-s*s/s%s[s]s(s)s{s}s.s:s,s==ss<=s>=s!=s=uor\buand\bunot\buin\buissandsblockscycleselifselsesendblocks endfiltersendforsendifsendmacrosendrawsendtranssextendssfiltersforsifsinsincludesissmacrosnotsors pluralizesraws recursivessetstranssFailurecBs#tZdZedZdZRS(so Class that raises a `TemplateSyntaxError` if called. Used by the `Lexer` to specify known errors. cCs||_||_dS(N(smessagesselfsclss error_class(sselfsmessagescls((s)build/bdist.linux-i686/egg/jinja/lexer.pys__init__3s cCs|i|i|dS(N(sselfs error_classsmessageslineno(sselfslineno((s)build/bdist.linux-i686/egg/jinja/lexer.pys__call__7s(s__name__s __module__s__doc__sTemplateSyntaxErrors__init__s__call__(((s)build/bdist.linux-i686/egg/jinja/lexer.pysFailure-s  sLexercBs)tZdZdZdZdZRS(s Class that implements a lexer for a given environment. Automatically created by the environment class, usually you don't have to do that. c Csd}ti}tttftdtftdtftdtft dtfg}d|i fd|i fd|ifg}|id hd |d ||i ||ifd td tftf|ddigi}|D]&\}}|d|||fq~d dfdf|dd tfg<d|d||iddfdf|dtdftfg<d|||i|iodpdddfg|<d|||iddfg|<|_dS(NcCsti|titiBS(N(srescompilesxsMsS(sx((s)build/bdist.linux-i686/egg/jinja/lexer.pysCssnumbersnamesoperatorsstringscommentsblocksvariablecCs!tt|dt|dS(Ni(scmpslensbsa(sasb((s)build/bdist.linux-i686/egg/jinja/lexer.pysXssroots%(%s\s*raw\s%s)(.*?)(%s\s*endraw\s*%s)isdatas (.*?)(?:%s)s|s(?P<%s_begin>%s)s#bygroups.+s comment_begins (.*?)(%s)s comment_ends#pops(.)sMissing end of comment tags block_begins\n?ss block_endsvariable_begins variable_end(scsresescapeses whitespace_resNones number_resname_res operator_res string_res tag_ruless environmentscomment_start_stringsblock_start_stringsvariable_start_stringsroot_tag_rulesssortsblock_end_stringsjoinsappends_[1]snsrscomment_end_stringsFailures trim_blockssvariable_end_stringsselfsrules( sselfs environmentscses tag_rulessroot_tag_rulessns_[1]sr((s)build/bdist.linux-i686/egg/jinja/lexer.pys__init__As   B *cs d}t|SdS(sW Simple tokenize function that yields ``(position, type, contents)`` tuples. Wrap the generator returned by this function in a `TokenStream` to get real token instances and be able to push tokens back to the stream. That's for example done by the parser. Additionally non keywords are escaped. c#s\xUiD]D\}}}|djo |tjo|d7}n|||fVqWdS(Nsnames_(sselfs tokeniterssourceslinenostokensvalueskeywords(svaluestokenslineno(sselfssource(s)build/bdist.linux-i686/egg/jinja/lexer.pysfilter~s N(sfilters TokenStream(sselfssourcesfilter((sselfssources)build/bdist.linux-i686/egg/jinja/lexer.pystokenizeusccsGt|di|i}d}d}dg} |id}t |}xt ox|D]\}} }|i||}|ot| toCxt| D].\}}|tjo4|i|} | o|| id7}qqqt|to||i|dq|djoix|iiD]>\} }|tj o%|| |fV||id7}Pq?q?Wt!d|q|i|d} | o||| fVn|| id7}qWnI|i} | tj o| o|| | fVqn|| id7}|i#}|tj o|djo| i%nq|djoVx`|iiD]+\} }|tj o| i&| PqwqwWt!d|n| i&||i| d }n"||jot!d |n|}Pq`q`W||jod Snt'd |||f|qRWd S( s[ This method tokenizes the text and returns the tokens in a generator. Use this method if you just want to tokenize a template. The output you get is not compatible with the input the jinja parser wants. The parser uses the `tokenize` function with returns a `TokenStream` with some escaped tokens. s iisroots#bygroups?%r wanted to resolve the token dynamically but no group matcheds#popsC%r wanted to resolve the new state dynamically but no group matchedis,%r yielded empty string without stack changeNsunexpected char %r at %d((stypessourcesjoins splitlinessposslinenosstacksselfsruless statetokensslens source_lengthsTruesregexstokenss new_statesmatchsms isinstancestuples enumeratesidxstokensNonesgroupsgscountsFailuresstarts groupdicts iteritemsskeysvalues RuntimeErrorsdatasendspos2spopsappendsTemplateSyntaxError(sselfssources source_lengths new_states statetokenssposspos2sregexslinenostokensskeysdatasstacksgsidxsmsvaluestoken((s)build/bdist.linux-i686/egg/jinja/lexer.pys tokenitersz!                        (s__name__s __module__s__doc__s__init__stokenizes tokeniter(((s)build/bdist.linux-i686/egg/jinja/lexer.pysLexer;s  4 (s__doc__sresjinja.datastructures TokenStreamsjinja.exceptionssTemplateSyntaxErrorssets NameErrorssetssSetscompiles whitespace_resname_res string_res number_resjoinsappends_[1]sxs isinstancesunicodesstrsescapes operator_reskeywordssobjectsFailuresLexer(sname_ressets whitespace_resLexersFailuresTemplateSyntaxErrors string_resres operator_res number_res_[1]skeywordssxs TokenStream((s)build/bdist.linux-i686/egg/jinja/lexer.pys?s    ]PK݉w6K=??jinja/utils.pyc; iFc @sdZdkZdkZdkZdklZlZdklZl Z l Z dk l Z dk lZlZydklZWnej o eZnXdZeidZhd d <d d <d d<dds>s"s"s (&|<|>|")s(&|<|>)s^(\d+)$s(\s+)s4^(?P(?:%s)*)(?P.*?)(?P(?:%s)*)$s|s(s.s,s)s s&^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$cCs"tt| id|SdS(s Escape an object x. cCst|iS(N(s _escape_pairssmsgroup(sm((s)build/bdist.linux-i686/egg/jinja/utils.pys>sN(sMarkups _escape_ress attributessubsx(sxs attribute((s)build/bdist.linux-i686/egg/jinja/utils.pysescape:sc Cs|d}ti|} |odpd}xt| D]\}}t i |} | oz| i \}} }| idpyd| jol| id o[t| djoH| dtitijo-| idp| id p | id od | ||| f} n| idp | id od | ||| f} nd| jo,| id od| j o ti | od| | f} n|| ||jo|| || |Oss rel="nofollow"sswww.s@shttp://is.orgs.nets.coms%sshttps://s%ss:s%suN(strim_url_limitstrim_urls_word_split_ressplitstextswordssnofollows nofollow_attrs enumeratesiswords_punctuation_resmatchsgroupssleadsmiddlestrails startswithslensstringsletterssdigitssendswiths_simple_email_resjoin( stextstrim_url_limitsnofollowswordsleadstrim_urlstrailsis nofollow_attrsmiddleswordssmatch((s)build/bdist.linux-i686/egg/jinja/utils.pysurlizeBs$     <cCs!dkl}||iSdS(sB Use this function in templates to get a printed context. (spformatN(spprintspformatscontextsto_dict(senvscontextspformat((s)build/bdist.linux-i686/egg/jinja/utils.pys debug_contextos cCsz|tjo t}n|tjo t}n|tjo d}n|tjotd||Snt|||SdS(sF "Safe" form of range that does not generate too large lists. iiN(sstarts MAX_RANGEsstopsstepsNonesrange(sstartsstopsstep((s)build/bdist.linux-i686/egg/jinja/utils.pys safe_rangexs       iicCsdit|SdS(s3 Concatenate the generator output. uN(sjoinstuplesgen(sgen((s)build/bdist.linux-i686/egg/jinja/utils.pyscapture_generatorscCsdi|SdS(s2 Concatenate the generator output uN(sjoinsgen(sgen((s)build/bdist.linux-i686/egg/jinja/utils.pyscapture_generatorscsd}|SdS(sj Used by the python translator to capture output of substreams. (macros, filter sections etc) cst||SdS(N(scapture_generatorsfsargsskwargs(sargsskwargs(sf(s)build/bdist.linux-i686/egg/jinja/utils.pyswrappedsN(swrapped(sfswrapped((sfs)build/bdist.linux-i686/egg/jinja/utils.pys buffereaters c Bse} e|eo|i}|i}n |}h}d|d}e |d|d}hd|<d|<de||<d|<}y|||UWneiSnXd S( s Raise an exception "in a template". Return a traceback object. This is used for runtime debugging, not compile time. s israise __exception_to_raise__sexecs__name__s__file__s __loader__s__exception_to_raise__N(sTrues__traceback_hide__s isinstancescontext_or_envsContexts environmentsenvsto_dicts namespaceslinenosoffsetscompilesfilenamescodesTracebackLoaders exceptionsglobalsssyssexc_info( s exceptionsfilenameslinenoscontext_or_envscodesenvs namespacesglobalssoffsets__traceback_hide__((s)build/bdist.linux-i686/egg/jinja/utils.pysfake_template_exceptions 3c Cs|ii} |id}t}|t | jo|Snx|djoyt i | |}|tj oK|i \}}|djo t}n|djot|}nPn|d8}q@W| o|Snt||||dSdS(s> Translate an exception and return the new traceback. iisNoneiN(stemplatestranslated_sources splitliness sourceliness tracebacks tb_linenosstartpossNonesfilenameslens_debug_info_ressearchsmsgroupsslinenosintsfake_template_exceptions exc_valuescontext( stemplatesexc_types exc_values tracebackscontextsmslinenosstartpossfilenames sourcelines((s)build/bdist.linux-i686/egg/jinja/utils.pystranslate_exceptions*       cCs7t||i|i|}|d|d|ddS(s 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. iiiN(sfake_template_exceptions exceptionsfilenameslinenosenvsexc_info(s exceptionsenvsexc_info((s)build/bdist.linux-i686/egg/jinja/utils.pysraise_syntax_errors c Csq|g}g}xG|o?|i}|itjo#|i|i|i |i fn|it jo#|iit jo|ii djot|iddfjog}x|iD],}|itj oPn|i|iqWt|djo|d}t}n|\}}}|i|i||fqAn|i|iqW|id|SdS(s= Collect all translatable strings for the given ast. s_iiicCst|d|dS(Ni(scmpsasb(sasb((s)build/bdist.linux-i686/egg/jinja/utils.pyssN(saststodosresultspopsnodes __class__sTranssappendslinenossingularspluralsCallFuncsNamesnameslensargssargsConstsvaluesNones_sextends getChildNodesssort( sastsnodessingularspluralsargssresultsargstodos_((s)build/bdist.linux-i686/egg/jinja/utils.pyscollect_translationss0  #6   !sTracebackLoadercBs tZdZdZdZRS(sC Fake importer that just returns the source of a template. cCs|i|_||_dS(N(s environmentsloadersselfsfilename(sselfs environmentsfilename((s)build/bdist.linux-i686/egg/jinja/utils.pys__init__s cCs|ii|iSdS(N(sselfsloaders get_sourcesfilename(sselfsimpname((s)build/bdist.linux-i686/egg/jinja/utils.pys get_sources(s__name__s __module__s__doc__s__init__s get_source(((s)build/bdist.linux-i686/egg/jinja/utils.pysTracebackLoader s  s CacheDictcBstZdZdZdZedZedZdZdZ dZ dZ d Z d Z d Zd Zd ZeZRS(s 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. cs||_h|_ttj ot|_|ii|_n%g|_|iid|_|ii|_ |ii |_ |ii |_ dS(Ncs dS(Ni(spop((spop(s)build/bdist.linux-i686/egg/jinja/utils.pysMs(scapacitysselfs_mappingsdequesNones_queuespoplefts_popleftspops_popsremoves_removesappends_append(sselfscapacityspop((spops)build/bdist.linux-i686/egg/jinja/utils.pys__init__As      cCs7t|i}|ii|i|i|_|SdS(N(s CacheDictsselfscapacitysrvs_mappingsupdates_queue(sselfsrv((s)build/bdist.linux-i686/egg/jinja/utils.pyscopyTs cCs!||jo ||Sn|SdS(N(skeysselfsdefault(sselfskeysdefault((s)build/bdist.linux-i686/egg/jinja/utils.pysgetZs  cCs+||jo ||Sn|||<|SdS(N(skeysselfsdefault(sselfskeysdefault((s)build/bdist.linux-i686/egg/jinja/utils.pys setdefault_s   cCs?|iiy|iiWntj o|i2nXdS(N(sselfs_mappingsclears_queuesAttributeError(sself((s)build/bdist.linux-i686/egg/jinja/utils.pyscleares  cCs||ijSdS(N(skeysselfs_mapping(sselfskey((s)build/bdist.linux-i686/egg/jinja/utils.pys __contains__lscCst|iSdS(N(slensselfs_mapping(sself((s)build/bdist.linux-i686/egg/jinja/utils.pys__len__oscCsd|ii|ifSdS(Ns<%s %r>(sselfs __class__s__name__s_mapping(sself((s)build/bdist.linux-i686/egg/jinja/utils.pys__repr__rscCsG|i|}|id|jo|i||i|n|SdS(Ni(sselfs_mappingskeysrvs_queues_removes_append(sselfskeysrv((s)build/bdist.linux-i686/egg/jinja/utils.pys __getitem__xs   cCsl||ijo|i|n.t|i|ijo|i|i=n|i|||i|sN((((s)build/bdist.linux-i686/egg/jinja/tests.pystest_oddscCs dSdS(s. Return true of the variable is even. cCs|ddjS(Nii(sv(sescsv((s)build/bdist.linux-i686/egg/jinja/tests.pyssN((((s)build/bdist.linux-i686/egg/jinja/tests.pys test_evenscCs dSdS(s Return true if the variable is defined: .. sourcecode:: jinja {% if variable is defined %} value of variable: {{ variable }} {% else %} variable is not defined {% endif %} See also the ``default`` filter. cCs |tj S(N(svs Undefined(sescsv((s)build/bdist.linux-i686/egg/jinja/tests.pys0sN((((s)build/bdist.linux-i686/egg/jinja/tests.pys test_defined"s cCs dSdS(s3 Return true if the variable is lowercase. cCst|to |iS(N(s isinstancesvs basestringsislower(sescsv((s)build/bdist.linux-i686/egg/jinja/tests.pys7sN((((s)build/bdist.linux-i686/egg/jinja/tests.pys test_lower3scCs dSdS(s3 Return true if the variable is uppercase. cCst|to |iS(N(s isinstancesvs basestringsisupper(sescsv((s)build/bdist.linux-i686/egg/jinja/tests.pys>sN((((s)build/bdist.linux-i686/egg/jinja/tests.pys test_upper:scCs dSdS(s1 Return true if the variable is numeric. cCs<t|tttfp#t|toti|tj S(N( s isinstancesvsintslongsfloats basestrings number_resmatchsNone(sescsv((s)build/bdist.linux-i686/egg/jinja/tests.pysEsN((((s)build/bdist.linux-i686/egg/jinja/tests.pys test_numericAscCsd}|SdS(sc Return true if the variable is a sequence. Sequences are variables that are iterable. cCs+yt||iWn tSnXtSdS(N(slensvalues __getitem__sFalsesTrue(s environmentscontextsvalue((s)build/bdist.linux-i686/egg/jinja/tests.pyswrappedOs   N(swrapped(swrapped((s)build/bdist.linux-i686/egg/jinja/tests.pys test_sequenceJs cs}ttotitinAttotinttj o t nd}|SdS(sq Test if the variable matches the regular expression given. Note that you have to escape special chars using *two* backslashes, these are *not* raw strings. .. sourcecode:: jinja {% if var is matching('^\\d+$') %} var looks like a number {% else %} var doesn't really look like a number {% endif %} cs,tjotSni|tj SdS(N(sregexsNonesFalsessearchsvalue(s environmentscontextsvalue(sregex(s)build/bdist.linux-i686/egg/jinja/tests.pyswrappedms N( s isinstancesregexsunicodesrescompilesUsstrstypes regex_typesNoneswrapped(sregexswrapped((sregexs)build/bdist.linux-i686/egg/jinja/tests.pys test_matchingYs   soddsevensdefinedslowersuppersnumericssequencesmatching(s__doc__sresjinja.datastructures Undefinedscompiles number_restypes regex_typestest_odds test_evens test_defineds test_lowers test_uppers test_numerics test_sequences test_matchingsTESTS( s test_lowers regex_types Undefinedstest_odds test_numericsTESTSs test_defineds test_matchings test_evensres test_uppers number_res test_sequence((s)build/bdist.linux-i686/egg/jinja/tests.pys? s          PK݉w6hd'd'jinja/environment.pyc; iFc@sdZdkZdklZdklZdklZdkl Z l Z l Z l Z dk lZlZdklZlZlZdklZlZlZd efd YZdS( s jinja.environment ~~~~~~~~~~~~~~~~~ Provides a class that holds runtime and parsing time options. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. N(sLexer(sParser(s LoaderWrapper(s UndefinedsContextsMarkupsFakeTranslator(sescapescollect_translations(sFilterNotFounds TestNotFoundsSecurityException(sDEFAULT_FILTERSs DEFAULT_TESTSsDEFAULT_NAMESPACEs EnvironmentcBstZdZddddddeeddeeeeedZd Zed eeiZed Z d Z d Z dZ dZ dZdZdZdZdZdZdZRS(s The jinja environment. s{%s%}s{{s}}s{#s#}sutf-8cCs||_||_||_||_||_||_||_| |_| |_ | |_ | t jo t ip| |_ |t jo tip||_||_||_| t jo tip| |_t||_dS(N(sblock_start_stringsselfsblock_end_stringsvariable_start_stringsvariable_end_stringscomment_start_stringscomment_end_strings trim_blocksstemplate_charsetscharsetsloadersfilterssNonesDEFAULT_FILTERSscopystestss DEFAULT_TESTSs auto_escapes context_classs namespacesDEFAULT_NAMESPACEsglobalssLexerslexer(sselfsblock_start_stringsblock_end_stringsvariable_start_stringsvariable_end_stringscomment_start_stringscomment_end_strings trim_blockss auto_escapestemplate_charsetscharsets namespacesloadersfiltersstestss context_class((s/build/bdist.linux-i686/egg/jinja/environment.pys__init__s           ##  #cCst|||_dS(s1 Get or set the template loader. N(s LoaderWrappersselfsvalues_loader(sselfsvalue((s/build/bdist.linux-i686/egg/jinja/environment.pysloaderDscCs|iS(N(sss_loader(ss((s/build/bdist.linux-i686/egg/jinja/environment.pysIscCs t|||}|iSdS(s9Function that creates a new parser and parses the source.N(sParsersselfssourcesfilenamesparsersparse(sselfssourcesfilenamesparser((s/build/bdist.linux-i686/egg/jinja/environment.pysparseKscCs=dkl}dkl}|i||||iSdS(sLoad a template from a string.(sParser(sPythonTranslatorN(s jinja.parsersParsersjinja.translators.pythonsPythonTranslatorsprocesssselfssourcesparse(sselfssourcesParsersPythonTranslator((s/build/bdist.linux-i686/egg/jinja/environment.pys from_stringPs  cCs|ii|SdS(sNLoad a template from a filename. Only works if a proper loader is set.N(sselfs_loadersloadsfilename(sselfsfilename((s/build/bdist.linux-i686/egg/jinja/environment.pys get_templateVscCst|ttfjodSnVt|to|Sn>yt|SWn,tj o t|i|i dSnXdS(sW Convert a value to unicode with the rules defined on the environment. usignoreN( svaluesNones Undefineds isinstancesunicodes UnicodeErrorsstrsdecodesselfscharset(sselfsvalue((s/build/bdist.linux-i686/egg/jinja/environment.pys to_unicode[scCs tSdS(s 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! N(sFakeTranslator(sselfscontext((s/build/bdist.linux-i686/egg/jinja/environment.pysget_translatoriscCst|ii|SdS(s 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!) N(scollect_translationssselfsloadersparsesname(sselfsname((s/build/bdist.linux-i686/egg/jinja/environment.pysget_translationssscCsx|D]}||ijo|i|}nK|\}}||ijot|n|i|||i|<}||||}qW|SdS(s: Apply a list of filters on the variable. N( sfiltersskeyscontextscachesfuncs filternamesargssselfsFilterNotFoundsvalue(sselfsvaluescontextsfilterssfuncs filternamesargsskey((s/build/bdist.linux-i686/egg/jinja/environment.pys apply_filters{s c Cs||f}||ijo|i|}n?||ijot|n|i|||i|<}||||}|o | Snt |SdS(s/ Perform a test on a variable. N( stestnamesargsskeyscontextscachesfuncsselfstestss TestNotFoundsvaluesrvsinvertsbool( sselfscontextstestnamesargssvaluesinvertsrvsfuncskey((s/build/bdist.linux-i686/egg/jinja/environment.pys perform_tests  cCs|}x|D]}y||}Wq tttfj oot|| otSnt |dt }|t j o ||jot d|nt ||}q Xq W|SdS(s5 Get some attributes from an object. sjinja_allowed_attributessunsafe attributed %r accessedN( sobjsnodes attributessnames TypeErrorsKeyErrors IndexErrorshasattrs UndefinedsgetattrsNonesrsSecurityException(sselfsobjs attributessnodesnamesr((s/build/bdist.linux-i686/egg/jinja/environment.pys get_attributescCs|tj o|t|7}n|tj o|i|nt|dt pt|dt ot d|i nt|dt o||f|}n|||SdS(sg Function call helper. Called for all functions that are passed any arguments. sjinja_unsafe_calls alters_datasunsafe function %r calledsjinja_context_callableN(sdyn_argssNonesargsstuples dyn_kwargsskwargssupdatesgetattrsfsFalsesSecurityExceptions__name__sselfscontext(sselfsfscontextsargsskwargssdyn_argss dyn_kwargs((s/build/bdist.linux-i686/egg/jinja/environment.pys call_functions  &cCslt|dtpt|dtotd|int|dto|||Sn|SdS(s Function call without arguments. Because of the smaller signature and fewer logic here we have a bit of redundant code. sjinja_unsafe_calls alters_datasunsafe function %r calledsjinja_context_callableN(sgetattrsfsFalsesSecurityExceptions__name__sselfscontext(sselfsfscontext((s/build/bdist.linux-i686/egg/jinja/environment.pyscall_function_simples &cCs|tjp |tjodSnOt|ttttfot|Sn%t|t o|i |}n|i ot |t Sn|SdS(s 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. uN(svalues UndefinedsNones isinstancesintsfloatsMarkupsboolsunicodesselfs to_unicodes auto_escapesescapesTrue(sselfsvalue((s/build/bdist.linux-i686/egg/jinja/environment.pys finish_vars (s__name__s __module__s__doc__sFalsesNonesContexts__init__sloaderspropertysparses from_strings get_templates to_unicodesget_translatorsget_translationss apply_filterss perform_tests get_attributes call_functionscall_function_simples finish_var(((s/build/bdist.linux-i686/egg/jinja/environment.pys Environments 6*           (s__doc__sres jinja.lexersLexers jinja.parsersParsers jinja.loaderss LoaderWrappersjinja.datastructures UndefinedsContextsMarkupsFakeTranslators jinja.utilssescapescollect_translationssjinja.exceptionssFilterNotFounds TestNotFoundsSecurityExceptionsjinja.defaultssDEFAULT_FILTERSs DEFAULT_TESTSsDEFAULT_NAMESPACEsobjects Environment(s DEFAULT_TESTSs LoaderWrappersDEFAULT_NAMESPACEs UndefinedsParsersMarkupsLexersescapesSecurityExceptionsres TestNotFoundsContextsFakeTranslatorscollect_translationssDEFAULT_FILTERSs EnvironmentsFilterNotFound((s/build/bdist.linux-i686/egg/jinja/environment.pys? s    PK5w6G99jinja/translators/__init__.py# -*- coding: utf-8 -*- """ jinja.translators ~~~~~~~~~~~~~~~~~ The submodules of this module provide translators for the jinja ast which basically just is the python ast with a few more nodes. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ class Translator(object): """ Base class of all translators. """ def process(environment, tree): """ Process the given ast with the rules defined in environment and return a translated version of it. The translated object can be anything. The python translator for example outputs Template instances, a javascript translator would probably output strings. This is a static function. """ pass process = staticmethod(process) PKw6vvjinja/translators/python.py# -*- coding: utf-8 -*- """ jinja.translators.python ~~~~~~~~~~~~~~~~~~~~~~~~ This module translates a jinja ast into python code. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import sys from compiler import ast from jinja import nodes from jinja.nodes import get_nodes from jinja.parser import Parser from jinja.exceptions import TemplateSyntaxError from jinja.translators import Translator from jinja.utils import translate_exception, capture_generator def _to_tuple(args): """ Return a tuple repr without nested repr. """ return '(%s%s)' % ( ', '.join(args), len(args) == 1 and ',' or '' ) class Template(object): """ Represents a finished template. """ def __init__(self, environment, code, translated_source=None): self.environment = environment self.code = code self.translated_source = translated_source self.generate_func = None def dump(self, stream=None): """Dump the template into python bytecode.""" if stream is not None: from marshal import dump dump((self.code, self.translated_source), stream) else: from marshal import dumps return dumps((self.code, self.translated_source)) def load(environment, data): """Load the template from python bytecode.""" if isinstance(data, basestring): from marshal import loads code, src = loads(data) else: from marshal import load code, src = load(data) return Template(environment, code, src) load = staticmethod(load) def render(self, *args, **kwargs): """Render a template.""" if self.generate_func is None: ns = {'environment': self.environment} exec self.code in ns self.generate_func = ns['generate'] ctx = self.environment.context_class(self.environment, *args, **kwargs) try: return capture_generator(self.generate_func(ctx)) except: exc_type, exc_value, traceback = sys.exc_info() # translate the exception, We skip two frames. One # frame that is the "capture_generator" frame, and another # one which is the frame of this function traceback = translate_exception(self, exc_type, exc_value, traceback.tb_next.tb_next, ctx) raise exc_type, exc_value, traceback class PythonTranslator(Translator): """ Pass this translator a ast tree to get valid python code. """ def __init__(self, environment, node): self.environment = environment self.node = node self.constants = { 'true': 'True', 'false': 'False', 'none': 'None', 'undefined': 'Undefined' } self.handlers = { # jinja nodes nodes.Template: self.handle_template, nodes.Text: self.handle_template_text, nodes.NodeList: self.handle_node_list, nodes.ForLoop: self.handle_for_loop, nodes.IfCondition: self.handle_if_condition, nodes.Cycle: self.handle_cycle, nodes.Print: self.handle_print, nodes.Macro: self.handle_macro, nodes.Set: self.handle_set, nodes.Filter: self.handle_filter, nodes.Block: self.handle_block, nodes.Include: self.handle_include, nodes.Trans: self.handle_trans, # used python nodes ast.Name: self.handle_name, ast.AssName: self.handle_name, ast.Compare: self.handle_compare, ast.Const: self.handle_const, ast.Subscript: self.handle_subscript, ast.Getattr: self.handle_getattr, ast.AssTuple: self.handle_ass_tuple, ast.Bitor: self.handle_bitor, ast.CallFunc: self.handle_call_func, ast.Add: self.handle_add, ast.Sub: self.handle_sub, ast.Div: self.handle_div, ast.Mul: self.handle_mul, ast.Mod: self.handle_mod, ast.UnaryAdd: self.handle_unary_add, ast.UnarySub: self.handle_unary_sub, ast.Power: self.handle_power, ast.Dict: self.handle_dict, ast.List: self.handle_list, ast.Tuple: self.handle_list, ast.And: self.handle_and, ast.Or: self.handle_or, ast.Not: self.handle_not, ast.Slice: self.handle_slice, ast.Sliceobj: self.handle_sliceobj } self.unsupported = { ast.ListComp: 'list comprehensions', ast.From: 'imports', ast.Import: 'imports', } if hasattr(ast, 'GenExpr'): self.unsupported.update({ ast.GenExpr: 'generator expressions' }) self.require_translations = False # -- public methods def process(environment, node): translator = PythonTranslator(environment, node) filename = node.filename or '