comparison 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
comparison
equal deleted inserted replaced
928:a1fa93e72d69 940:303af96ec546
75 method = {'xml': XMLSerializer, 75 method = {'xml': XMLSerializer,
76 'xhtml': XHTMLSerializer, 76 'xhtml': XHTMLSerializer,
77 'html': HTMLSerializer, 77 'html': HTMLSerializer,
78 'text': TextSerializer}[method.lower()] 78 'text': TextSerializer}[method.lower()]
79 return method(**kwargs) 79 return method(**kwargs)
80
81
82 def _prepare_cache(use_cache=True):
83 """Prepare a private token serialization cache.
84
85 :param use_cache: boolean indicating whether a real cache should
86 be used or not. If not, the returned functions
87 are no-ops.
88
89 :return: emit and get functions, for storing and retrieving
90 serialized values from the cache.
91 """
92 cache = {}
93 if use_cache:
94 def _emit(kind, input, output):
95 cache[kind, input] = output
96 return output
97 _get = cache.get
98 else:
99 def _emit(kind, input, output):
100 return output
101 def _get(key):
102 pass
103 return _emit, _get, cache
80 104
81 105
82 class DocType(object): 106 class DocType(object):
83 """Defines a number of commonly used DOCTYPE declarations as constants.""" 107 """Defines a number of commonly used DOCTYPE declarations as constants."""
84 108
202 cache=cache)) 226 cache=cache))
203 if doctype: 227 if doctype:
204 self.filters.append(DocTypeInserter(doctype)) 228 self.filters.append(DocTypeInserter(doctype))
205 self.cache = cache 229 self.cache = cache
206 230
231 def _prepare_cache(self):
232 return _prepare_cache(self.cache)[:2]
233
207 def __call__(self, stream): 234 def __call__(self, stream):
208 have_decl = have_doctype = False 235 have_decl = have_doctype = False
209 in_cdata = False 236 in_cdata = False
210 237 _emit, _get = self._prepare_cache()
211 cache = {}
212 cache_get = cache.get
213 if self.cache:
214 def _emit(kind, input, output):
215 cache[kind, input] = output
216 return output
217 else:
218 def _emit(kind, input, output):
219 return output
220 238
221 for filter_ in self.filters: 239 for filter_ in self.filters:
222 stream = filter_(stream) 240 stream = filter_(stream)
223 for kind, data, pos in stream: 241 for kind, data, pos in stream:
224 cached = cache_get((kind, data)) 242 if kind is TEXT and isinstance(data, Markup):
243 yield data
244 continue
245 cached = _get((kind, data))
225 if cached is not None: 246 if cached is not None:
226 yield cached 247 yield cached
227
228 elif kind is START or kind is EMPTY: 248 elif kind is START or kind is EMPTY:
229 tag, attrib = data 249 tag, attrib = data
230 buf = ['<', tag] 250 buf = ['<', tag]
231 for attr, value in attrib: 251 for attr, value in attrib:
232 buf += [' ', attr, '="', escape(value), '"'] 252 buf += [' ', attr, '="', escape(value), '"']
321 boolean_attrs = self._BOOLEAN_ATTRS 341 boolean_attrs = self._BOOLEAN_ATTRS
322 empty_elems = self._EMPTY_ELEMS 342 empty_elems = self._EMPTY_ELEMS
323 drop_xml_decl = self.drop_xml_decl 343 drop_xml_decl = self.drop_xml_decl
324 have_decl = have_doctype = False 344 have_decl = have_doctype = False
325 in_cdata = False 345 in_cdata = False
326 346 _emit, _get = self._prepare_cache()
327 cache = {}
328 cache_get = cache.get
329 if self.cache:
330 def _emit(kind, input, output):
331 cache[kind, input] = output
332 return output
333 else:
334 def _emit(kind, input, output):
335 return output
336 347
337 for filter_ in self.filters: 348 for filter_ in self.filters:
338 stream = filter_(stream) 349 stream = filter_(stream)
339 for kind, data, pos in stream: 350 for kind, data, pos in stream:
340 cached = cache_get((kind, data)) 351 if kind is TEXT and isinstance(data, Markup):
352 yield data
353 continue
354 cached = _get((kind, data))
341 if cached is not None: 355 if cached is not None:
342 yield cached 356 yield cached
343 357
344 elif kind is START or kind is EMPTY: 358 elif kind is START or kind is EMPTY:
345 tag, attrib = data 359 tag, attrib = data
452 boolean_attrs = self._BOOLEAN_ATTRS 466 boolean_attrs = self._BOOLEAN_ATTRS
453 empty_elems = self._EMPTY_ELEMS 467 empty_elems = self._EMPTY_ELEMS
454 noescape_elems = self._NOESCAPE_ELEMS 468 noescape_elems = self._NOESCAPE_ELEMS
455 have_doctype = False 469 have_doctype = False
456 noescape = False 470 noescape = False
457 471 _emit, _get = self._prepare_cache()
458 cache = {}
459 cache_get = cache.get
460 if self.cache:
461 def _emit(kind, input, output):
462 cache[kind, input] = output
463 return output
464 else:
465 def _emit(kind, input, output):
466 return output
467 472
468 for filter_ in self.filters: 473 for filter_ in self.filters:
469 stream = filter_(stream) 474 stream = filter_(stream)
470 for kind, data, _ in stream: 475 for kind, data, _ in stream:
471 output = cache_get((kind, data)) 476 if kind is TEXT and isinstance(data, Markup):
477 yield data
478 continue
479 output = _get((kind, data))
472 if output is not None: 480 if output is not None:
473 yield output 481 yield output
474 if (kind is START or kind is EMPTY) \ 482 if (kind is START or kind is EMPTY) \
475 and data[0] in noescape_elems: 483 and data[0] in noescape_elems:
476 noescape = True 484 noescape = True
624 if prefixes is not None: 632 if prefixes is not None:
625 self.prefixes.update(prefixes) 633 self.prefixes.update(prefixes)
626 self.cache = cache 634 self.cache = cache
627 635
628 def __call__(self, stream): 636 def __call__(self, stream):
629 cache = {}
630 cache_get = cache.get
631 if self.cache:
632 def _emit(kind, input, output, pos):
633 cache[kind, input] = output
634 return kind, output, pos
635 else:
636 def _emit(kind, input, output, pos):
637 return output
638
639 prefixes = dict([(v, [k]) for k, v in self.prefixes.items()]) 637 prefixes = dict([(v, [k]) for k, v in self.prefixes.items()])
640 namespaces = {XML_NAMESPACE.uri: ['xml']} 638 namespaces = {XML_NAMESPACE.uri: ['xml']}
639 _emit, _get, cache = _prepare_cache(self.cache)
641 def _push_ns(prefix, uri): 640 def _push_ns(prefix, uri):
642 namespaces.setdefault(uri, []).append(prefix) 641 namespaces.setdefault(uri, []).append(prefix)
643 prefixes.setdefault(prefix, []).append(uri) 642 prefixes.setdefault(prefix, []).append(uri)
644 cache.clear() 643 cache.clear()
645 def _pop_ns(prefix): 644 def _pop_ns(prefix):
666 val += 1 665 val += 1
667 yield 'ns%d' % val 666 yield 'ns%d' % val
668 _gen_prefix = _gen_prefix().next 667 _gen_prefix = _gen_prefix().next
669 668
670 for kind, data, pos in stream: 669 for kind, data, pos in stream:
671 output = cache_get((kind, data)) 670 if kind is TEXT and isinstance(data, Markup):
671 yield kind, data, pos
672 continue
673 output = _get((kind, data))
672 if output is not None: 674 if output is not None:
673 yield kind, output, pos 675 yield kind, output, pos
674 676
675 elif kind is START or kind is EMPTY: 677 elif kind is START or kind is EMPTY:
676 tag, attrs = data 678 tag, attrs = data
699 prefix = namespaces[attrns][-1] 701 prefix = namespaces[attrns][-1]
700 if prefix: 702 if prefix:
701 attrname = '%s:%s' % (prefix, attrname) 703 attrname = '%s:%s' % (prefix, attrname)
702 new_attrs.append((attrname, value)) 704 new_attrs.append((attrname, value))
703 705
704 yield _emit(kind, data, (tagname, Attrs(ns_attrs + new_attrs)), pos) 706 data = _emit(kind, data, (tagname, Attrs(ns_attrs + new_attrs)))
707 yield kind, data, pos
705 del ns_attrs[:] 708 del ns_attrs[:]
706 709
707 elif kind is END: 710 elif kind is END:
708 tagname = data.localname 711 tagname = data.localname
709 tagns = data.namespace 712 tagns = data.namespace
710 if tagns: 713 if tagns:
711 prefix = namespaces[tagns][-1] 714 prefix = namespaces[tagns][-1]
712 if prefix: 715 if prefix:
713 tagname = '%s:%s' % (prefix, tagname) 716 tagname = '%s:%s' % (prefix, tagname)
714 yield _emit(kind, data, tagname, pos) 717 yield kind, _emit(kind, data, tagname), pos
715 718
716 elif kind is START_NS: 719 elif kind is START_NS:
717 prefix, uri = data 720 prefix, uri = data
718 if uri not in namespaces: 721 if uri not in namespaces:
719 prefix = prefixes.get(uri, [prefix])[-1] 722 prefix = prefixes.get(uri, [prefix])[-1]
Copyright (C) 2012-2017 Edgewall Software