Index: third_party/jinja2/runtime.py |
diff --git a/third_party/jinja2/runtime.py b/third_party/jinja2/runtime.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a4a47a284ed4a51ec9e54ae9b439682884e1905c |
--- /dev/null |
+++ b/third_party/jinja2/runtime.py |
@@ -0,0 +1,548 @@ |
+# -*- coding: utf-8 -*- |
+""" |
+ jinja2.runtime |
+ ~~~~~~~~~~~~~~ |
+ |
+ Runtime helpers. |
+ |
+ :copyright: (c) 2010 by the Jinja Team. |
+ :license: BSD. |
+""" |
+from itertools import chain, imap |
+from jinja2.nodes import EvalContext, _context_function_types |
+from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \ |
+ concat, internalcode, next, object_type_repr |
+from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \ |
+ TemplateNotFound |
+ |
+ |
+# these variables are exported to the template runtime |
+__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup', |
+ 'TemplateRuntimeError', 'missing', 'concat', 'escape', |
+ 'markup_join', 'unicode_join', 'to_string', 'identity', |
+ 'TemplateNotFound'] |
+ |
+#: the name of the function that is used to convert something into |
+#: a string. 2to3 will adopt that automatically and the generated |
+#: code can take advantage of it. |
+to_string = unicode |
+ |
+#: the identity function. Useful for certain things in the environment |
+identity = lambda x: x |
+ |
+ |
+def markup_join(seq): |
+ """Concatenation that escapes if necessary and converts to unicode.""" |
+ buf = [] |
+ iterator = imap(soft_unicode, seq) |
+ for arg in iterator: |
+ buf.append(arg) |
+ if hasattr(arg, '__html__'): |
+ return Markup(u'').join(chain(buf, iterator)) |
+ return concat(buf) |
+ |
+ |
+def unicode_join(seq): |
+ """Simple args to unicode conversion and concatenation.""" |
+ return concat(imap(unicode, seq)) |
+ |
+ |
+def new_context(environment, template_name, blocks, vars=None, |
+ shared=None, globals=None, locals=None): |
+ """Internal helper to for context creation.""" |
+ if vars is None: |
+ vars = {} |
+ if shared: |
+ parent = vars |
+ else: |
+ parent = dict(globals or (), **vars) |
+ if locals: |
+ # if the parent is shared a copy should be created because |
+ # we don't want to modify the dict passed |
+ if shared: |
+ parent = dict(parent) |
+ for key, value in locals.iteritems(): |
+ if key[:2] == 'l_' and value is not missing: |
+ parent[key[2:]] = value |
+ return Context(environment, parent, template_name, blocks) |
+ |
+ |
+class TemplateReference(object): |
+ """The `self` in templates.""" |
+ |
+ def __init__(self, context): |
+ self.__context = context |
+ |
+ def __getitem__(self, name): |
+ blocks = self.__context.blocks[name] |
+ return BlockReference(name, self.__context, blocks, 0) |
+ |
+ def __repr__(self): |
+ return '<%s %r>' % ( |
+ self.__class__.__name__, |
+ self.__context.name |
+ ) |
+ |
+ |
+class Context(object): |
+ """The template context holds the variables of a template. It stores the |
+ values passed to the template and also the names the template exports. |
+ Creating instances is neither supported nor useful as it's created |
+ automatically at various stages of the template evaluation and should not |
+ be created by hand. |
+ |
+ The context is immutable. Modifications on :attr:`parent` **must not** |
+ happen and modifications on :attr:`vars` are allowed from generated |
+ template code only. Template filters and global functions marked as |
+ :func:`contextfunction`\s get the active context passed as first argument |
+ and are allowed to access the context read-only. |
+ |
+ The template context supports read only dict operations (`get`, |
+ `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, |
+ `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` |
+ method that doesn't fail with a `KeyError` but returns an |
+ :class:`Undefined` object for missing variables. |
+ """ |
+ __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars', |
+ 'name', 'blocks', '__weakref__') |
+ |
+ def __init__(self, environment, parent, name, blocks): |
+ self.parent = parent |
+ self.vars = {} |
+ self.environment = environment |
+ self.eval_ctx = EvalContext(self.environment, name) |
+ self.exported_vars = set() |
+ self.name = name |
+ |
+ # create the initial mapping of blocks. Whenever template inheritance |
+ # takes place the runtime will update this mapping with the new blocks |
+ # from the template. |
+ self.blocks = dict((k, [v]) for k, v in blocks.iteritems()) |
+ |
+ def super(self, name, current): |
+ """Render a parent block.""" |
+ try: |
+ blocks = self.blocks[name] |
+ index = blocks.index(current) + 1 |
+ blocks[index] |
+ except LookupError: |
+ return self.environment.undefined('there is no parent block ' |
+ 'called %r.' % name, |
+ name='super') |
+ return BlockReference(name, self, blocks, index) |
+ |
+ def get(self, key, default=None): |
+ """Returns an item from the template context, if it doesn't exist |
+ `default` is returned. |
+ """ |
+ try: |
+ return self[key] |
+ except KeyError: |
+ return default |
+ |
+ def resolve(self, key): |
+ """Looks up a variable like `__getitem__` or `get` but returns an |
+ :class:`Undefined` object with the name of the name looked up. |
+ """ |
+ if key in self.vars: |
+ return self.vars[key] |
+ if key in self.parent: |
+ return self.parent[key] |
+ return self.environment.undefined(name=key) |
+ |
+ def get_exported(self): |
+ """Get a new dict with the exported variables.""" |
+ return dict((k, self.vars[k]) for k in self.exported_vars) |
+ |
+ def get_all(self): |
+ """Return a copy of the complete context as dict including the |
+ exported variables. |
+ """ |
+ return dict(self.parent, **self.vars) |
+ |
+ @internalcode |
+ def call(__self, __obj, *args, **kwargs): |
+ """Call the callable with the arguments and keyword arguments |
+ provided but inject the active context or environment as first |
+ argument if the callable is a :func:`contextfunction` or |
+ :func:`environmentfunction`. |
+ """ |
+ if __debug__: |
+ __traceback_hide__ = True |
+ if isinstance(__obj, _context_function_types): |
+ if getattr(__obj, 'contextfunction', 0): |
+ args = (__self,) + args |
+ elif getattr(__obj, 'evalcontextfunction', 0): |
+ args = (__self.eval_ctx,) + args |
+ elif getattr(__obj, 'environmentfunction', 0): |
+ args = (__self.environment,) + args |
+ try: |
+ return __obj(*args, **kwargs) |
+ except StopIteration: |
+ return __self.environment.undefined('value was undefined because ' |
+ 'a callable raised a ' |
+ 'StopIteration exception') |
+ |
+ def derived(self, locals=None): |
+ """Internal helper function to create a derived context.""" |
+ context = new_context(self.environment, self.name, {}, |
+ self.parent, True, None, locals) |
+ context.vars.update(self.vars) |
+ context.eval_ctx = self.eval_ctx |
+ context.blocks.update((k, list(v)) for k, v in self.blocks.iteritems()) |
+ return context |
+ |
+ def _all(meth): |
+ proxy = lambda self: getattr(self.get_all(), meth)() |
+ proxy.__doc__ = getattr(dict, meth).__doc__ |
+ proxy.__name__ = meth |
+ return proxy |
+ |
+ keys = _all('keys') |
+ values = _all('values') |
+ items = _all('items') |
+ |
+ # not available on python 3 |
+ if hasattr(dict, 'iterkeys'): |
+ iterkeys = _all('iterkeys') |
+ itervalues = _all('itervalues') |
+ iteritems = _all('iteritems') |
+ del _all |
+ |
+ def __contains__(self, name): |
+ return name in self.vars or name in self.parent |
+ |
+ def __getitem__(self, key): |
+ """Lookup a variable or raise `KeyError` if the variable is |
+ undefined. |
+ """ |
+ item = self.resolve(key) |
+ if isinstance(item, Undefined): |
+ raise KeyError(key) |
+ return item |
+ |
+ def __repr__(self): |
+ return '<%s %s of %r>' % ( |
+ self.__class__.__name__, |
+ repr(self.get_all()), |
+ self.name |
+ ) |
+ |
+ |
+# register the context as mapping if possible |
+try: |
+ from collections import Mapping |
+ Mapping.register(Context) |
+except ImportError: |
+ pass |
+ |
+ |
+class BlockReference(object): |
+ """One block on a template reference.""" |
+ |
+ def __init__(self, name, context, stack, depth): |
+ self.name = name |
+ self._context = context |
+ self._stack = stack |
+ self._depth = depth |
+ |
+ @property |
+ def super(self): |
+ """Super the block.""" |
+ if self._depth + 1 >= len(self._stack): |
+ return self._context.environment. \ |
+ undefined('there is no parent block called %r.' % |
+ self.name, name='super') |
+ return BlockReference(self.name, self._context, self._stack, |
+ self._depth + 1) |
+ |
+ @internalcode |
+ def __call__(self): |
+ rv = concat(self._stack[self._depth](self._context)) |
+ if self._context.eval_ctx.autoescape: |
+ rv = Markup(rv) |
+ return rv |
+ |
+ |
+class LoopContext(object): |
+ """A loop context for dynamic iteration.""" |
+ |
+ def __init__(self, iterable, recurse=None): |
+ self._iterator = iter(iterable) |
+ self._recurse = recurse |
+ self.index0 = -1 |
+ |
+ # try to get the length of the iterable early. This must be done |
+ # here because there are some broken iterators around where there |
+ # __len__ is the number of iterations left (i'm looking at your |
+ # listreverseiterator!). |
+ try: |
+ self._length = len(iterable) |
+ except (TypeError, AttributeError): |
+ self._length = None |
+ |
+ def cycle(self, *args): |
+ """Cycles among the arguments with the current loop index.""" |
+ if not args: |
+ raise TypeError('no items for cycling given') |
+ return args[self.index0 % len(args)] |
+ |
+ first = property(lambda x: x.index0 == 0) |
+ last = property(lambda x: x.index0 + 1 == x.length) |
+ index = property(lambda x: x.index0 + 1) |
+ revindex = property(lambda x: x.length - x.index0) |
+ revindex0 = property(lambda x: x.length - x.index) |
+ |
+ def __len__(self): |
+ return self.length |
+ |
+ def __iter__(self): |
+ return LoopContextIterator(self) |
+ |
+ @internalcode |
+ def loop(self, iterable): |
+ if self._recurse is None: |
+ raise TypeError('Tried to call non recursive loop. Maybe you ' |
+ "forgot the 'recursive' modifier.") |
+ return self._recurse(iterable, self._recurse) |
+ |
+ # a nifty trick to enhance the error message if someone tried to call |
+ # the the loop without or with too many arguments. |
+ __call__ = loop |
+ del loop |
+ |
+ @property |
+ def length(self): |
+ if self._length is None: |
+ # if was not possible to get the length of the iterator when |
+ # the loop context was created (ie: iterating over a generator) |
+ # we have to convert the iterable into a sequence and use the |
+ # length of that. |
+ iterable = tuple(self._iterator) |
+ self._iterator = iter(iterable) |
+ self._length = len(iterable) + self.index0 + 1 |
+ return self._length |
+ |
+ def __repr__(self): |
+ return '<%s %r/%r>' % ( |
+ self.__class__.__name__, |
+ self.index, |
+ self.length |
+ ) |
+ |
+ |
+class LoopContextIterator(object): |
+ """The iterator for a loop context.""" |
+ __slots__ = ('context',) |
+ |
+ def __init__(self, context): |
+ self.context = context |
+ |
+ def __iter__(self): |
+ return self |
+ |
+ def next(self): |
+ ctx = self.context |
+ ctx.index0 += 1 |
+ return next(ctx._iterator), ctx |
+ |
+ |
+class Macro(object): |
+ """Wraps a macro function.""" |
+ |
+ def __init__(self, environment, func, name, arguments, defaults, |
+ catch_kwargs, catch_varargs, caller): |
+ self._environment = environment |
+ self._func = func |
+ self._argument_count = len(arguments) |
+ self.name = name |
+ self.arguments = arguments |
+ self.defaults = defaults |
+ self.catch_kwargs = catch_kwargs |
+ self.catch_varargs = catch_varargs |
+ self.caller = caller |
+ |
+ @internalcode |
+ def __call__(self, *args, **kwargs): |
+ # try to consume the positional arguments |
+ arguments = list(args[:self._argument_count]) |
+ off = len(arguments) |
+ |
+ # if the number of arguments consumed is not the number of |
+ # arguments expected we start filling in keyword arguments |
+ # and defaults. |
+ if off != self._argument_count: |
+ for idx, name in enumerate(self.arguments[len(arguments):]): |
+ try: |
+ value = kwargs.pop(name) |
+ except KeyError: |
+ try: |
+ value = self.defaults[idx - self._argument_count + off] |
+ except IndexError: |
+ value = self._environment.undefined( |
+ 'parameter %r was not provided' % name, name=name) |
+ arguments.append(value) |
+ |
+ # it's important that the order of these arguments does not change |
+ # if not also changed in the compiler's `function_scoping` method. |
+ # the order is caller, keyword arguments, positional arguments! |
+ if self.caller: |
+ caller = kwargs.pop('caller', None) |
+ if caller is None: |
+ caller = self._environment.undefined('No caller defined', |
+ name='caller') |
+ arguments.append(caller) |
+ if self.catch_kwargs: |
+ arguments.append(kwargs) |
+ elif kwargs: |
+ raise TypeError('macro %r takes no keyword argument %r' % |
+ (self.name, next(iter(kwargs)))) |
+ if self.catch_varargs: |
+ arguments.append(args[self._argument_count:]) |
+ elif len(args) > self._argument_count: |
+ raise TypeError('macro %r takes not more than %d argument(s)' % |
+ (self.name, len(self.arguments))) |
+ return self._func(*arguments) |
+ |
+ def __repr__(self): |
+ return '<%s %s>' % ( |
+ self.__class__.__name__, |
+ self.name is None and 'anonymous' or repr(self.name) |
+ ) |
+ |
+ |
+class Undefined(object): |
+ """The default undefined type. This undefined type can be printed and |
+ iterated over, but every other access will raise an :exc:`UndefinedError`: |
+ |
+ >>> foo = Undefined(name='foo') |
+ >>> str(foo) |
+ '' |
+ >>> not foo |
+ True |
+ >>> foo + 42 |
+ Traceback (most recent call last): |
+ ... |
+ UndefinedError: 'foo' is undefined |
+ """ |
+ __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', |
+ '_undefined_exception') |
+ |
+ def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError): |
+ self._undefined_hint = hint |
+ self._undefined_obj = obj |
+ self._undefined_name = name |
+ self._undefined_exception = exc |
+ |
+ @internalcode |
+ def _fail_with_undefined_error(self, *args, **kwargs): |
+ """Regular callback function for undefined objects that raises an |
+ `UndefinedError` on call. |
+ """ |
+ if self._undefined_hint is None: |
+ if self._undefined_obj is missing: |
+ hint = '%r is undefined' % self._undefined_name |
+ elif not isinstance(self._undefined_name, basestring): |
+ hint = '%s has no element %r' % ( |
+ object_type_repr(self._undefined_obj), |
+ self._undefined_name |
+ ) |
+ else: |
+ hint = '%r has no attribute %r' % ( |
+ object_type_repr(self._undefined_obj), |
+ self._undefined_name |
+ ) |
+ else: |
+ hint = self._undefined_hint |
+ raise self._undefined_exception(hint) |
+ |
+ @internalcode |
+ def __getattr__(self, name): |
+ if name[:2] == '__': |
+ raise AttributeError(name) |
+ return self._fail_with_undefined_error() |
+ |
+ __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ |
+ __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ |
+ __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ |
+ __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ |
+ __float__ = __complex__ = __pow__ = __rpow__ = \ |
+ _fail_with_undefined_error |
+ |
+ def __str__(self): |
+ return unicode(self).encode('utf-8') |
+ |
+ # unicode goes after __str__ because we configured 2to3 to rename |
+ # __unicode__ to __str__. because the 2to3 tree is not designed to |
+ # remove nodes from it, we leave the above __str__ around and let |
+ # it override at runtime. |
+ def __unicode__(self): |
+ return u'' |
+ |
+ def __len__(self): |
+ return 0 |
+ |
+ def __iter__(self): |
+ if 0: |
+ yield None |
+ |
+ def __nonzero__(self): |
+ return False |
+ |
+ def __repr__(self): |
+ return 'Undefined' |
+ |
+ |
+class DebugUndefined(Undefined): |
+ """An undefined that returns the debug info when printed. |
+ |
+ >>> foo = DebugUndefined(name='foo') |
+ >>> str(foo) |
+ '{{ foo }}' |
+ >>> not foo |
+ True |
+ >>> foo + 42 |
+ Traceback (most recent call last): |
+ ... |
+ UndefinedError: 'foo' is undefined |
+ """ |
+ __slots__ = () |
+ |
+ def __unicode__(self): |
+ if self._undefined_hint is None: |
+ if self._undefined_obj is missing: |
+ return u'{{ %s }}' % self._undefined_name |
+ return '{{ no such element: %s[%r] }}' % ( |
+ object_type_repr(self._undefined_obj), |
+ self._undefined_name |
+ ) |
+ return u'{{ undefined value printed: %s }}' % self._undefined_hint |
+ |
+ |
+class StrictUndefined(Undefined): |
+ """An undefined that barks on print and iteration as well as boolean |
+ tests and all kinds of comparisons. In other words: you can do nothing |
+ with it except checking if it's defined using the `defined` test. |
+ |
+ >>> foo = StrictUndefined(name='foo') |
+ >>> str(foo) |
+ Traceback (most recent call last): |
+ ... |
+ UndefinedError: 'foo' is undefined |
+ >>> not foo |
+ Traceback (most recent call last): |
+ ... |
+ UndefinedError: 'foo' is undefined |
+ >>> foo + 42 |
+ Traceback (most recent call last): |
+ ... |
+ UndefinedError: 'foo' is undefined |
+ """ |
+ __slots__ = () |
+ __iter__ = __unicode__ = __str__ = __len__ = __nonzero__ = __eq__ = \ |
+ __ne__ = __bool__ = Undefined._fail_with_undefined_error |
+ |
+ |
+# remove remaining slots attributes, after the metaclass did the magic they |
+# are unneeded and irritating as they contain wrong data for the subclasses. |
+del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__ |