Changeset 816
- Timestamp:
- Mar 28, 2008, 3:14:26 PM (16 years ago)
- Location:
- trunk
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/ChangeLog
r812 r816 66 66 package data, and loading from different loaders depending on the path 67 67 prefix of the requested filename (ticket #182). 68 * Match templates can now be processed without keeping the complete matched 69 content in memory, which could cause excessive memory use on long pages. 70 The buffering can be disabled using the new `buffer` optimization hint on 71 the `<py:match>` directive. 68 72 69 73 -
trunk/doc/xml-templates.txt
r810 r816 359 359 | Attribute | Default | Description | 360 360 +===============+===========+===============================================+ 361 | ``buffer`` | ``true`` | Whether the matched content should be | 362 | | | buffered in memory. Buffering can improve | 363 | | | performance a bit at the cost of needing more | 364 | | | memory during rendering. Buffering is | 365 | | | ''required'' for match templates that contain | 366 | | | more than one invocation of the ``select()`` | 367 | | | function. If there is only one call, and the | 368 | | | matched content can potentially be very long, | 369 | | | consider disabling buffering to avoid | 370 | | | excessive memory use. | 371 +---------------+-----------+-----------------------------------------------+ 361 372 | ``once`` | ``false`` | Whether the engine should stop looking for | 362 373 | | | more matching elements after the first match. | -
trunk/genshi/template/base.py
r755 r816 255 255 256 256 257 def _apply_directives(stream, ctxt, directives):257 def _apply_directives(stream, directives, ctxt, **vars): 258 258 """Apply the given directives to the stream. 259 259 260 260 :param stream: the stream the directives should be applied to 261 :param directives: the list of directives to apply 261 262 :param ctxt: the `Context` 262 :param directives: the list of directives to apply 263 :param vars: additional variables that should be available when Python 264 code is executed 263 265 :return: the stream with the given directives applied 264 266 """ 265 267 if directives: 266 stream = directives[0](iter(stream), ctxt, directives[1:])268 stream = directives[0](iter(stream), directives[1:], ctxt, **vars) 267 269 return stream 270 271 def _eval_expr(expr, ctxt, **vars): 272 """Evaluate the given `Expression` object. 273 274 :param expr: the expression to evaluate 275 :param ctxt: the `Context` 276 :param vars: additional variables that should be available to the 277 expression 278 :return: the result of the evaluation 279 """ 280 if vars: 281 ctxt.push(vars) 282 retval = expr.evaluate(ctxt) 283 if vars: 284 ctxt.pop() 285 return retval 286 287 def _exec_suite(suite, ctxt, **vars): 288 """Execute the given `Suite` object. 289 290 :param suite: the code suite to execute 291 :param ctxt: the `Context` 292 :param vars: additional variables that should be available to the 293 code 294 """ 295 if vars: 296 ctxt.push(vars) 297 ctxt.push({}) 298 suite.execute(_ctxt2dict(ctxt)) 299 if vars: 300 top = ctxt.pop() 301 ctxt.pop() 302 ctxt.frames[0].update(top) 268 303 269 304 … … 427 462 the template to the context data. 428 463 """ 464 vars = {} 429 465 if args: 430 466 assert len(args) == 1 … … 432 468 if ctxt is None: 433 469 ctxt = Context(**kwargs) 470 else: 471 vars = kwargs 434 472 assert isinstance(ctxt, Context) 435 473 else: … … 438 476 stream = self.stream 439 477 for filter_ in self.filters: 440 stream = filter_(iter(stream), ctxt )478 stream = filter_(iter(stream), ctxt, **vars) 441 479 return Stream(stream, self.serializer) 442 480 443 def _eval(self, stream, ctxt ):481 def _eval(self, stream, ctxt, **vars): 444 482 """Internal stream filter that evaluates any expressions in `START` and 445 483 `TEXT` events. … … 461 499 values = [] 462 500 for subkind, subdata, subpos in self._eval(substream, 463 ctxt): 501 ctxt, 502 **vars): 464 503 if subkind is TEXT: 465 504 values.append(subdata) … … 471 510 472 511 elif kind is EXPR: 473 result = data.evaluate(ctxt)512 result = _eval_expr(data, ctxt, **vars) 474 513 if result is not None: 475 514 # First check for a string, otherwise the iterable test … … 483 522 substream = _ensure(result) 484 523 for filter_ in filters: 485 substream = filter_(substream, ctxt )524 substream = filter_(substream, ctxt, **vars) 486 525 for event in substream: 487 526 yield event … … 492 531 yield kind, data, pos 493 532 494 def _exec(self, stream, ctxt ):533 def _exec(self, stream, ctxt, **vars): 495 534 """Internal stream filter that executes Python code blocks.""" 496 535 for event in stream: 497 536 if event[0] is EXEC: 498 event[1].execute(_ctxt2dict(ctxt))537 _exec_suite(event[1], ctxt, **vars) 499 538 else: 500 539 yield event 501 540 502 def _flatten(self, stream, ctxt ):541 def _flatten(self, stream, ctxt, **vars): 503 542 """Internal stream filter that expands `SUB` events in the stream.""" 504 543 for event in stream: … … 507 546 # events to which those directives should be applied 508 547 directives, substream = event[1] 509 substream = _apply_directives(substream, ctxt, directives) 510 for event in self._flatten(substream, ctxt): 548 substream = _apply_directives(substream, directives, ctxt, 549 **vars) 550 for event in self._flatten(substream, ctxt, **vars): 511 551 yield event 512 552 else: 513 553 yield event 514 554 515 def _include(self, stream, ctxt ):555 def _include(self, stream, ctxt, **vars): 516 556 """Internal stream filter that performs inclusion of external 517 557 template files. … … 524 564 if not isinstance(href, basestring): 525 565 parts = [] 526 for subkind, subdata, subpos in self._eval(href, ctxt): 566 for subkind, subdata, subpos in self._eval(href, ctxt, 567 **vars): 527 568 if subkind is TEXT: 528 569 parts.append(subdata) … … 531 572 tmpl = self.loader.load(href, relative_to=event[2][0], 532 573 cls=cls or self.__class__) 533 for event in tmpl.generate(ctxt ):574 for event in tmpl.generate(ctxt, **vars): 534 575 yield event 535 576 except TemplateNotFound: … … 537 578 raise 538 579 for filter_ in self.filters: 539 fallback = filter_(iter(fallback), ctxt )580 fallback = filter_(iter(fallback), ctxt, **vars) 540 581 for event in fallback: 541 582 yield event -
trunk/genshi/template/directives.py
r800 r816 23 23 from genshi.path import Path 24 24 from genshi.template.base import TemplateRuntimeError, TemplateSyntaxError, \ 25 EXPR, _apply_directives, _ctxt2dict 25 EXPR, _apply_directives, _eval_expr, \ 26 _exec_suite 26 27 from genshi.template.eval import Expression, Suite, ExpressionASTTransformer, \ 27 28 _parse … … 89 90 attach = classmethod(attach) 90 91 91 def __call__(self, stream, ctxt, directives):92 def __call__(self, stream, directives, ctxt, **vars): 92 93 """Apply the directive to the given stream. 93 94 94 95 :param stream: the event stream 95 :param ctxt: the context data96 96 :param directives: a list of the remaining directives that should 97 97 process the stream 98 :param ctxt: the context data 99 :param vars: additional variables that should be made available when 100 Python code is executed 98 101 """ 99 102 raise NotImplementedError … … 168 171 __slots__ = [] 169 172 170 def __call__(self, stream, ctxt, directives):173 def __call__(self, stream, directives, ctxt, **vars): 171 174 def _generate(): 172 175 kind, (tag, attrib), pos = stream.next() 173 attrs = self.expr.evaluate(ctxt)176 attrs = _eval_expr(self.expr, ctxt, **vars) 174 177 if attrs: 175 178 if isinstance(attrs, Stream): … … 187 190 yield event 188 191 189 return _apply_directives(_generate(), ctxt, directives)192 return _apply_directives(_generate(), directives, ctxt, **vars) 190 193 191 194 … … 292 295 attach = classmethod(attach) 293 296 294 def __call__(self, stream, ctxt, directives):297 def __call__(self, stream, directives, ctxt, **vars): 295 298 stream = list(stream) 296 299 … … 305 308 val = kwargs.pop(name) 306 309 else: 307 val = self.defaults.get(name).evaluate(ctxt)310 val = _eval_expr(self.defaults.get(name), ctxt, **vars) 308 311 scope[name] = val 309 312 if not self.star_args is None: … … 312 315 scope[self.dstar_args] = kwargs 313 316 ctxt.push(scope) 314 for event in _apply_directives(stream, ctxt, directives):317 for event in _apply_directives(stream, directives, ctxt, **vars): 315 318 yield event 316 319 ctxt.pop() … … 365 368 attach = classmethod(attach) 366 369 367 def __call__(self, stream, ctxt, directives):368 iterable = self.expr.evaluate(ctxt)370 def __call__(self, stream, directives, ctxt, **vars): 371 iterable = _eval_expr(self.expr, ctxt, **vars) 369 372 if iterable is None: 370 373 return … … 376 379 assign(scope, item) 377 380 ctxt.push(scope) 378 for event in _apply_directives(stream, ctxt, directives):381 for event in _apply_directives(stream, directives, ctxt, **vars): 379 382 yield event 380 383 ctxt.pop() … … 406 409 attach = classmethod(attach) 407 410 408 def __call__(self, stream, ctxt, directives): 409 if self.expr.evaluate(ctxt): 410 return _apply_directives(stream, ctxt, directives) 411 def __call__(self, stream, directives, ctxt, **vars): 412 value = _eval_expr(self.expr, ctxt, **vars) 413 if value: 414 return _apply_directives(stream, directives, ctxt, **vars) 411 415 return [] 412 416 … … 441 445 hints = [] 442 446 if type(value) is dict: 447 if value.get('buffer', '').lower() == 'false': 448 hints.append('not_buffered') 443 449 if value.get('once', '').lower() == 'true': 444 450 hints.append('match_once') … … 450 456 attach = classmethod(attach) 451 457 452 def __call__(self, stream, ctxt, directives):458 def __call__(self, stream, directives, ctxt, **vars): 453 459 ctxt._match_templates.append((self.path.test(ignore_context=True), 454 460 self.path, list(stream), self.hints, … … 532 538 __slots__ = [] 533 539 534 def __call__(self, stream, ctxt, directives):540 def __call__(self, stream, directives, ctxt, **vars): 535 541 def _generate(): 536 if self.expr.evaluate(ctxt):542 if _eval_expr(self.expr, ctxt, **vars): 537 543 stream.next() # skip start tag 538 544 previous = stream.next() … … 543 549 for event in stream: 544 550 yield event 545 return _apply_directives(_generate(), ctxt, directives)551 return _apply_directives(_generate(), directives, ctxt, **vars) 546 552 547 553 def attach(cls, template, stream, value, namespaces, pos): … … 601 607 attach = classmethod(attach) 602 608 603 def __call__(self, stream, ctxt, directives):609 def __call__(self, stream, directives, ctxt, **vars): 604 610 info = [False, bool(self.expr), None] 605 611 if self.expr: 606 info[2] = self.expr.evaluate(ctxt)612 info[2] = _eval_expr(self.expr, ctxt, **vars) 607 613 ctxt._choice_stack.append(info) 608 for event in _apply_directives(stream, ctxt, directives):614 for event in _apply_directives(stream, directives, ctxt, **vars): 609 615 yield event 610 616 ctxt._choice_stack.pop() … … 630 636 attach = classmethod(attach) 631 637 632 def __call__(self, stream, ctxt, directives):638 def __call__(self, stream, directives, ctxt, **vars): 633 639 info = ctxt._choice_stack and ctxt._choice_stack[-1] 634 640 if not info: … … 645 651 value = info[2] 646 652 if self.expr: 647 matched = value == self.expr.evaluate(ctxt)653 matched = value == _eval_expr(self.expr, ctxt, **vars) 648 654 else: 649 655 matched = bool(value) 650 656 else: 651 matched = bool( self.expr.evaluate(ctxt))657 matched = bool(_eval_expr(self.expr, ctxt, **vars)) 652 658 info[0] = matched 653 659 if not matched: 654 660 return [] 655 661 656 return _apply_directives(stream, ctxt, directives)662 return _apply_directives(stream, directives, ctxt, **vars) 657 663 658 664 … … 669 675 self.filename = template.filepath 670 676 671 def __call__(self, stream, ctxt, directives):677 def __call__(self, stream, directives, ctxt, **vars): 672 678 info = ctxt._choice_stack and ctxt._choice_stack[-1] 673 679 if not info: … … 679 685 info[0] = True 680 686 681 return _apply_directives(stream, ctxt, directives)687 return _apply_directives(stream, directives, ctxt, **vars) 682 688 683 689 … … 723 729 attach = classmethod(attach) 724 730 725 def __call__(self, stream, ctxt, directives): 726 frame = {} 727 ctxt.push(frame) 728 self.suite.execute(_ctxt2dict(ctxt)) 729 for event in _apply_directives(stream, ctxt, directives): 731 def __call__(self, stream, directives, ctxt, **vars): 732 ctxt.push({}) 733 _exec_suite(self.suite, ctxt, **vars) 734 for event in _apply_directives(stream, directives, ctxt, **vars): 730 735 yield event 731 736 ctxt.pop() -
trunk/genshi/template/markup.py
r810 r816 226 226 return streams[0] 227 227 228 def _match(self, stream, ctxt, match_templates=None ):228 def _match(self, stream, ctxt, match_templates=None, **vars): 229 229 """Internal stream filter that applies any defined match templates 230 230 to the stream. … … 272 272 # Consume and store all events until an end event 273 273 # corresponding to this start event is encountered 274 inner = _strip(stream)275 274 pre_match_templates = match_templates[:idx + 1] 276 275 if 'match_once' not in hints and 'not_recursive' in hints: 277 276 pre_match_templates.pop() 278 inner = self._match(inner, ctxt, pre_match_templates) 279 content = list(self._include(chain([event], inner, tail), 280 ctxt)) 281 282 for test in [mt[0] for mt in match_templates]: 283 test(tail[0], namespaces, ctxt, updateonly=True) 277 inner = _strip(stream) 278 if pre_match_templates: 279 inner = self._match(inner, ctxt, pre_match_templates) 280 content = self._include(chain([event], inner, tail), ctxt) 281 if 'not_buffered' not in hints: 282 content = list(content) 283 284 if tail: 285 for test in [mt[0] for mt in match_templates]: 286 test(tail[0], namespaces, ctxt, updateonly=True) 284 287 285 288 # Make the select() function available in the body of the … … 287 290 def select(path): 288 291 return Stream(content).select(path, namespaces, ctxt) 289 ctxt.push(dict(select=select))292 vars = dict(select=select) 290 293 291 294 # Recursively process the output 292 template = _apply_directives(template, ctxt, directives) 293 remaining = match_templates 294 for event in self._match(self._exec( 295 self._eval(self._flatten(template, ctxt), 296 ctxt), ctxt), ctxt, match_templates[idx + 1:]): 295 template = _apply_directives(template, directives, ctxt, 296 **vars) 297 for event in self._match( 298 self._exec( 299 self._eval( 300 self._flatten(template, ctxt, **vars), 301 ctxt, **vars), 302 ctxt, **vars), 303 ctxt, match_templates[idx + 1:], **vars): 297 304 yield event 298 305 299 ctxt.pop()300 306 break 301 307 -
trunk/genshi/template/tests/markup.py
r772 r816 612 612 </html>""", tmpl.generate().render()) 613 613 614 def test_with_in_match(self): 615 xml = ("""<html xmlns:py="http://genshi.edgewall.org/"> 616 <py:match path="body/p"> 617 <h1>${select('text()')}</h1> 618 ${select('.')} 619 </py:match> 620 <body><p py:with="foo='bar'">${foo}</p></body> 621 </html>""") 622 tmpl = MarkupTemplate(xml, filename='test.html') 623 self.assertEqual("""<html> 624 <body> 625 <h1>bar</h1> 626 <p>bar</p> 627 </body> 628 </html>""", tmpl.generate().render()) 629 614 630 def test_nested_include_matches(self): 615 631 # See ticket #157 -
trunk/genshi/template/text.py
r809 r816 34 34 from genshi.template.eval import Suite 35 35 from genshi.template.directives import * 36 from genshi.template.directives import Directive , _apply_directives36 from genshi.template.directives import Directive 37 37 from genshi.template.interpolation import interpolate 38 38
Note: See TracChangeset
for help on using the changeset viewer.