1# -*- coding: utf-8 -*- 2""" 3 jinja2.runtime 4 ~~~~~~~~~~~~~~ 5 6 Runtime helpers. 7 8 :copyright: (c) 2010 by the Jinja Team. 9 :license: BSD. 10""" 11from itertools import chain 12from jinja2.nodes import EvalContext, _context_function_types 13from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \ 14 internalcode, object_type_repr 15from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \ 16 TemplateNotFound 17from jinja2._compat import next, imap, text_type, iteritems, \ 18 implements_iterator, implements_to_string, string_types, PY2 19 20 21# these variables are exported to the template runtime 22__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup', 23 'TemplateRuntimeError', 'missing', 'concat', 'escape', 24 'markup_join', 'unicode_join', 'to_string', 'identity', 25 'TemplateNotFound'] 26 27#: the name of the function that is used to convert something into 28#: a string. We can just use the text type here. 29to_string = text_type 30 31#: the identity function. Useful for certain things in the environment 32identity = lambda x: x 33 34_last_iteration = object() 35 36 37def markup_join(seq): 38 """Concatenation that escapes if necessary and converts to unicode.""" 39 buf = [] 40 iterator = imap(soft_unicode, seq) 41 for arg in iterator: 42 buf.append(arg) 43 if hasattr(arg, '__html__'): 44 return Markup(u'').join(chain(buf, iterator)) 45 return concat(buf) 46 47 48def unicode_join(seq): 49 """Simple args to unicode conversion and concatenation.""" 50 return concat(imap(text_type, seq)) 51 52 53def new_context(environment, template_name, blocks, vars=None, 54 shared=None, globals=None, locals=None): 55 """Internal helper to for context creation.""" 56 if vars is None: 57 vars = {} 58 if shared: 59 parent = vars 60 else: 61 parent = dict(globals or (), **vars) 62 if locals: 63 # if the parent is shared a copy should be created because 64 # we don't want to modify the dict passed 65 if shared: 66 parent = dict(parent) 67 for key, value in iteritems(locals): 68 if key[:2] == 'l_' and value is not missing: 69 parent[key[2:]] = value 70 return Context(environment, parent, template_name, blocks) 71 72 73class TemplateReference(object): 74 """The `self` in templates.""" 75 76 def __init__(self, context): 77 self.__context = context 78 79 def __getitem__(self, name): 80 blocks = self.__context.blocks[name] 81 return BlockReference(name, self.__context, blocks, 0) 82 83 def __repr__(self): 84 return '<%s %r>' % ( 85 self.__class__.__name__, 86 self.__context.name 87 ) 88 89 90class Context(object): 91 """The template context holds the variables of a template. It stores the 92 values passed to the template and also the names the template exports. 93 Creating instances is neither supported nor useful as it's created 94 automatically at various stages of the template evaluation and should not 95 be created by hand. 96 97 The context is immutable. Modifications on :attr:`parent` **must not** 98 happen and modifications on :attr:`vars` are allowed from generated 99 template code only. Template filters and global functions marked as 100 :func:`contextfunction`\s get the active context passed as first argument 101 and are allowed to access the context read-only. 102 103 The template context supports read only dict operations (`get`, 104 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, 105 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` 106 method that doesn't fail with a `KeyError` but returns an 107 :class:`Undefined` object for missing variables. 108 """ 109 __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars', 110 'name', 'blocks', '__weakref__') 111 112 def __init__(self, environment, parent, name, blocks): 113 self.parent = parent 114 self.vars = {} 115 self.environment = environment 116 self.eval_ctx = EvalContext(self.environment, name) 117 self.exported_vars = set() 118 self.name = name 119 120 # create the initial mapping of blocks. Whenever template inheritance 121 # takes place the runtime will update this mapping with the new blocks 122 # from the template. 123 self.blocks = dict((k, [v]) for k, v in iteritems(blocks)) 124 125 def super(self, name, current): 126 """Render a parent block.""" 127 try: 128 blocks = self.blocks[name] 129 index = blocks.index(current) + 1 130 blocks[index] 131 except LookupError: 132 return self.environment.undefined('there is no parent block ' 133 'called %r.' % name, 134 name='super') 135 return BlockReference(name, self, blocks, index) 136 137 def get(self, key, default=None): 138 """Returns an item from the template context, if it doesn't exist 139 `default` is returned. 140 """ 141 try: 142 return self[key] 143 except KeyError: 144 return default 145 146 def resolve(self, key): 147 """Looks up a variable like `__getitem__` or `get` but returns an 148 :class:`Undefined` object with the name of the name looked up. 149 """ 150 if key in self.vars: 151 return self.vars[key] 152 if key in self.parent: 153 return self.parent[key] 154 return self.environment.undefined(name=key) 155 156 def get_exported(self): 157 """Get a new dict with the exported variables.""" 158 return dict((k, self.vars[k]) for k in self.exported_vars) 159 160 def get_all(self): 161 """Return a copy of the complete context as dict including the 162 exported variables. 163 """ 164 return dict(self.parent, **self.vars) 165 166 @internalcode 167 def call(__self, __obj, *args, **kwargs): 168 """Call the callable with the arguments and keyword arguments 169 provided but inject the active context or environment as first 170 argument if the callable is a :func:`contextfunction` or 171 :func:`environmentfunction`. 172 """ 173 if __debug__: 174 __traceback_hide__ = True 175 176 # Allow callable classes to take a context 177 fn = __obj.__call__ 178 for fn_type in ('contextfunction', 179 'evalcontextfunction', 180 'environmentfunction'): 181 if hasattr(fn, fn_type): 182 __obj = fn 183 break 184 185 if isinstance(__obj, _context_function_types): 186 if getattr(__obj, 'contextfunction', 0): 187 args = (__self,) + args 188 elif getattr(__obj, 'evalcontextfunction', 0): 189 args = (__self.eval_ctx,) + args 190 elif getattr(__obj, 'environmentfunction', 0): 191 args = (__self.environment,) + args 192 try: 193 return __obj(*args, **kwargs) 194 except StopIteration: 195 return __self.environment.undefined('value was undefined because ' 196 'a callable raised a ' 197 'StopIteration exception') 198 199 def derived(self, locals=None): 200 """Internal helper function to create a derived context.""" 201 context = new_context(self.environment, self.name, {}, 202 self.parent, True, None, locals) 203 context.vars.update(self.vars) 204 context.eval_ctx = self.eval_ctx 205 context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks)) 206 return context 207 208 def _all(meth): 209 proxy = lambda self: getattr(self.get_all(), meth)() 210 proxy.__doc__ = getattr(dict, meth).__doc__ 211 proxy.__name__ = meth 212 return proxy 213 214 keys = _all('keys') 215 values = _all('values') 216 items = _all('items') 217 218 # not available on python 3 219 if PY2: 220 iterkeys = _all('iterkeys') 221 itervalues = _all('itervalues') 222 iteritems = _all('iteritems') 223 del _all 224 225 def __contains__(self, name): 226 return name in self.vars or name in self.parent 227 228 def __getitem__(self, key): 229 """Lookup a variable or raise `KeyError` if the variable is 230 undefined. 231 """ 232 item = self.resolve(key) 233 if isinstance(item, Undefined): 234 raise KeyError(key) 235 return item 236 237 def __repr__(self): 238 return '<%s %s of %r>' % ( 239 self.__class__.__name__, 240 repr(self.get_all()), 241 self.name 242 ) 243 244 245# register the context as mapping if possible 246try: 247 from collections import Mapping 248 Mapping.register(Context) 249except ImportError: 250 pass 251 252 253class BlockReference(object): 254 """One block on a template reference.""" 255 256 def __init__(self, name, context, stack, depth): 257 self.name = name 258 self._context = context 259 self._stack = stack 260 self._depth = depth 261 262 @property 263 def super(self): 264 """Super the block.""" 265 if self._depth + 1 >= len(self._stack): 266 return self._context.environment. \ 267 undefined('there is no parent block called %r.' % 268 self.name, name='super') 269 return BlockReference(self.name, self._context, self._stack, 270 self._depth + 1) 271 272 @internalcode 273 def __call__(self): 274 rv = concat(self._stack[self._depth](self._context)) 275 if self._context.eval_ctx.autoescape: 276 rv = Markup(rv) 277 return rv 278 279 280class LoopContext(object): 281 """A loop context for dynamic iteration.""" 282 283 def __init__(self, iterable, recurse=None, depth0=0): 284 self._iterator = iter(iterable) 285 self._recurse = recurse 286 self._after = self._safe_next() 287 self.index0 = -1 288 self.depth0 = depth0 289 290 # try to get the length of the iterable early. This must be done 291 # here because there are some broken iterators around where there 292 # __len__ is the number of iterations left (i'm looking at your 293 # listreverseiterator!). 294 try: 295 self._length = len(iterable) 296 except (TypeError, AttributeError): 297 self._length = None 298 299 def cycle(self, *args): 300 """Cycles among the arguments with the current loop index.""" 301 if not args: 302 raise TypeError('no items for cycling given') 303 return args[self.index0 % len(args)] 304 305 first = property(lambda x: x.index0 == 0) 306 last = property(lambda x: x._after is _last_iteration) 307 index = property(lambda x: x.index0 + 1) 308 revindex = property(lambda x: x.length - x.index0) 309 revindex0 = property(lambda x: x.length - x.index) 310 depth = property(lambda x: x.depth0 + 1) 311 312 def __len__(self): 313 return self.length 314 315 def __iter__(self): 316 return LoopContextIterator(self) 317 318 def _safe_next(self): 319 try: 320 return next(self._iterator) 321 except StopIteration: 322 return _last_iteration 323 324 @internalcode 325 def loop(self, iterable): 326 if self._recurse is None: 327 raise TypeError('Tried to call non recursive loop. Maybe you ' 328 "forgot the 'recursive' modifier.") 329 return self._recurse(iterable, self._recurse, self.depth0 + 1) 330 331 # a nifty trick to enhance the error message if someone tried to call 332 # the the loop without or with too many arguments. 333 __call__ = loop 334 del loop 335 336 @property 337 def length(self): 338 if self._length is None: 339 # if was not possible to get the length of the iterator when 340 # the loop context was created (ie: iterating over a generator) 341 # we have to convert the iterable into a sequence and use the 342 # length of that. 343 iterable = tuple(self._iterator) 344 self._iterator = iter(iterable) 345 self._length = len(iterable) + self.index0 + 1 346 return self._length 347 348 def __repr__(self): 349 return '<%s %r/%r>' % ( 350 self.__class__.__name__, 351 self.index, 352 self.length 353 ) 354 355 356@implements_iterator 357class LoopContextIterator(object): 358 """The iterator for a loop context.""" 359 __slots__ = ('context',) 360 361 def __init__(self, context): 362 self.context = context 363 364 def __iter__(self): 365 return self 366 367 def __next__(self): 368 ctx = self.context 369 ctx.index0 += 1 370 if ctx._after is _last_iteration: 371 raise StopIteration() 372 next_elem = ctx._after 373 ctx._after = ctx._safe_next() 374 return next_elem, ctx 375 376 377class Macro(object): 378 """Wraps a macro function.""" 379 380 def __init__(self, environment, func, name, arguments, defaults, 381 catch_kwargs, catch_varargs, caller): 382 self._environment = environment 383 self._func = func 384 self._argument_count = len(arguments) 385 self.name = name 386 self.arguments = arguments 387 self.defaults = defaults 388 self.catch_kwargs = catch_kwargs 389 self.catch_varargs = catch_varargs 390 self.caller = caller 391 392 @internalcode 393 def __call__(self, *args, **kwargs): 394 # try to consume the positional arguments 395 arguments = list(args[:self._argument_count]) 396 off = len(arguments) 397 398 # if the number of arguments consumed is not the number of 399 # arguments expected we start filling in keyword arguments 400 # and defaults. 401 if off != self._argument_count: 402 for idx, name in enumerate(self.arguments[len(arguments):]): 403 try: 404 value = kwargs.pop(name) 405 except KeyError: 406 try: 407 value = self.defaults[idx - self._argument_count + off] 408 except IndexError: 409 value = self._environment.undefined( 410 'parameter %r was not provided' % name, name=name) 411 arguments.append(value) 412 413 # it's important that the order of these arguments does not change 414 # if not also changed in the compiler's `function_scoping` method. 415 # the order is caller, keyword arguments, positional arguments! 416 if self.caller: 417 caller = kwargs.pop('caller', None) 418 if caller is None: 419 caller = self._environment.undefined('No caller defined', 420 name='caller') 421 arguments.append(caller) 422 if self.catch_kwargs: 423 arguments.append(kwargs) 424 elif kwargs: 425 raise TypeError('macro %r takes no keyword argument %r' % 426 (self.name, next(iter(kwargs)))) 427 if self.catch_varargs: 428 arguments.append(args[self._argument_count:]) 429 elif len(args) > self._argument_count: 430 raise TypeError('macro %r takes not more than %d argument(s)' % 431 (self.name, len(self.arguments))) 432 return self._func(*arguments) 433 434 def __repr__(self): 435 return '<%s %s>' % ( 436 self.__class__.__name__, 437 self.name is None and 'anonymous' or repr(self.name) 438 ) 439 440 441@implements_to_string 442class Undefined(object): 443 """The default undefined type. This undefined type can be printed and 444 iterated over, but every other access will raise an :exc:`UndefinedError`: 445 446 >>> foo = Undefined(name='foo') 447 >>> str(foo) 448 '' 449 >>> not foo 450 True 451 >>> foo + 42 452 Traceback (most recent call last): 453 ... 454 UndefinedError: 'foo' is undefined 455 """ 456 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', 457 '_undefined_exception') 458 459 def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError): 460 self._undefined_hint = hint 461 self._undefined_obj = obj 462 self._undefined_name = name 463 self._undefined_exception = exc 464 465 @internalcode 466 def _fail_with_undefined_error(self, *args, **kwargs): 467 """Regular callback function for undefined objects that raises an 468 `UndefinedError` on call. 469 """ 470 if self._undefined_hint is None: 471 if self._undefined_obj is missing: 472 hint = '%r is undefined' % self._undefined_name 473 elif not isinstance(self._undefined_name, string_types): 474 hint = '%s has no element %r' % ( 475 object_type_repr(self._undefined_obj), 476 self._undefined_name 477 ) 478 else: 479 hint = '%r has no attribute %r' % ( 480 object_type_repr(self._undefined_obj), 481 self._undefined_name 482 ) 483 else: 484 hint = self._undefined_hint 485 raise self._undefined_exception(hint) 486 487 @internalcode 488 def __getattr__(self, name): 489 if name[:2] == '__': 490 raise AttributeError(name) 491 return self._fail_with_undefined_error() 492 493 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ 494 __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ 495 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ 496 __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ 497 __float__ = __complex__ = __pow__ = __rpow__ = \ 498 _fail_with_undefined_error 499 500 def __eq__(self, other): 501 return type(self) is type(other) 502 503 def __ne__(self, other): 504 return not self.__eq__(other) 505 506 def __hash__(self): 507 return id(type(self)) 508 509 def __str__(self): 510 return u'' 511 512 def __len__(self): 513 return 0 514 515 def __iter__(self): 516 if 0: 517 yield None 518 519 def __nonzero__(self): 520 return False 521 522 def __repr__(self): 523 return 'Undefined' 524 525 526@implements_to_string 527class DebugUndefined(Undefined): 528 """An undefined that returns the debug info when printed. 529 530 >>> foo = DebugUndefined(name='foo') 531 >>> str(foo) 532 '{{ foo }}' 533 >>> not foo 534 True 535 >>> foo + 42 536 Traceback (most recent call last): 537 ... 538 UndefinedError: 'foo' is undefined 539 """ 540 __slots__ = () 541 542 def __str__(self): 543 if self._undefined_hint is None: 544 if self._undefined_obj is missing: 545 return u'{{ %s }}' % self._undefined_name 546 return '{{ no such element: %s[%r] }}' % ( 547 object_type_repr(self._undefined_obj), 548 self._undefined_name 549 ) 550 return u'{{ undefined value printed: %s }}' % self._undefined_hint 551 552 553@implements_to_string 554class StrictUndefined(Undefined): 555 """An undefined that barks on print and iteration as well as boolean 556 tests and all kinds of comparisons. In other words: you can do nothing 557 with it except checking if it's defined using the `defined` test. 558 559 >>> foo = StrictUndefined(name='foo') 560 >>> str(foo) 561 Traceback (most recent call last): 562 ... 563 UndefinedError: 'foo' is undefined 564 >>> not foo 565 Traceback (most recent call last): 566 ... 567 UndefinedError: 'foo' is undefined 568 >>> foo + 42 569 Traceback (most recent call last): 570 ... 571 UndefinedError: 'foo' is undefined 572 """ 573 __slots__ = () 574 __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \ 575 __ne__ = __bool__ = __hash__ = \ 576 Undefined._fail_with_undefined_error 577 578 579# remove remaining slots attributes, after the metaclass did the magic they 580# are unneeded and irritating as they contain wrong data for the subclasses. 581del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__ 582