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