def __call__(self, stream): namespace = self.namespace for kind, data, pos in stream: if kind is START or kind is EMPTY: tag, attrs = data if tag.namespace and tag not in namespace: continue new_attrs = [] for attr, value in attrs: if not attr.namespace or attr in namespace: new_attrs.append((attr, value)) data = tag.localname, Attrs(new_attrs) elif kind is END: if data.namespace and data not in namespace: continue data = data.localname elif kind is START_NS or kind is END_NS: continue yield kind, data, pos
def __call__(self, kind, data, pos, namespaces, variables): namespace = Namespace(namespaces.get(self.prefix)) if kind is START: if self.principal_type is ATTRIBUTE and data[1]: return Attrs([(name, value) for name, value in data[1] if name in namespace]) or None else: return data[0] in namespace
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) self.assertEqual("""<div> <x:p xmlns:x="http://example.org/"/> <x:p xmlns:x="http://example.org/"/> </div>""", output)
def handle_starttag(self, tag, attrib): fixed_attrib = [] for name, value in attrib: # Fixup minimized attributes if value is None: value = unicode(name) elif not isinstance(value, unicode): 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 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) self.assertEqual("""<div xmlns="http://example.org/"> <p/> <p/> </div>""", output)
def _eval(self, stream, ctxt): """Internal stream filter that evaluates any expressions in `START` and `TEXT` events. """ filters = (self._flatten, self._eval) 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): 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 = data.evaluate(ctxt) 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 hasattr(result, '__iter__'): substream = _ensure(result) for filter_ in filters: substream = filter_(substream, ctxt) for event in substream: yield event else: yield TEXT, unicode(result), pos else: yield kind, data, pos
def ET(element): """Convert a given ElementTree element to a markup stream. :param element: an ElementTree element :return: a markup stream """ tag_name = QName(element.tag.lstrip('{')) attrs = Attrs([(QName(attr.lstrip('{')), value) for attr, value in element.items()]) yield START, (tag_name, attrs), (None, -1, -1) if element.text: yield TEXT, element.text, (None, -1, -1) for child in element.getchildren(): for item in ET(child): yield item yield END, tag_name, (None, -1, -1) if element.tail: yield TEXT, element.tail, (None, -1, -1)
def __call__(self, stream): prefixes = dict([(v, [k]) for k, v in self.prefixes.items()]) namespaces = {XML_NAMESPACE.uri: ['xml']} def _push_ns(prefix, uri): namespaces.setdefault(uri, []).append(prefix) prefixes.setdefault(prefix, []).append(uri) ns_attrs = [] _push_ns_attr = ns_attrs.append def _make_ns_attr(prefix, uri): return u'xmlns%s' % (prefix and ':%s' % prefix or ''), uri def _gen_prefix(): val = 0 while 1: val += 1 yield 'ns%d' % val _gen_prefix = _gen_prefix().next for kind, data, pos in stream: if kind is START or kind is EMPTY: tag, attrs = data tagname = tag.localname tagns = tag.namespace if tagns: if tagns in namespaces: prefix = namespaces[tagns][-1] if prefix: tagname = u'%s:%s' % (prefix, tagname) else: _push_ns_attr((u'xmlns', tagns)) _push_ns('', tagns) new_attrs = [] for attr, value in attrs: attrname = attr.localname attrns = attr.namespace if attrns: if attrns not in namespaces: prefix = _gen_prefix() _push_ns(prefix, attrns) _push_ns_attr(('xmlns:%s' % prefix, attrns)) else: prefix = namespaces[attrns][-1] if prefix: attrname = u'%s:%s' % (prefix, attrname) new_attrs.append((attrname, value)) yield kind, (tagname, Attrs(ns_attrs + new_attrs)), pos del ns_attrs[:] elif kind is END: tagname = data.localname tagns = data.namespace if tagns: prefix = namespaces[tagns][-1] if prefix: tagname = u'%s:%s' % (prefix, tagname) yield kind, tagname, pos elif kind is START_NS: prefix, uri = data if uri not in namespaces: prefix = prefixes.get(uri, [prefix])[-1] _push_ns_attr(_make_ns_attr(prefix, uri)) _push_ns(prefix, uri) elif kind is END_NS: if data in prefixes: uris = prefixes.get(data) uri = uris.pop() if not uris: del prefixes[data] if uri not in uris or uri != uris[-1]: uri_prefixes = namespaces[uri] uri_prefixes.pop() if not uri_prefixes: del namespaces[uri] if ns_attrs: attr = _make_ns_attr(data, uri) if attr in ns_attrs: ns_attrs.remove(attr) else: yield kind, data, pos
def _handle_start(self, tag, attrib): attrs = Attrs([(QName(name), value) for name, value in zip(*[iter(attrib)] * 2)]) self._enqueue(START, (QName(tag), attrs))
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]) value = attrs.get(getattr(cls, 'ATTRIBUTE', None), '') directives.append((cls, value, 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.basedir, pos[0], 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) 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] stream.append((INCLUDE, (includes.pop(), 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': try: # As Expat doesn't report whitespace between the PI target # and the data, we have to jump through some hoops here to # get correctly indented Python code # Unfortunately, we'll still probably not get the line # number quite right lines = [ line.expandtabs() for line in data[1].splitlines() ] first = lines[0] rest = dedent('\n'.join(lines[1:])).rstrip() if first.rstrip().endswith(':') and not rest[0].isspace(): rest = '\n'.join( [' ' + line for line in rest.splitlines()]) source = '\n'.join([first, rest]) suite = Suite(source, 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.basedir, pos[0], pos[1], pos[2], lookup=self.lookup): stream.append((kind, data, pos))