def test_nested_bound_namespaces(self): stream = Stream([ (Stream.START_NS, ('x', 'http://example.org/'), (None, -1, -1)), (Stream.START, (QName('http://example.org/}div'), Attrs()), (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.START_NS, ('x', 'http://example.org/'), (None, -1, -1)), (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), (Stream.END_NS, 'x', (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.START_NS, ('x', 'http://example.org/'), (None, -1, -1)), (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), (Stream.END_NS, 'x', (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.END, QName('http://example.org/}div'), (None, -1, -1)), (Stream.END_NS, 'x', (None, -1, -1)) ]) output = stream.render(XMLSerializer) self.assertEqual( """<x:div xmlns:x="http://example.org/"> <x:p/> <x:p/> </x:div>""", output)
def test_nested_default_namespaces(self): stream = Stream([ (Stream.START_NS, ('', 'http://example.org/'), (None, -1, -1)), (Stream.START, (QName('http://example.org/}div'), Attrs()), (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.START_NS, ('', 'http://example.org/'), (None, -1, -1)), (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), (Stream.END_NS, '', (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.START_NS, ('', 'http://example.org/'), (None, -1, -1)), (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), (Stream.END_NS, '', (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.END, QName('http://example.org/}div'), (None, -1, -1)), (Stream.END_NS, '', (None, -1, -1)) ]) output = stream.render(XMLSerializer, encoding=None) self.assertEqual( """<div xmlns="http://example.org/"> <p/> <p/> </div>""", output)
def test_cache_markup(self): loc = (None, -1, -1) stream = Stream([(Stream.START, (QName('foo'), Attrs()), loc), (Stream.TEXT, u'…', loc), (Stream.END, QName('foo'), loc), (Stream.START, (QName('bar'), Attrs()), loc), (Stream.TEXT, Markup('…'), loc), (Stream.END, QName('bar'), loc)]) output = stream.render(XMLSerializer, encoding=None, strip_whitespace=False) self.assertEqual('<foo>&hellip;</foo><bar>…</bar>', output)
def test_convert_ElementTree_to_markup_stream(self): tree = ElementTree.fromstring( u'<div class="test_div">text<span>some more text</span></div>' ) events = list(ET(tree)) self.assertEqual(6, len(events)) self.assertEqual( (Stream.START, (QName("div"), Attrs([(QName("class"), "test_div")]))), events[0][:2], ) self.assertEqual((Stream.TEXT, "text"), events[1][:2]) self.assertEqual((Stream.START, (QName("span"), Attrs())), events[2][:2]) self.assertEqual((Stream.TEXT, "some more text"), events[3][:2]) self.assertEqual((Stream.END, QName("span")), events[4][:2]) self.assertEqual((Stream.END, QName("div")), events[5][:2])
def _format_fragment(self, text, fragment): START, TEXT, END, Attrs = self.START, self.TEXT, self.END, self.Attrs qname = self.qname output = [] index = fragment.startchar lastmatched = False for t in fragment.matches: if t.startchar > index: if lastmatched: output.append((END, qname, (None, -1, -1))) lastmatched = False self._add_text(text[index:t.startchar], output) ttxt = text[t.startchar:t.endchar] if not lastmatched: output.append((START, (qname, Attrs()), (None, -1, -1))) lastmatched = True output.append((TEXT, ttxt, (None, -1, -1))) index = t.endchar if lastmatched: output.append((END, qname, (None, -1, -1))) return output
def __call__(self, stream): """Apply the filter to the given stream. :deprecated: the ability to behave as a Genshi filter will be removed in Trac 1.5.1. :param stream: the markup event stream to filter """ waiting_for = None for kind, data, pos in stream: if kind is START: if waiting_for: continue tag, attrs = data if not self.is_safe_elem(tag, attrs): waiting_for = tag continue new_attrs = self.sanitize_attrs(tag, dict(attrs)) yield kind, (tag, Attrs(new_attrs.iteritems())), pos elif kind is END: tag = data if waiting_for: if waiting_for == tag: waiting_for = None else: yield kind, data, pos elif kind is not COMMENT: if not waiting_for: yield kind, data, pos
def post_process_request(self, req, template, data, content_type): """post process request filter""" # update the nav item links root = req.href() rootproducts = req.href.products() pid = req.args.get('productid') if pid: for navkey, section in req.chrome['nav'].iteritems(): for item in section: try: href = item['label'].attrib.get('href') except AttributeError: continue if href.startswith(rootproducts): continue if href.startswith(root): tail = href[len(root):] if tail not in self.NAVITEM_DO_NOT_TRANSFORM: attrs = [ attr for attr in item['label'].attrib if attr[0] != 'href' ] newhref = req.href.products(pid, tail) item['label'].attrib = Attrs([(QName('href'), newhref)] + attrs) return (template, data, content_type)
def test_attr_selection_with_namespace(self): xml = XML('<root xmlns:ns1="http://example.com">' '<foo ns1:bar="abc"></foo>' '</root>') path = Path('foo/@ns1:bar') result = path.select(xml, namespaces={'ns1': 'http://example.com'}) self.assertEqual(list(result), [Attrs([(QName('http://example.com}bar'), 'abc')])])
def test_pickle(self): attrs = Attrs([("attr1", "foo"), ("attr2", "bar")]) buf = BytesIO() pickle.dump(attrs, buf, 2) buf.seek(0) unpickled = pickle.load(buf) self.assertEquals("Attrs([('attr1', 'foo'), ('attr2', 'bar')])", repr(unpickled))
def test_map_element(self): self.assertEqual( self._map('foo'), [(QName('foo'), Attrs([(QName('name'), 'foo'), (QName('size'), '100')])), 'FOO', QName('foo')] )
def handle_starttag(self, tag, attrib): fixed_attrib = [(QName(name), name if value is None else value) for name, value in attrib] self._enqueue(START, (QName(tag), Attrs(fixed_attrib))) if tag in self._EMPTY_ELEMS: self._enqueue(END, QName(tag)) else: self._open_tags.append(tag)
def mark_text(self, pos, text, tag): ws, text = self.cut_leading_space(text) tag = QName(tag) if ws: self.append(TEXT, ws, pos) self.append(START, (tag, Attrs()), pos) self.append(TEXT, text, pos) self.append(END, tag, pos)
def test_duplicate_attributes(self): link = tag.a(href='#1', href_='#1')('Bar') events = list(link.generate()) self.assertEqual( (Stream.START, ('a', Attrs([('href', "#1")])), (None, -1, -1)), events[0]) self.assertEqual((Stream.TEXT, 'Bar', (None, -1, -1)), events[1]) self.assertEqual((Stream.END, 'a', (None, -1, -1)), events[2])
def test_link(self): link = tag.a(href='#', accesskey=None)('Bar') events = list(link.generate()) self.assertEqual( (Stream.START, ('a', Attrs([('href', "#")])), (None, -1, -1)), events[0]) self.assertEqual((Stream.TEXT, 'Bar', (None, -1, -1)), events[1]) self.assertEqual((Stream.END, 'a', (None, -1, -1)), events[2])
def test_duplicate_attributes(self): link = tag.a(href='#1', href_='#2')('Bar') bits = iter(link.generate()) self.assertEqual( (Stream.START, ('a', Attrs([('href', "#1")])), (None, -1, -1)), bits.next()) self.assertEqual((Stream.TEXT, u'Bar', (None, -1, -1)), bits.next()) self.assertEqual((Stream.END, 'a', (None, -1, -1)), bits.next())
def _kwargs_to_attrs(kwargs): attrs = [] names = set() for name, value in kwargs.items(): name = name.rstrip('_').replace('_', '-') if value is not None and name not in names: attrs.append((QName(name), unicode(value))) names.add(name) return Attrs(attrs)
def __call__(self, *args, **kwargs): """Append any positional arguments as child nodes, and keyword arguments as attributes. :see: `Fragment.append` """ self.attrib |= Attrs(_kwargs_to_attrs(kwargs)) Fragment.__call__(self, *args) return self
def test_link(self): link = tag.a(href='#', title='Foo', accesskey=None)('Bar') bits = iter(link.generate()) self.assertEqual( (Stream.START, ('a', Attrs([('href', "#"), ('title', "Foo")])), (None, -1, -1)), bits.next()) self.assertEqual((Stream.TEXT, u'Bar', (None, -1, -1)), bits.next()) self.assertEqual((Stream.END, 'a', (None, -1, -1)), bits.next())
def _generate(): for c, text in self._chunk(tokens): if c: attrs = Attrs([(class_, c)]) yield START, (span, attrs), pos yield TEXT, text, pos yield END, span, pos else: yield TEXT, text, pos
def test_nonstring_attributes(self): """ Verify that if an attribute value is given as an int (or some other non-string type), it is coverted to a string when the stream is generated. """ events = list(tag.foo(id=3)) self.assertEqual( (Stream.START, ('foo', Attrs([('id', '3')])), (None, -1, -1)), events[0])
def to_genshi(walker): """Convert a tree to a genshi tree :arg walker: the treewalker to use to walk the tree to convert it :returns: generator of genshi nodes """ text = [] for token in walker: type = token["type"] if type in ("Characters", "SpaceCharacters"): text.append(token["data"]) elif text: yield TEXT, "".join(text), (None, -1, -1) text = [] if type in ("StartTag", "EmptyTag"): if token["namespace"]: name = "{%s}%s" % (token["namespace"], token["name"]) else: name = token["name"] attrs = Attrs( [ (QName("{%s}%s" % attr if attr[0] is not None else attr[1]), value) for attr, value in token["data"].items() ] ) yield (START, (QName(name), attrs), (None, -1, -1)) if type == "EmptyTag": type = "EndTag" if type == "EndTag": if token["namespace"]: name = "{%s}%s" % (token["namespace"], token["name"]) else: name = token["name"] yield END, QName(name), (None, -1, -1) elif type == "Comment": yield COMMENT, token["data"], (None, -1, -1) elif type == "Doctype": yield DOCTYPE, (token["name"], token["publicId"], token["systemId"]), ( None, -1, -1, ) else: pass # FIXME: What to do? if text: yield TEXT, "".join(text), (None, -1, -1)
def test_empty_text_in_span(self): """ http://trac.edgewall.org/ticket/4336 """ ns = Namespace('http://www.w3.org/1999/xhtml') input = [(START, (ns.span, Attrs([])), (None, -1, -1)), (TEXT, "", (None, -1, -1)), (END, ns.span, (None, -1, -1)), ] lines = list(_group_lines(input)) self.assertEqual(len(lines), 0)
def handle_starttag(self, tag, attrib): fixed_attrib = [] for name, value in attrib: # Fixup minimized attributes if value is None: value = name fixed_attrib.append((QName(name), stripentities(value))) self._enqueue(START, (QName(tag), Attrs(fixed_attrib))) if tag in self._EMPTY_ELEMS: self._enqueue(END, QName(tag)) else: self._open_tags.append(tag)
def test_out_of_order_tags2(self): text = '<span class="baz"><b><i>Foobar</span></b></i>' events = list(HTMLParser(StringIO(text))) self.assertEqual(7, len(events)) self.assertEqual((Stream.START, ('span', Attrs([('class', 'baz')]))), events[0][:2]) self.assertEqual((Stream.START, ('b', ())), events[1][:2]) self.assertEqual((Stream.START, ('i', ())), events[2][:2]) self.assertEqual((Stream.TEXT, 'Foobar'), events[3][:2]) self.assertEqual((Stream.END, 'i'), events[4][:2]) self.assertEqual((Stream.END, 'b'), events[5][:2]) self.assertEqual((Stream.END, 'span'), events[6][:2])
def test_multiple_bound_namespaces(self): stream = Stream([ (Stream.START, (QName('div'), Attrs()), (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.START_NS, ('x', 'http://example.org/'), (None, -1, -1)), (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), (Stream.END_NS, 'x', (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.START_NS, ('x', 'http://example.org/'), (None, -1, -1)), (Stream.START, (QName('http://example.org/}p'), Attrs()), (None, -1, -1)), (Stream.END, QName('http://example.org/}p'), (None, -1, -1)), (Stream.END_NS, 'x', (None, -1, -1)), (Stream.TEXT, '\n ', (None, -1, -1)), (Stream.END, QName('div'), (None, -1, -1)), ]) output = stream.render(XMLSerializer, encoding=None) self.assertEqual("""<div> <x:p xmlns:x="http://example.org/"/> <x:p xmlns:x="http://example.org/"/> </div>""", output)
def _eval(self, stream, ctxt, **vars): """Internal stream filter that evaluates any expressions in `START` and `TEXT` events. """ filters = (self._flatten, self._eval) number_conv = self._number_conv for kind, data, pos in stream: if kind is START and data[1]: # Attributes may still contain expressions in start tags at # this point, so do some evaluation tag, attrs = data new_attrs = [] for name, substream in attrs: if isinstance(substream, basestring): value = substream else: values = [] for subkind, subdata, subpos in self._eval(substream, ctxt, **vars): if subkind is TEXT: values.append(subdata) value = [x for x in values if x is not None] if not value: continue new_attrs.append((name, u''.join(value))) yield kind, (tag, Attrs(new_attrs)), pos elif kind is EXPR: result = _eval_expr(data, ctxt, **vars) if result is not None: # First check for a string, otherwise the iterable test # below succeeds, and the string will be chopped up into # individual characters if isinstance(result, basestring): yield TEXT, result, pos elif isinstance(result, (int, float, long)): yield TEXT, number_conv(result), pos elif hasattr(result, '__iter__'): substream = _ensure(result) for filter_ in filters: substream = filter_(substream, ctxt, **vars) for event in substream: yield event else: yield TEXT, unicode(result), pos else: yield kind, data, pos
def handle_starttag(self, tag, attrib): fixed_attrib = [] for name, value in attrib: # Fixup minimized attributes if value is None: value = str(name) elif not isinstance(value, str): value = value.decode(self.encoding, 'replace') fixed_attrib.append((QName(name), stripentities(value))) self._enqueue(START, (QName(tag), Attrs(fixed_attrib))) if tag in self._EMPTY_ELEMS: self._enqueue(END, QName(tag)) else: self._open_tags.append(tag)
def GenshiAdapter(tree): text = None for token in treewalkers.getTreeWalker('dom')(tree): type = token['type'] if type in ('Characters', 'SpaceCharacters'): if text is None: text = token['data'] else: text += token['data'] elif text is not None: yield TEXT, text, (None, -1, -1) text = None if type in ('StartTag', 'EmptyTag'): if token['namespace']: name = '{%s}%s' % (token['namespace'], token['name']) else: name = token['name'] attrs = Attrs([ (QName('{%s}%s' % attr if attr[0] is not None else attr[1]), value) for attr, value in token['data'].items() ]) yield (START, (QName(name), attrs), (None, -1, -1)) if type == 'EmptyTag': type = 'EndTag' if type == 'EndTag': if token['namespace']: name = '{%s}%s' % (token['namespace'], token['name']) else: name = token['name'] yield END, QName(name), (None, -1, -1) elif type == 'Comment': yield COMMENT, token['data'], (None, -1, -1) elif type == 'Doctype': yield DOCTYPE, (token['name'], token['publicId'], token['systemId']), (None, -1, -1) else: pass # FIXME: What to do? if text is not None: yield TEXT, text, (None, -1, -1)
def GenshiAdapter(tree): text = None for token in treewalkers.getTreeWalker("dom")(tree): type = token["type"] if type in ("Characters", "SpaceCharacters"): if text is None: text = token["data"] else: text += token["data"] elif text is not None: yield TEXT, text, (None, -1, -1) text = None if type in ("StartTag", "EmptyTag"): if token["namespace"]: name = "{%s}%s" % (token["namespace"], token["name"]) else: name = token["name"] attrs = Attrs([ (QName("{%s}%s" % attr if attr[0] is not None else attr[1]), value) for attr, value in token["data"].items() ]) yield (START, (QName(name), attrs), (None, -1, -1)) if type == "EmptyTag": type = "EndTag" if type == "EndTag": if token["namespace"]: name = "{%s}%s" % (token["namespace"], token["name"]) else: name = token["name"] yield END, QName(name), (None, -1, -1) elif type == "Comment": yield COMMENT, token["data"], (None, -1, -1) elif type == "Doctype": yield DOCTYPE, (token["name"], token["publicId"], token["systemId"]), (None, -1, -1) else: pass # FIXME: What to do? if text is not None: yield TEXT, text, (None, -1, -1)
def __call__(self, stream): """Apply the filter to the given stream. :param stream: the markup event stream to filter """ waiting_for = None for kind, data, pos in stream: if kind is START: if waiting_for: continue tag, attrs = data if not self.is_safe_elem(tag, attrs): waiting_for = tag continue new_attrs = [] for attr, value in attrs: value = stripentities(value) if attr not in self.safe_attrs: continue elif attr in self.uri_attrs: # Don't allow URI schemes such as "javascript:" if not self.is_safe_uri(value): continue elif attr == 'style': # Remove dangerous CSS declarations from inline styles decls = self.sanitize_css(value) if not decls: continue value = '; '.join(decls) new_attrs.append((attr, value)) yield kind, (tag, Attrs(new_attrs)), pos elif kind is END: tag = data if waiting_for: if waiting_for == tag: waiting_for = None else: yield kind, data, pos elif kind is not COMMENT: if not waiting_for: yield kind, data, pos
def _parse(self, source, encoding): streams = [[]] # stacked lists of events of the "compiled" template dirmap = {} # temporary mapping of directives to elements ns_prefix = {} depth = 0 fallbacks = [] includes = [] if not isinstance(source, Stream): source = XMLParser(source, filename=self.filename, encoding=encoding) for kind, data, pos in source: stream = streams[-1] if kind is START_NS: # Strip out the namespace declaration for template directives prefix, uri = data ns_prefix[prefix] = uri if uri not in (self.DIRECTIVE_NAMESPACE, self.XINCLUDE_NAMESPACE): stream.append((kind, data, pos)) elif kind is END_NS: uri = ns_prefix.pop(data, None) if uri and uri not in (self.DIRECTIVE_NAMESPACE, self.XINCLUDE_NAMESPACE): stream.append((kind, data, pos)) elif kind is START: # Record any directive attributes in start tags tag, attrs = data directives = [] strip = False if tag in self.DIRECTIVE_NAMESPACE: cls = self._dir_by_name.get(tag.localname) if cls is None: raise BadDirectiveError(tag.localname, self.filepath, pos[1]) args = dict([(name.localname, value) for name, value in attrs if not name.namespace]) directives.append((cls, args, ns_prefix.copy(), pos)) strip = True new_attrs = [] for name, value in attrs: if name in self.DIRECTIVE_NAMESPACE: cls = self._dir_by_name.get(name.localname) if cls is None: raise BadDirectiveError(name.localname, self.filepath, pos[1]) directives.append((cls, value, ns_prefix.copy(), pos)) else: if value: value = list(interpolate(value, self.filepath, pos[1], pos[2], lookup=self.lookup)) if len(value) == 1 and value[0][0] is TEXT: value = value[0][1] else: value = [(TEXT, u"", pos)] new_attrs.append((name, value)) new_attrs = Attrs(new_attrs) if directives: index = self._dir_order.index directives.sort(lambda a, b: cmp(index(a[0]), index(b[0]))) dirmap[(depth, tag)] = (directives, len(stream), strip) if tag in self.XINCLUDE_NAMESPACE: if tag.localname == "include": include_href = new_attrs.get("href") if not include_href: raise TemplateSyntaxError( "Include misses required " 'attribute "href"', self.filepath, *pos[1:] ) includes.append((include_href, new_attrs.get("parse"))) streams.append([]) elif tag.localname == "fallback": streams.append([]) fallbacks.append(streams[-1]) else: stream.append((kind, (tag, new_attrs), pos)) depth += 1 elif kind is END: depth -= 1 if fallbacks and data == self.XINCLUDE_NAMESPACE["fallback"]: assert streams.pop() is fallbacks[-1] elif data == self.XINCLUDE_NAMESPACE["include"]: fallback = None if len(fallbacks) == len(includes): fallback = fallbacks.pop() streams.pop() # discard anything between the include tags # and the fallback element stream = streams[-1] href, parse = includes.pop() try: cls = {"xml": MarkupTemplate, "text": NewTextTemplate}[parse or "xml"] except KeyError: raise TemplateSyntaxError( 'Invalid value for "parse" ' "attribute of include", self.filepath, *pos[1:] ) stream.append((INCLUDE, (href, cls, fallback), pos)) else: stream.append((kind, data, pos)) # If there have have directive attributes with the corresponding # start tag, move the events inbetween into a "subprogram" if (depth, data) in dirmap: directives, start_offset, strip = dirmap.pop((depth, data)) substream = stream[start_offset:] if strip: substream = substream[1:-1] stream[start_offset:] = [(SUB, (directives, substream), pos)] elif kind is PI and data[0] == "python": if not self.allow_exec: raise TemplateSyntaxError("Python code blocks not allowed", self.filepath, *pos[1:]) try: suite = Suite(data[1], self.filepath, pos[1], lookup=self.lookup) except SyntaxError, err: raise TemplateSyntaxError( err, self.filepath, pos[1] + (err.lineno or 1) - 1, pos[2] + (err.offset or 0) ) stream.append((EXEC, suite, pos)) elif kind is TEXT: for kind, data, pos in interpolate(data, self.filepath, pos[1], pos[2], lookup=self.lookup): stream.append((kind, data, pos))
def __call__(self, stream, ctxt=None, search_text=True, msgbuf=None): """Translate any localizable strings in the given stream. This function shouldn't be called directly. Instead, an instance of the `Translator` class should be registered as a filter with the `Template` or the `TemplateLoader`, or applied as a regular stream filter. If used as a template filter, it should be inserted in front of all the default filters. :param stream: the markup event stream :param ctxt: the template context (not used) :param search_text: whether text nodes should be translated (used internally) :param msgbuf: a `MessageBuffer` object or `None` (used internally) :return: the localized stream """ ignore_tags = self.ignore_tags include_attrs = self.include_attrs translate = self.translate if not self.extract_text: search_text = False skip = 0 i18n_msg = I18N_NAMESPACE['msg'] ns_prefixes = [] xml_lang = XML_NAMESPACE['lang'] for kind, data, pos in stream: # skip chunks that should not be localized if skip: if kind is START: skip += 1 elif kind is END: skip -= 1 yield kind, data, pos continue # handle different events that can be localized if kind is START: tag, attrs = data if tag in self.ignore_tags or \ isinstance(attrs.get(xml_lang), basestring): skip += 1 yield kind, data, pos continue new_attrs = [] changed = False for name, value in attrs: newval = value if search_text and isinstance(value, basestring): if name in include_attrs: newval = self.translate(value) else: newval = list(self(_ensure(value), ctxt, search_text=False, msgbuf=msgbuf) ) if newval != value: value = newval changed = True new_attrs.append((name, value)) if changed: attrs = Attrs(new_attrs) if msgbuf: msgbuf.append(kind, data, pos) continue elif i18n_msg in attrs: params = attrs.get(i18n_msg) if params and type(params) is list: # event tuple params = params[0][1] msgbuf = MessageBuffer(params) attrs -= i18n_msg yield kind, (tag, attrs), pos elif search_text and kind is TEXT: if not msgbuf: text = data.strip() if text: data = data.replace(text, unicode(translate(text))) yield kind, data, pos else: msgbuf.append(kind, data, pos) elif msgbuf and kind is EXPR: msgbuf.append(kind, data, pos) elif not skip and msgbuf and kind is END: msgbuf.append(kind, data, pos) if not msgbuf.depth: for event in msgbuf.translate(translate(msgbuf.format())): yield event msgbuf = None yield kind, data, pos elif kind is SUB: subkind, substream = data new_substream = list(self(substream, ctxt, msgbuf=msgbuf)) yield kind, (subkind, new_substream), pos elif kind is START_NS and data[1] == I18N_NAMESPACE: ns_prefixes.append(data[0]) elif kind is END_NS and data in ns_prefixes: ns_prefixes.remove(data) else: yield kind, data, pos