Mercurial > genshi > mirror
comparison genshi/template/base.py @ 703:af57b12e3dd2 experimental-match-fastpaths
merge in trunk up through r818 - fundamentally changed the way MatchSet works, but actually is more consistent now
author | aflett |
---|---|
date | Mon, 31 Mar 2008 22:47:50 +0000 |
parents | 3d7288f373bd |
children | 422d0607ba85 |
comparison
equal
deleted
inserted
replaced
701:52a597419c0d | 703:af57b12e3dd2 |
---|---|
253 | 253 |
254 def pop(self): | 254 def pop(self): |
255 """Pop the top-most scope from the stack.""" | 255 """Pop the top-most scope from the stack.""" |
256 | 256 |
257 | 257 |
258 def _apply_directives(stream, ctxt, directives): | 258 def _apply_directives(stream, directives, ctxt, **vars): |
259 """Apply the given directives to the stream. | 259 """Apply the given directives to the stream. |
260 | 260 |
261 :param stream: the stream the directives should be applied to | 261 :param stream: the stream the directives should be applied to |
262 :param directives: the list of directives to apply | |
262 :param ctxt: the `Context` | 263 :param ctxt: the `Context` |
263 :param directives: the list of directives to apply | 264 :param vars: additional variables that should be available when Python |
265 code is executed | |
264 :return: the stream with the given directives applied | 266 :return: the stream with the given directives applied |
265 """ | 267 """ |
266 if directives: | 268 if directives: |
267 stream = directives[0](iter(stream), ctxt, directives[1:]) | 269 stream = directives[0](iter(stream), directives[1:], ctxt, **vars) |
268 return stream | 270 return stream |
271 | |
272 def _eval_expr(expr, ctxt, **vars): | |
273 """Evaluate the given `Expression` object. | |
274 | |
275 :param expr: the expression to evaluate | |
276 :param ctxt: the `Context` | |
277 :param vars: additional variables that should be available to the | |
278 expression | |
279 :return: the result of the evaluation | |
280 """ | |
281 if vars: | |
282 ctxt.push(vars) | |
283 retval = expr.evaluate(ctxt) | |
284 if vars: | |
285 ctxt.pop() | |
286 return retval | |
287 | |
288 def _exec_suite(suite, ctxt, **vars): | |
289 """Execute the given `Suite` object. | |
290 | |
291 :param suite: the code suite to execute | |
292 :param ctxt: the `Context` | |
293 :param vars: additional variables that should be available to the | |
294 code | |
295 """ | |
296 if vars: | |
297 ctxt.push(vars) | |
298 ctxt.push({}) | |
299 suite.execute(_ctxt2dict(ctxt)) | |
300 if vars: | |
301 top = ctxt.pop() | |
302 ctxt.pop() | |
303 ctxt.frames[0].update(top) | |
269 | 304 |
270 | 305 |
271 class TemplateMeta(type): | 306 class TemplateMeta(type): |
272 """Meta class for templates.""" | 307 """Meta class for templates.""" |
273 | 308 |
425 This calling style is used for internal processing. | 460 This calling style is used for internal processing. |
426 | 461 |
427 :return: a markup event stream representing the result of applying | 462 :return: a markup event stream representing the result of applying |
428 the template to the context data. | 463 the template to the context data. |
429 """ | 464 """ |
465 vars = {} | |
430 if args: | 466 if args: |
431 assert len(args) == 1 | 467 assert len(args) == 1 |
432 ctxt = args[0] | 468 ctxt = args[0] |
433 if ctxt is None: | 469 if ctxt is None: |
434 ctxt = Context(**kwargs) | 470 ctxt = Context(**kwargs) |
471 else: | |
472 vars = kwargs | |
435 assert isinstance(ctxt, Context) | 473 assert isinstance(ctxt, Context) |
436 else: | 474 else: |
437 ctxt = Context(**kwargs) | 475 ctxt = Context(**kwargs) |
438 | 476 |
439 stream = self.stream | 477 stream = self.stream |
440 for filter_ in self.filters: | 478 for filter_ in self.filters: |
441 stream = filter_(iter(stream), ctxt) | 479 stream = filter_(iter(stream), ctxt, **vars) |
442 return Stream(stream, self.serializer) | 480 return Stream(stream, self.serializer) |
443 | 481 |
444 def _eval(self, stream, ctxt): | 482 def _eval(self, stream, ctxt, **vars): |
445 """Internal stream filter that evaluates any expressions in `START` and | 483 """Internal stream filter that evaluates any expressions in `START` and |
446 `TEXT` events. | 484 `TEXT` events. |
447 """ | 485 """ |
448 filters = (self._flatten, self._eval) | 486 filters = (self._flatten, self._eval) |
449 number_conv = self._number_conv | 487 number_conv = self._number_conv |
459 if isinstance(substream, basestring): | 497 if isinstance(substream, basestring): |
460 value = substream | 498 value = substream |
461 else: | 499 else: |
462 values = [] | 500 values = [] |
463 for subkind, subdata, subpos in self._eval(substream, | 501 for subkind, subdata, subpos in self._eval(substream, |
464 ctxt): | 502 ctxt, |
503 **vars): | |
465 if subkind is TEXT: | 504 if subkind is TEXT: |
466 values.append(subdata) | 505 values.append(subdata) |
467 value = [x for x in values if x is not None] | 506 value = [x for x in values if x is not None] |
468 if not value: | 507 if not value: |
469 continue | 508 continue |
470 new_attrs.append((name, u''.join(value))) | 509 new_attrs.append((name, u''.join(value))) |
471 yield kind, (tag, Attrs(new_attrs)), pos | 510 yield kind, (tag, Attrs(new_attrs)), pos |
472 | 511 |
473 elif kind is EXPR: | 512 elif kind is EXPR: |
474 result = data.evaluate(ctxt) | 513 result = _eval_expr(data, ctxt, **vars) |
475 if result is not None: | 514 if result is not None: |
476 # First check for a string, otherwise the iterable test | 515 # First check for a string, otherwise the iterable test |
477 # below succeeds, and the string will be chopped up into | 516 # below succeeds, and the string will be chopped up into |
478 # individual characters | 517 # individual characters |
479 if isinstance(result, basestring): | 518 if isinstance(result, basestring): |
481 elif isinstance(result, (int, float, long)): | 520 elif isinstance(result, (int, float, long)): |
482 yield TEXT, number_conv(result), pos | 521 yield TEXT, number_conv(result), pos |
483 elif hasattr(result, '__iter__'): | 522 elif hasattr(result, '__iter__'): |
484 substream = _ensure(result) | 523 substream = _ensure(result) |
485 for filter_ in filters: | 524 for filter_ in filters: |
486 substream = filter_(substream, ctxt) | 525 substream = filter_(substream, ctxt, **vars) |
487 for event in substream: | 526 for event in substream: |
488 yield event | 527 yield event |
489 else: | 528 else: |
490 yield TEXT, unicode(result), pos | 529 yield TEXT, unicode(result), pos |
491 | 530 |
492 else: | 531 else: |
493 yield kind, data, pos | 532 yield kind, data, pos |
494 | 533 |
495 def _exec(self, stream, ctxt): | 534 def _exec(self, stream, ctxt, **vars): |
496 """Internal stream filter that executes Python code blocks.""" | 535 """Internal stream filter that executes Python code blocks.""" |
497 for event in stream: | 536 for event in stream: |
498 if event[0] is EXEC: | 537 if event[0] is EXEC: |
499 event[1].execute(_ctxt2dict(ctxt)) | 538 _exec_suite(event[1], ctxt, **vars) |
500 else: | 539 else: |
501 yield event | 540 yield event |
502 | 541 |
503 def _flatten(self, stream, ctxt): | 542 def _flatten(self, stream, ctxt, **vars): |
504 """Internal stream filter that expands `SUB` events in the stream.""" | 543 """Internal stream filter that expands `SUB` events in the stream.""" |
505 for event in stream: | 544 for event in stream: |
506 if event[0] is SUB: | 545 if event[0] is SUB: |
507 # This event is a list of directives and a list of nested | 546 # This event is a list of directives and a list of nested |
508 # events to which those directives should be applied | 547 # events to which those directives should be applied |
509 directives, substream = event[1] | 548 directives, substream = event[1] |
510 substream = _apply_directives(substream, ctxt, directives) | 549 substream = _apply_directives(substream, directives, ctxt, |
511 for event in self._flatten(substream, ctxt): | 550 **vars) |
551 for event in self._flatten(substream, ctxt, **vars): | |
512 yield event | 552 yield event |
513 else: | 553 else: |
514 yield event | 554 yield event |
515 | 555 |
516 def _include(self, stream, ctxt): | 556 def _include(self, stream, ctxt, **vars): |
517 """Internal stream filter that performs inclusion of external | 557 """Internal stream filter that performs inclusion of external |
518 template files. | 558 template files. |
519 """ | 559 """ |
520 from genshi.template.loader import TemplateNotFound | 560 from genshi.template.loader import TemplateNotFound |
521 | 561 |
522 for event in stream: | 562 for event in stream: |
523 if event[0] is INCLUDE: | 563 if event[0] is INCLUDE: |
524 href, cls, fallback = event[1] | 564 href, cls, fallback = event[1] |
525 if not isinstance(href, basestring): | 565 if not isinstance(href, basestring): |
526 parts = [] | 566 parts = [] |
527 for subkind, subdata, subpos in self._eval(href, ctxt): | 567 for subkind, subdata, subpos in self._eval(href, ctxt, |
568 **vars): | |
528 if subkind is TEXT: | 569 if subkind is TEXT: |
529 parts.append(subdata) | 570 parts.append(subdata) |
530 href = u''.join([x for x in parts if x is not None]) | 571 href = u''.join([x for x in parts if x is not None]) |
531 try: | 572 try: |
532 tmpl = self.loader.load(href, relative_to=event[2][0], | 573 tmpl = self.loader.load(href, relative_to=event[2][0], |
533 cls=cls or self.__class__) | 574 cls=cls or self.__class__) |
534 for event in tmpl.generate(ctxt): | 575 for event in tmpl.generate(ctxt, **vars): |
535 yield event | 576 yield event |
536 except TemplateNotFound: | 577 except TemplateNotFound: |
537 if fallback is None: | 578 if fallback is None: |
538 raise | 579 raise |
539 for filter_ in self.filters: | 580 for filter_ in self.filters: |
540 fallback = filter_(iter(fallback), ctxt) | 581 fallback = filter_(iter(fallback), ctxt, **vars) |
541 for event in fallback: | 582 for event in fallback: |
542 yield event | 583 yield event |
543 else: | 584 else: |
544 yield event | 585 yield event |
545 | 586 |