X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=scripts%2Fjinja2%2Fnodes.py;h=f9da1da5a295f364635ece012a746d3e3afc8814;hb=ce4fdd2c9f1702dab8def3196226e8f6b66caf82;hp=424c1cd8e36837e7b4a6160e101353facbd571cb;hpb=1a3b7d363474ab544c13093a2f0b578718d37c7a;p=libfirm diff --git a/scripts/jinja2/nodes.py b/scripts/jinja2/nodes.py index 424c1cd8e..f9da1da5a 100644 --- a/scripts/jinja2/nodes.py +++ b/scripts/jinja2/nodes.py @@ -15,7 +15,11 @@ import operator from itertools import chain, izip from collections import deque -from jinja2.utils import Markup +from jinja2.utils import Markup, MethodType, FunctionType + + +#: the types we support for context functions +_context_function_types = (FunctionType, MethodType) _binop_to_func = { @@ -67,6 +71,37 @@ class NodeType(type): return type.__new__(cls, name, bases, d) +class EvalContext(object): + """Holds evaluation time information. Custom attributes can be attached + to it in extensions. + """ + + def __init__(self, environment, template_name=None): + self.environment = environment + if callable(environment.autoescape): + self.autoescape = environment.autoescape(template_name) + else: + self.autoescape = environment.autoescape + self.volatile = False + + def save(self): + return self.__dict__.copy() + + def revert(self, old): + self.__dict__.clear() + self.__dict__.update(old) + + +def get_eval_context(node, ctx): + if ctx is None: + if node.environment is None: + raise RuntimeError('if no eval context is passed, the ' + 'node must have an attached ' + 'environment.') + return EvalContext(node.environment) + return ctx + + class Node(object): """Baseclass for all Jinja2 nodes. There are a number of nodes available of different types. There are three major types: @@ -312,19 +347,16 @@ class Expr(Node): """Baseclass for all expressions.""" abstract = True - def as_const(self): + def as_const(self, eval_ctx=None): """Return the value of the expression as constant or raise - :exc:`Impossible` if this was not possible: + :exc:`Impossible` if this was not possible. - >>> Add(Const(23), Const(42)).as_const() - 65 - >>> Add(Const(23), Name('var', 'load')).as_const() - Traceback (most recent call last): - ... - Impossible + An :class:`EvalContext` can be provided, if none is given + a default context is created which requires the nodes to have + an attached environment. - This requires the `environment` attribute of all nodes to be - set to the environment that created the nodes. + .. versionchanged:: 2.4 + the `eval_ctx` parameter was added. """ raise Impossible() @@ -339,11 +371,16 @@ class BinExpr(Expr): operator = None abstract = True - def as_const(self): + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + # intercepted operators cannot be folded at compile time + if self.environment.sandboxed and \ + self.operator in self.environment.intercepted_binops: + raise Impossible() f = _binop_to_func[self.operator] try: - return f(self.left.as_const(), self.right.as_const()) - except: + return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx)) + except Exception: raise Impossible() @@ -353,11 +390,16 @@ class UnaryExpr(Expr): operator = None abstract = True - def as_const(self): + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + # intercepted operators cannot be folded at compile time + if self.environment.sandboxed and \ + self.operator in self.environment.intercepted_unops: + raise Impossible() f = _uaop_to_func[self.operator] try: - return f(self.node.as_const()) - except: + return f(self.node.as_const(eval_ctx)) + except Exception: raise Impossible() @@ -389,7 +431,7 @@ class Const(Literal): """ fields = ('value',) - def as_const(self): + def as_const(self, eval_ctx=None): return self.value @classmethod @@ -408,8 +450,11 @@ class TemplateData(Literal): """A constant template string.""" fields = ('data',) - def as_const(self): - if self.environment.autoescape: + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + if eval_ctx.autoescape: return Markup(self.data) return self.data @@ -421,8 +466,9 @@ class Tuple(Literal): """ fields = ('items', 'ctx') - def as_const(self): - return tuple(x.as_const() for x in self.items) + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return tuple(x.as_const(eval_ctx) for x in self.items) def can_assign(self): for item in self.items: @@ -435,8 +481,9 @@ class List(Literal): """Any list literal such as ``[1, 2, 3]``""" fields = ('items',) - def as_const(self): - return [x.as_const() for x in self.items] + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return [x.as_const(eval_ctx) for x in self.items] class Dict(Literal): @@ -445,24 +492,27 @@ class Dict(Literal): """ fields = ('items',) - def as_const(self): - return dict(x.as_const() for x in self.items) + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return dict(x.as_const(eval_ctx) for x in self.items) class Pair(Helper): """A key, value pair for dicts.""" fields = ('key', 'value') - def as_const(self): - return self.key.as_const(), self.value.as_const() + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx) class Keyword(Helper): """A key, value pair for keyword arguments where key is a string.""" fields = ('key', 'value') - def as_const(self): - return self.key, self.value.as_const() + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.key, self.value.as_const(eval_ctx) class CondExpr(Expr): @@ -471,15 +521,16 @@ class CondExpr(Expr): """ fields = ('test', 'expr1', 'expr2') - def as_const(self): - if self.test.as_const(): - return self.expr1.as_const() + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if self.test.as_const(eval_ctx): + return self.expr1.as_const(eval_ctx) # if we evaluate to an undefined object, we better do that at runtime if self.expr2 is None: raise Impossible() - return self.expr2.as_const() + return self.expr2.as_const(eval_ctx) class Filter(Expr): @@ -491,8 +542,9 @@ class Filter(Expr): """ fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') - def as_const(self, obj=None): - if self.node is obj is None: + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile or self.node is None: raise Impossible() # we have to be careful here because we call filter_ below. # if this variable would be called filter, 2to3 would wrap the @@ -502,25 +554,26 @@ class Filter(Expr): filter_ = self.environment.filters.get(self.name) if filter_ is None or getattr(filter_, 'contextfilter', False): raise Impossible() - if obj is None: - obj = self.node.as_const() - args = [x.as_const() for x in self.args] - if getattr(filter_, 'environmentfilter', False): + obj = self.node.as_const(eval_ctx) + args = [x.as_const(eval_ctx) for x in self.args] + if getattr(filter_, 'evalcontextfilter', False): + args.insert(0, eval_ctx) + elif getattr(filter_, 'environmentfilter', False): args.insert(0, self.environment) - kwargs = dict(x.as_const() for x in self.kwargs) + kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs) if self.dyn_args is not None: try: - args.extend(self.dyn_args.as_const()) - except: + args.extend(self.dyn_args.as_const(eval_ctx)) + except Exception: raise Impossible() if self.dyn_kwargs is not None: try: - kwargs.update(self.dyn_kwargs.as_const()) - except: + kwargs.update(self.dyn_kwargs.as_const(eval_ctx)) + except Exception: raise Impossible() try: return filter_(obj, *args, **kwargs) - except: + except Exception: raise Impossible() @@ -540,30 +593,36 @@ class Call(Expr): """ fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') - def as_const(self): - obj = self.node.as_const() + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + obj = self.node.as_const(eval_ctx) # don't evaluate context functions - args = [x.as_const() for x in self.args] - if getattr(obj, 'contextfunction', False): - raise Impossible() - elif getattr(obj, 'environmentfunction', False): - args.insert(0, self.environment) + args = [x.as_const(eval_ctx) for x in self.args] + if isinstance(obj, _context_function_types): + if getattr(obj, 'contextfunction', False): + raise Impossible() + elif getattr(obj, 'evalcontextfunction', False): + args.insert(0, eval_ctx) + elif getattr(obj, 'environmentfunction', False): + args.insert(0, self.environment) - kwargs = dict(x.as_const() for x in self.kwargs) + kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs) if self.dyn_args is not None: try: - args.extend(self.dyn_args.as_const()) - except: + args.extend(self.dyn_args.as_const(eval_ctx)) + except Exception: raise Impossible() if self.dyn_kwargs is not None: try: - kwargs.update(self.dyn_kwargs.as_const()) - except: + kwargs.update(self.dyn_kwargs.as_const(eval_ctx)) + except Exception: raise Impossible() try: return obj(*args, **kwargs) - except: + except Exception: raise Impossible() @@ -571,13 +630,14 @@ class Getitem(Expr): """Get an attribute or item from an expression and prefer the item.""" fields = ('node', 'arg', 'ctx') - def as_const(self): + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) if self.ctx != 'load': raise Impossible() try: - return self.environment.getitem(self.node.as_const(), - self.arg.as_const()) - except: + return self.environment.getitem(self.node.as_const(eval_ctx), + self.arg.as_const(eval_ctx)) + except Exception: raise Impossible() def can_assign(self): @@ -590,12 +650,14 @@ class Getattr(Expr): """ fields = ('node', 'attr', 'ctx') - def as_const(self): + def as_const(self, eval_ctx=None): if self.ctx != 'load': raise Impossible() try: - return self.environment.getattr(self.node.as_const(), arg) - except: + eval_ctx = get_eval_context(self, eval_ctx) + return self.environment.getattr(self.node.as_const(eval_ctx), + self.attr) + except Exception: raise Impossible() def can_assign(self): @@ -608,11 +670,12 @@ class Slice(Expr): """ fields = ('start', 'stop', 'step') - def as_const(self): + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) def const(obj): if obj is None: - return obj - return obj.as_const() + return None + return obj.as_const(eval_ctx) return slice(const(self.start), const(self.stop), const(self.step)) @@ -622,8 +685,9 @@ class Concat(Expr): """ fields = ('nodes',) - def as_const(self): - return ''.join(unicode(x.as_const()) for x in self.nodes) + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return ''.join(unicode(x.as_const(eval_ctx)) for x in self.nodes) class Compare(Expr): @@ -632,14 +696,15 @@ class Compare(Expr): """ fields = ('expr', 'ops') - def as_const(self): - result = value = self.expr.as_const() + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + result = value = self.expr.as_const(eval_ctx) try: for op in self.ops: - new_value = op.expr.as_const() + new_value = op.expr.as_const(eval_ctx) result = _cmpop_to_func[op.op](value, new_value) value = new_value - except: + except Exception: raise Impossible() return result @@ -695,16 +760,18 @@ class And(BinExpr): """Short circuited AND.""" operator = 'and' - def as_const(self): - return self.left.as_const() and self.right.as_const() + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx) class Or(BinExpr): """Short circuited OR.""" operator = 'or' - def as_const(self): - return self.left.as_const() or self.right.as_const() + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx) class Not(UnaryExpr): @@ -769,12 +836,40 @@ class MarkSafe(Expr): """Mark the wrapped expression as safe (wrap it as `Markup`).""" fields = ('expr',) - def as_const(self): - return Markup(self.expr.as_const()) + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return Markup(self.expr.as_const(eval_ctx)) + + +class MarkSafeIfAutoescape(Expr): + """Mark the wrapped expression as safe (wrap it as `Markup`) but + only if autoescaping is active. + + .. versionadded:: 2.5 + """ + fields = ('expr',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + expr = self.expr.as_const(eval_ctx) + if eval_ctx.autoescape: + return Markup(expr) + return expr class ContextReference(Expr): - """Returns the current template context.""" + """Returns the current template context. It can be used like a + :class:`Name` node, with a ``'load'`` ctx and will return the + current :class:`~jinja2.runtime.Context` object. + + Here an example that assigns the current template name to a + variable named `foo`:: + + Assign(Name('foo', ctx='store'), + Getattr(ContextReference(), 'name')) + """ class Continue(Stmt): @@ -790,6 +885,25 @@ class Scope(Stmt): fields = ('body',) +class EvalContextModifier(Stmt): + """Modifies the eval context. For each option that should be modified, + a :class:`Keyword` has to be added to the :attr:`options` list. + + Example to change the `autoescape` setting:: + + EvalContextModifier(options=[Keyword('autoescape', Const(True))]) + """ + fields = ('options',) + + +class ScopedEvalContextModifier(EvalContextModifier): + """Modifies the eval context and reverts it later. Works exactly like + :class:`EvalContextModifier` but will only modify the + :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`. + """ + fields = ('body',) + + # make sure nobody creates custom nodes def _failing_new(*args, **kwargs): raise TypeError('can\'t create custom node types')