comparison genshi/filters/transform.py @ 720:acf7c5ee36e7 experimental-newctxt

newctxt branch: Merged revisions [678:835] via svnmerge from [source:trunk].
author cmlenz
date Fri, 11 Apr 2008 08:42:11 +0000
parents 4d486f15c986
children
comparison
equal deleted inserted replaced
567:837786a584d5 720:acf7c5ee36e7
41 </body> 41 </body>
42 </html> 42 </html>
43 43
44 The ``Transformer`` support a large number of useful transformations out of the 44 The ``Transformer`` support a large number of useful transformations out of the
45 box, but custom transformations can be added easily. 45 box, but custom transformations can be added easily.
46
47 :since: version 0.5
46 """ 48 """
47 49
48 import re 50 import re
49 import sys 51 import sys
50 52
51 from genshi.builder import Element 53 from genshi.builder import Element
52 from genshi.core import Stream, Attrs, QName, TEXT, START, END, _ensure 54 from genshi.core import Stream, Attrs, QName, TEXT, START, END, _ensure, Markup
53 from genshi.path import Path 55 from genshi.path import Path
54 56
55 __all__ = ['Transformer', 'StreamBuffer', 'InjectorTransformation', 'ENTER', 57 __all__ = ['Transformer', 'StreamBuffer', 'InjectorTransformation', 'ENTER',
56 'EXIT', 'INSIDE', 'OUTSIDE'] 58 'EXIT', 'INSIDE', 'OUTSIDE']
57 59
139 class="emphasis">body</em> text.</body></html> 141 class="emphasis">body</em> text.</body></html>
140 """ 142 """
141 143
142 __slots__ = ['transforms'] 144 __slots__ = ['transforms']
143 145
144 def __init__(self, path=None): 146 def __init__(self, path='.'):
145 """Construct a new transformation filter. 147 """Construct a new transformation filter.
146 148
147 :param path: an XPath expression (as string) or a `Path` instance 149 :param path: an XPath expression (as string) or a `Path` instance
148 """ 150 """
149 self.transforms = [] 151 self.transforms = [SelectTransformation(path)]
150 if path:
151 self.transforms.append(SelectTransformation(path))
152 152
153 def __call__(self, stream): 153 def __call__(self, stream):
154 """Apply the transform filter to the marked stream. 154 """Apply the transform filter to the marked stream.
155 155
156 :param stream: the marked event stream to filter 156 :param stream: the marked event stream to filter
158 :rtype: `Stream` 158 :rtype: `Stream`
159 """ 159 """
160 transforms = self._mark(stream) 160 transforms = self._mark(stream)
161 for link in self.transforms: 161 for link in self.transforms:
162 transforms = link(transforms) 162 transforms = link(transforms)
163 return Stream(self._unmark(transforms)) 163 return Stream(self._unmark(transforms),
164 serializer=getattr(stream, 'serializer', None))
164 165
165 def apply(self, function): 166 def apply(self, function):
166 """Apply a transformation to the stream. 167 """Apply a transformation to the stream.
167 168
168 Transformations can be chained, similar to stream filters. Any callable 169 Transformations can be chained, similar to stream filters. Any callable
546 547
547 >>> html = HTML('<html><body>Some text, some more text and ' 548 >>> html = HTML('<html><body>Some text, some more text and '
548 ... '<b>some bold text</b></body></html>') 549 ... '<b>some bold text</b></body></html>')
549 >>> print html | Transformer('body').substitute('(?i)some', 'SOME') 550 >>> print html | Transformer('body').substitute('(?i)some', 'SOME')
550 <html><body>SOME text, some more text and <b>SOME bold text</b></body></html> 551 <html><body>SOME text, some more text and <b>SOME bold text</b></body></html>
552 >>> tags = tag.html(tag.body('Some text, some more text and ',
553 ... Markup('<b>some bold text</b>')))
554 >>> print tags.generate() | Transformer('body').substitute('(?i)some', 'SOME')
555 <html><body>SOME text, some more text and <b>SOME bold text</b></body></html>
551 556
552 :param pattern: A regular expression object or string. 557 :param pattern: A regular expression object or string.
553 :param replace: Replacement pattern. 558 :param replace: Replacement pattern.
554 :param count: Number of replacements to make in each text fragment. 559 :param count: Number of replacements to make in each text fragment.
555 :rtype: `Transformer` 560 :rtype: `Transformer`
556 """ 561 """
557 return self.apply(SubstituteTransformation(pattern, replace, count)) 562 return self.apply(SubstituteTransformation(pattern, replace, count))
563
564 def rename(self, name):
565 """Rename matching elements.
566
567 >>> html = HTML('<html><body>Some text, some more text and '
568 ... '<b>some bold text</b></body></html>')
569 >>> print html | Transformer('body/b').rename('strong')
570 <html><body>Some text, some more text and <strong>some bold text</strong></body></html>
571 """
572 return self.apply(RenameTransformation(name))
558 573
559 def trace(self, prefix='', fileobj=None): 574 def trace(self, prefix='', fileobj=None):
560 """Print events as they pass through the transform. 575 """Print events as they pass through the transform.
561 576
562 >>> html = HTML('<body>Some <em>test</em> text</body>') 577 >>> html = HTML('<body>Some <em>test</em> text</body>')
730 element = list(self.element.generate()) 745 element = list(self.element.generate())
731 for prefix in element[:-1]: 746 for prefix in element[:-1]:
732 yield None, prefix 747 yield None, prefix
733 yield mark, event 748 yield mark, event
734 while True: 749 while True:
735 mark, event = stream.next() 750 try:
751 mark, event = stream.next()
752 except StopIteration:
753 yield None, element[-1]
736 if not mark: 754 if not mark:
737 break 755 break
738 yield mark, event 756 yield mark, event
739 yield None, element[-1] 757 yield None, element[-1]
740 yield mark, event 758 yield mark, event
789 queue = [] 807 queue = []
790 for mark, event in stream: 808 for mark, event in stream:
791 if mark: 809 if mark:
792 queue.append(event) 810 queue.append(event)
793 else: 811 else:
794 for event in flush(queue): 812 for queue_event in flush(queue):
795 yield event 813 yield queue_event
796 yield None, event 814 yield None, event
797 for event in flush(queue): 815 for event in flush(queue):
798 yield event 816 yield event
799 817
800 818
849 867
850 :param stream: The marked event stream to filter 868 :param stream: The marked event stream to filter
851 """ 869 """
852 for mark, (kind, data, pos) in stream: 870 for mark, (kind, data, pos) in stream:
853 if kind is TEXT: 871 if kind is TEXT:
854 data = self.pattern.sub(self.replace, data, self.count) 872 new_data = self.pattern.sub(self.replace, data, self.count)
873 if isinstance(data, Markup):
874 data = Markup(new_data)
875 else:
876 data = new_data
877 yield mark, (kind, data, pos)
878
879
880 class RenameTransformation(object):
881 """Rename matching elements."""
882 def __init__(self, name):
883 """Create the transform.
884
885 :param name: New element name.
886 """
887 self.name = QName(name)
888
889 def __call__(self, stream):
890 """Apply the transform filter to the marked stream.
891
892 :param stream: The marked event stream to filter
893 """
894 for mark, (kind, data, pos) in stream:
895 if mark is ENTER:
896 data = self.name, data[1]
897 elif mark is EXIT:
898 data = self.name
855 yield mark, (kind, data, pos) 899 yield mark, (kind, data, pos)
856 900
857 901
858 class InjectorTransformation(object): 902 class InjectorTransformation(object):
859 """Abstract base class for transformations that inject content into a 903 """Abstract base class for transformations that inject content into a
910 """Apply the transform filter to the marked stream. 954 """Apply the transform filter to the marked stream.
911 955
912 :param stream: The marked event stream to filter 956 :param stream: The marked event stream to filter
913 """ 957 """
914 for mark, event in stream: 958 for mark, event in stream:
915 if mark in (ENTER, OUTSIDE): 959 if mark is not None:
916 for subevent in self._inject(): 960 for subevent in self._inject():
917 yield subevent 961 yield subevent
962 yield mark, event
963 while True:
964 mark, event = stream.next()
965 if not mark:
966 break
967 yield mark, event
918 yield mark, event 968 yield mark, event
919 969
920 970
921 class AfterTransformation(InjectorTransformation): 971 class AfterTransformation(InjectorTransformation):
922 """Insert content after selection.""" 972 """Insert content after selection."""
928 """ 978 """
929 for mark, event in stream: 979 for mark, event in stream:
930 yield mark, event 980 yield mark, event
931 if mark: 981 if mark:
932 while True: 982 while True:
933 mark, event = stream.next() 983 try:
984 mark, event = stream.next()
985 except StopIteration:
986 break
934 if not mark: 987 if not mark:
935 break 988 break
936 yield mark, event 989 yield mark, event
937 for subevent in self._inject(): 990 for subevent in self._inject():
938 yield subevent 991 yield subevent
Copyright (C) 2012-2017 Edgewall Software