diff genshi/output.py @ 940:303af96ec546 stable-0.6.x

Merge r1163 and r1164 from trunk (fix Markup event caching issue in serializers, issue #429).
author hodgestar
date Sun, 12 Jun 2011 00:45:56 +0000
parents 869ca3cc2f4c
children 26d6ac224d3b
line wrap: on
line diff
--- a/genshi/output.py
+++ b/genshi/output.py
@@ -79,6 +79,30 @@
     return method(**kwargs)
 
 
+def _prepare_cache(use_cache=True):
+    """Prepare a private token serialization cache.
+
+    :param use_cache: boolean indicating whether a real cache should
+                      be used or not. If not, the returned functions
+                      are no-ops.
+
+    :return: emit and get functions, for storing and retrieving
+             serialized values from the cache.
+    """
+    cache = {}
+    if use_cache:
+        def _emit(kind, input, output):
+            cache[kind, input] = output
+            return output
+        _get = cache.get
+    else:
+        def _emit(kind, input, output):
+            return output
+        def _get(key):
+            pass
+    return _emit, _get, cache
+
+
 class DocType(object):
     """Defines a number of commonly used DOCTYPE declarations as constants."""
 
@@ -204,27 +228,23 @@
             self.filters.append(DocTypeInserter(doctype))
         self.cache = cache
 
+    def _prepare_cache(self):
+        return _prepare_cache(self.cache)[:2]
+
     def __call__(self, stream):
         have_decl = have_doctype = False
         in_cdata = False
-
-        cache = {}
-        cache_get = cache.get
-        if self.cache:
-            def _emit(kind, input, output):
-                cache[kind, input] = output
-                return output
-        else:
-            def _emit(kind, input, output):
-                return output
+        _emit, _get = self._prepare_cache()
 
         for filter_ in self.filters:
             stream = filter_(stream)
         for kind, data, pos in stream:
-            cached = cache_get((kind, data))
+            if kind is TEXT and isinstance(data, Markup):
+                yield data
+                continue
+            cached = _get((kind, data))
             if cached is not None:
                 yield cached
-
             elif kind is START or kind is EMPTY:
                 tag, attrib = data
                 buf = ['<', tag]
@@ -323,21 +343,15 @@
         drop_xml_decl = self.drop_xml_decl
         have_decl = have_doctype = False
         in_cdata = False
-
-        cache = {}
-        cache_get = cache.get
-        if self.cache:
-            def _emit(kind, input, output):
-                cache[kind, input] = output
-                return output
-        else:
-            def _emit(kind, input, output):
-                return output
+        _emit, _get = self._prepare_cache()
 
         for filter_ in self.filters:
             stream = filter_(stream)
         for kind, data, pos in stream:
-            cached = cache_get((kind, data))
+            if kind is TEXT and isinstance(data, Markup):
+                yield data
+                continue
+            cached = _get((kind, data))
             if cached is not None:
                 yield cached
 
@@ -454,21 +468,15 @@
         noescape_elems = self._NOESCAPE_ELEMS
         have_doctype = False
         noescape = False
-
-        cache = {}
-        cache_get = cache.get
-        if self.cache:
-            def _emit(kind, input, output):
-                cache[kind, input] = output
-                return output
-        else:
-            def _emit(kind, input, output):
-                return output
+        _emit, _get = self._prepare_cache()
 
         for filter_ in self.filters:
             stream = filter_(stream)
         for kind, data, _ in stream:
-            output = cache_get((kind, data))
+            if kind is TEXT and isinstance(data, Markup):
+                yield data
+                continue
+            output = _get((kind, data))
             if output is not None:
                 yield output
                 if (kind is START or kind is EMPTY) \
@@ -626,18 +634,9 @@
         self.cache = cache
 
     def __call__(self, stream):
-        cache = {}
-        cache_get = cache.get
-        if self.cache:
-            def _emit(kind, input, output, pos):
-                cache[kind, input] = output
-                return kind, output, pos
-        else:
-            def _emit(kind, input, output, pos):
-                return output
-
         prefixes = dict([(v, [k]) for k, v in self.prefixes.items()])
         namespaces = {XML_NAMESPACE.uri: ['xml']}
+        _emit, _get, cache = _prepare_cache(self.cache)
         def _push_ns(prefix, uri):
             namespaces.setdefault(uri, []).append(prefix)
             prefixes.setdefault(prefix, []).append(uri)
@@ -668,7 +667,10 @@
         _gen_prefix = _gen_prefix().next
 
         for kind, data, pos in stream:
-            output = cache_get((kind, data))
+            if kind is TEXT and isinstance(data, Markup):
+                yield kind, data, pos
+                continue
+            output = _get((kind, data))
             if output is not None:
                 yield kind, output, pos
 
@@ -701,7 +703,8 @@
                             attrname = '%s:%s' % (prefix, attrname)
                     new_attrs.append((attrname, value))
 
-                yield _emit(kind, data, (tagname, Attrs(ns_attrs + new_attrs)), pos)
+                data = _emit(kind, data, (tagname, Attrs(ns_attrs + new_attrs)))
+                yield kind, data, pos
                 del ns_attrs[:]
 
             elif kind is END:
@@ -711,7 +714,7 @@
                     prefix = namespaces[tagns][-1]
                     if prefix:
                         tagname = '%s:%s' % (prefix, tagname)
-                yield _emit(kind, data, tagname, pos)
+                yield kind, _emit(kind, data, tagname), pos
 
             elif kind is START_NS:
                 prefix, uri = data
Copyright (C) 2012-2017 Edgewall Software