changeset 668:f102141fe331 trunk

Applied patch from cboos, fixing #168. Thanks!
author athomas
date Tue, 18 Dec 2007 00:03:41 +0000
parents c9a084ffaee6
children c11b89295985
files genshi/filters/tests/transform.py genshi/filters/transform.py genshi/output.py
diffstat 3 files changed, 57 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/genshi/filters/tests/transform.py
+++ b/genshi/filters/tests/transform.py
@@ -19,9 +19,12 @@
 
 def suite():
     from genshi.input import HTML
+    from genshi.core import Markup
+    from genshi.builder import tag
     suite = doctest.DocTestSuite(genshi.filters.transform,
                                  optionflags=doctest.NORMALIZE_WHITESPACE,
-                                 extraglobs={'HTML': HTML})
+                                 extraglobs={'HTML': HTML, 'tag': tag,
+                                     'Markup': Markup})
     return suite
 
 if __name__ == '__main__':
--- a/genshi/filters/transform.py
+++ b/genshi/filters/transform.py
@@ -51,7 +51,7 @@
 import sys
 
 from genshi.builder import Element
-from genshi.core import Stream, Attrs, QName, TEXT, START, END, _ensure
+from genshi.core import Stream, Attrs, QName, TEXT, START, END, _ensure, Markup
 from genshi.path import Path
 
 __all__ = ['Transformer', 'StreamBuffer', 'InjectorTransformation', 'ENTER',
@@ -143,12 +143,15 @@
 
     __slots__ = ['transforms']
 
-    def __init__(self, path='.'):
+    def __init__(self, path=None):
         """Construct a new transformation filter.
 
         :param path: an XPath expression (as string) or a `Path` instance
         """
-        self.transforms = [SelectTransformation(path)]
+        if path is not None:
+            self.transforms = [SelectTransformation(path)]
+        else:
+            self.transforms = []
 
     def __call__(self, stream):
         """Apply the transform filter to the marked stream.
@@ -549,6 +552,10 @@
         ...             '<b>some bold text</b></body></html>')
         >>> print html | Transformer('body').substitute('(?i)some', 'SOME')
         <html><body>SOME text, some more text and <b>SOME bold text</b></body></html>
+        >>> tags = tag.html(tag.body('Some text, some more text and ',
+        ...      Markup('<b>some bold text</b>')))
+        >>> print tags.generate() | Transformer('body').substitute('(?i)some', 'SOME')
+        <html><body>SOME text, some more text and <b>SOME bold text</b></body></html>
 
         :param pattern: A regular expression object or string.
         :param replace: Replacement pattern.
@@ -650,6 +657,9 @@
                 # indicate they are not really part of the stream.
                 yield ATTR, (None, (QName(event[1][0] + '@*'), result), event[2])
                 yield None, event
+            elif isinstance(result, tuple):
+                print result
+                yield None, result
             elif result:
                 yield None, (TEXT, unicode(result), (None, -1, -1))
             else:
@@ -865,7 +875,11 @@
         """
         for mark, (kind, data, pos) in stream:
             if kind is TEXT:
-                data = self.pattern.sub(self.replace, data, self.count)
+                new_data = self.pattern.sub(self.replace, data, self.count)
+                if isinstance(data, Markup):
+                    data = Markup(new_data)
+                else:
+                    data = new_data
             yield mark, (kind, data, pos)
 
 
--- a/genshi/output.py
+++ b/genshi/output.py
@@ -176,21 +176,17 @@
                                  stripped from the output
         :note: Changed in 0.4.2: The  `doctype` parameter can now be a string.
         """
-        self.preamble = []
-        if doctype:
-            if isinstance(doctype, basestring):
-                doctype = DocType.get(doctype)
-            self.preamble.append((DOCTYPE, doctype, (None, -1, -1)))
         self.filters = [EmptyTagFilter()]
         if strip_whitespace:
             self.filters.append(WhitespaceFilter(self._PRESERVE_SPACE))
         self.filters.append(NamespaceFlattener(prefixes=namespace_prefixes))
+        if doctype:
+            self.filters.append(DocTypeInserter(doctype))
 
     def __call__(self, stream):
         have_decl = have_doctype = False
         in_cdata = False
 
-        stream = chain(self.preamble, stream)
         for filter_ in self.filters:
             stream = filter_(stream)
         for kind, data, pos in stream:
@@ -696,3 +692,36 @@
 
                 if kind:
                     yield kind, data, pos
+
+
+class DocTypeInserter(object):
+    """A filter that inserts the DOCTYPE declaration in the correct location,
+    after the XML declaration.
+    """
+    def __init__(self, doctype):
+        """Initialize the filter.
+
+        :param doctype: DOCTYPE as a string or DocType object.
+        """
+        if isinstance(doctype, basestring):
+            doctype = DocType.get(doctype)
+        self.doctype_event = (DOCTYPE, doctype, (None, -1, -1))
+
+    def __call__(self, stream):
+        buffer = []
+        doctype_inserted = False
+        for kind, data, pos in stream:
+            # Buffer whitespace TEXT and XML_DECL
+            if not doctype_inserted:
+                if kind is XML_DECL or (kind is TEXT and not data.strip()):
+                    buffer.append((kind, data, pos))
+                    continue
+
+                for event in buffer:
+                    yield event
+
+                yield self.doctype_event
+
+                doctype_inserted = True
+
+            yield (kind, data, pos)
Copyright (C) 2012-2017 Edgewall Software