comparison genshi/template/base.py @ 902:09cc3627654c experimental-inline

Sync `experimental/inline` branch with [source:trunk@1126].
author cmlenz
date Fri, 23 Apr 2010 21:08:26 +0000
parents de82830f8816
children
comparison
equal deleted inserted replaced
830:de82830f8816 902:09cc3627654c
1 # -*- coding: utf-8 -*- 1 # -*- coding: utf-8 -*-
2 # 2 #
3 # Copyright (C) 2006-2008 Edgewall Software 3 # Copyright (C) 2006-2010 Edgewall Software
4 # All rights reserved. 4 # All rights reserved.
5 # 5 #
6 # This software is licensed as described in the file COPYING, which 6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms 7 # you should have received as part of this distribution. The terms
8 # are also available at http://genshi.edgewall.org/wiki/License. 8 # are also available at http://genshi.edgewall.org/wiki/License.
316 :since: version 0.6 316 :since: version 0.6
317 """ 317 """
318 __metaclass__ = DirectiveFactoryMeta 318 __metaclass__ = DirectiveFactoryMeta
319 319
320 directives = [] 320 directives = []
321 """A list of `(name, cls)` tuples that define the set of directives 321 """A list of ``(name, cls)`` tuples that define the set of directives
322 provided by this factory. 322 provided by this factory.
323 """ 323 """
324
325 def compare_directives(self):
326 """Return a function that takes two directive classes and compares
327 them to determine their relative ordering.
328 """
329 def _get_index(cls):
330 if cls in self._dir_order:
331 return self._dir_order.index(cls)
332 return 0
333 return lambda a, b: cmp(_get_index(a[0]), _get_index(b[0]))
334 324
335 def get_directive(self, name): 325 def get_directive(self, name):
336 """Return the directive class for the given name. 326 """Return the directive class for the given name.
337 327
338 :param name: the directive name as used in the template 328 :param name: the directive name as used in the template
339 :return: the directive class 329 :return: the directive class
340 :see: `Directive` 330 :see: `Directive`
341 """ 331 """
342 return self._dir_by_name.get(name) 332 return self._dir_by_name.get(name)
333
334 def get_directive_index(self, dir_cls):
335 """Return a key for the given directive class that should be used to
336 sort it among other directives on the same `SUB` event.
337
338 The default implementation simply returns the index of the directive in
339 the `directives` list.
340
341 :param dir_cls: the directive class
342 :return: the sort key
343 """
344 if dir_cls in self._dir_order:
345 return self._dir_order.index(dir_cls)
346 return len(self._dir_order)
343 347
344 348
345 class Template(DirectiveFactory): 349 class Template(DirectiveFactory):
346 """Abstract template base class. 350 """Abstract template base class.
347 351
390 self.filename = filename 394 self.filename = filename
391 self.loader = loader 395 self.loader = loader
392 self.lookup = lookup 396 self.lookup = lookup
393 self.allow_exec = allow_exec 397 self.allow_exec = allow_exec
394 self._init_filters() 398 self._init_filters()
399 self._init_loader()
395 self._module = None 400 self._module = None
396 self._prepared = False 401 self._prepared = False
397 402
398 if isinstance(source, basestring): 403 if isinstance(source, basestring):
399 source = StringIO(source) 404 source = StringIO(source)
412 def __setstate__(self, state): 417 def __setstate__(self, state):
413 self.__dict__ = state 418 self.__dict__ = state
414 self._init_filters() 419 self._init_filters()
415 420
416 def __repr__(self): 421 def __repr__(self):
417 return '<%s "%s">' % (self.__class__.__name__, self.filename) 422 return '<%s "%s">' % (type(self).__name__, self.filename)
418 423
419 def _init_filters(self): 424 def _init_filters(self):
420 self.filters = [self._flatten] 425 self.filters = [self._flatten, self._include]
421 if self.loader: 426
422 self.filters.append(self._include) 427 def _init_loader(self):
428 if self.loader is None:
429 from genshi.template.loader import TemplateLoader
430 if self.filename:
431 if self.filepath != self.filename:
432 basedir = os.path.normpath(self.filepath)[:-len(
433 os.path.normpath(self.filename))
434 ]
435 else:
436 basedir = os.path.dirname(self.filename)
437 else:
438 basedir = '.'
439 self.loader = TemplateLoader([os.path.abspath(basedir)])
423 440
424 @property 441 @property
425 def stream(self): 442 def stream(self):
426 if not self._prepared: 443 if not self._prepared:
427 self._stream = list(self._prepare(self._stream)) 444 self._stream = list(self._prepare(self._stream))
451 468
452 for kind, data, pos in stream: 469 for kind, data, pos in stream:
453 if kind is SUB: 470 if kind is SUB:
454 directives = [] 471 directives = []
455 substream = data[1] 472 substream = data[1]
456 for cls, value, namespaces, pos in data[0]: 473 for _, cls, value, namespaces, pos in sorted(data[0]):
457 directive, substream = cls.attach(self, substream, value, 474 directive, substream = cls.attach(self, substream, value,
458 namespaces, pos) 475 namespaces, pos)
459 if directive: 476 if directive:
460 directives.append(directive) 477 directives.append(directive)
461 substream = self._prepare(substream) 478 substream = self._prepare(substream)
540 557
541 return Stream(stream, self.serializer) 558 return Stream(stream, self.serializer)
542 559
543 def _flatten(self, stream, ctxt, **vars): 560 def _flatten(self, stream, ctxt, **vars):
544 number_conv = self._number_conv 561 number_conv = self._number_conv
545 562 stack = []
546 for kind, data, pos in stream: 563 push = stack.append
547 564 pop = stack.pop
548 if kind is START and data[1]: 565 stream = iter(stream)
549 # Attributes may still contain expressions in start tags at 566
550 # this point, so do some evaluation 567 while 1:
551 tag, attrs = data 568 for kind, data, pos in stream:
552 new_attrs = [] 569
553 for name, value in attrs: 570 if kind is START and data[1]:
554 if type(value) is list: # this is an interpolated string 571 # Attributes may still contain expressions in start tags at
555 values = [event[1] 572 # this point, so do some evaluation
556 for event in self._flatten(value, ctxt, **vars) 573 tag, attrs = data
557 if event[0] is TEXT and event[1] is not None 574 new_attrs = []
558 ] 575 for name, value in attrs:
559 if not values: 576 if type(value) is list: # this is an interpolated string
560 continue 577 values = [event[1]
561 value = u''.join(values) 578 for event in self._flatten(value, ctxt, **vars)
562 new_attrs.append((name, value)) 579 if event[0] is TEXT and event[1] is not None
563 yield kind, (tag, Attrs(new_attrs)), pos 580 ]
564 581 if not values:
565 elif kind is EXPR: 582 continue
566 result = _eval_expr(data, ctxt, vars) 583 value = ''.join(values)
567 if result is not None: 584 new_attrs.append((name, value))
568 # First check for a string, otherwise the iterable test 585 yield kind, (tag, Attrs(new_attrs)), pos
569 # below succeeds, and the string will be chopped up into 586
570 # individual characters 587 elif kind is EXPR:
571 if isinstance(result, basestring): 588 result = _eval_expr(data, ctxt, vars)
572 yield TEXT, result, pos 589 if result is not None:
573 elif isinstance(result, (int, float, long)): 590 # First check for a string, otherwise the iterable test
574 yield TEXT, number_conv(result), pos 591 # below succeeds, and the string will be chopped up into
575 elif hasattr(result, '__iter__'): 592 # individual characters
576 for event in self._flatten(_ensure(result), ctxt, 593 if isinstance(result, basestring):
577 **vars): 594 yield TEXT, result, pos
578 yield event 595 elif isinstance(result, (int, float, long)):
579 else: 596 yield TEXT, number_conv(result), pos
580 yield TEXT, unicode(result), pos 597 elif hasattr(result, '__iter__'):
581 598 push(stream)
582 elif kind is EXEC: 599 stream = _ensure(result)
583 _exec_suite(data, ctxt, vars) 600 break
584 601 else:
585 elif kind is SUB: 602 yield TEXT, unicode(result), pos
586 # This event is a list of directives and a list of nested 603
587 # events to which those directives should be applied 604 elif kind is SUB:
588 substream = _apply_directives(data[1], data[0], ctxt, vars) 605 # This event is a list of directives and a list of nested
589 for event in self._flatten(substream, ctxt, **vars): 606 # events to which those directives should be applied
590 yield event 607 push(stream)
608 stream = _apply_directives(data[1], data[0], ctxt, vars)
609 break
610
611 elif kind is EXEC:
612 _exec_suite(data, ctxt, vars)
613
614 else:
615 yield kind, data, pos
591 616
592 else: 617 else:
593 yield kind, data, pos 618 if not stack:
619 break
620 stream = pop()
594 621
595 def _include(self, stream, ctxt, **vars): 622 def _include(self, stream, ctxt, **vars):
596 """Internal stream filter that performs inclusion of external 623 """Internal stream filter that performs inclusion of external
597 template files. 624 template files.
598 """ 625 """
605 parts = [] 632 parts = []
606 for subkind, subdata, subpos in self._flatten(href, ctxt, 633 for subkind, subdata, subpos in self._flatten(href, ctxt,
607 **vars): 634 **vars):
608 if subkind is TEXT: 635 if subkind is TEXT:
609 parts.append(subdata) 636 parts.append(subdata)
610 href = u''.join([x for x in parts if x is not None]) 637 href = ''.join([x for x in parts if x is not None])
611 try: 638 try:
612 tmpl = self.loader.load(href, relative_to=event[2][0], 639 tmpl = self.loader.load(href, relative_to=event[2][0],
613 cls=cls or self.__class__) 640 cls=cls or self.__class__)
614 for event in tmpl.generate(ctxt, **vars): 641 for event in tmpl.generate(ctxt, **vars):
615 yield event 642 yield event
Copyright (C) 2012-2017 Edgewall Software