# HG changeset patch # User cmlenz # Date 1159786172 0 # Node ID 8de2620504b9d2c1a4c4fa768e48e82a2347596d # Parent c9fd819531698cd272af4e2c65b9a0418ad8b288 Fix the handling of namespace context for match templates. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,10 @@ HTML forms based on a dictionary of values. * The set of permitted tag and attribute names for the HTMLSanitizer can now be configured per instance. + * Fixed the namespace context used by XPath patterns in py:match templates. + The were erroneously using the namespace context of the elements being + matched, where they should rather use the context in which they were + defined. Version 0.3.1 diff --git a/genshi/core.py b/genshi/core.py --- a/genshi/core.py +++ b/genshi/core.py @@ -146,14 +146,14 @@ return output.encode(encoding, errors) return output - def select(self, path): + def select(self, path, namespaces=None, variables=None): """Return a new stream that contains the events matching the given XPath expression. @param path: a string containing the XPath expression """ from genshi.path import Path - return Path(path).select(self) + return Path(path).select(self, namespaces, variables) def serialize(self, method='xml', **kwargs): """Generate strings corresponding to a specific serialization of the @@ -509,6 +509,9 @@ def __contains__(self, qname): return qname.namespace == self.uri + def __ne__(self, other): + return not self == other + def __eq__(self, other): if isinstance(other, Namespace): return self.uri == other.uri diff --git a/genshi/template.py b/genshi/template.py --- a/genshi/template.py +++ b/genshi/template.py @@ -166,7 +166,8 @@ """ __slots__ = ['expr'] - def __init__(self, value, filename=None, lineno=-1, offset=-1): + def __init__(self, value, namespaces=None, filename=None, lineno=-1, + offset=-1): try: self.expr = value and Expression(value, filename, lineno) or None except SyntaxError, err: @@ -345,8 +346,9 @@ ATTRIBUTE = 'function' - def __init__(self, args, filename=None, lineno=-1, offset=-1): - Directive.__init__(self, None, filename, lineno, offset) + def __init__(self, args, namespaces=None, filename=None, lineno=-1, + offset=-1): + Directive.__init__(self, None, namespaces, filename, lineno, offset) ast = compiler.parse(args, 'eval').node self.args = [] self.defaults = {} @@ -414,14 +416,16 @@ ATTRIBUTE = 'each' - def __init__(self, value, filename=None, lineno=-1, offset=-1): + def __init__(self, value, namespaces=None, filename=None, lineno=-1, + offset=-1): if ' in ' not in value: raise TemplateSyntaxError('"in" keyword missing in "for" directive', filename, lineno, offset) assign, value = value.split(' in ', 1) ast = compiler.parse(assign, 'exec') self.assign = _assignment(ast.node.nodes[0].expr) - Directive.__init__(self, value.strip(), filename, lineno, offset) + Directive.__init__(self, value.strip(), namespaces, filename, lineno, + offset) def __call__(self, stream, ctxt, directives): iterable = self.expr.evaluate(ctxt) @@ -480,17 +484,22 @@ """ - __slots__ = ['path'] + __slots__ = ['path', 'namespaces'] ATTRIBUTE = 'path' - def __init__(self, value, filename=None, lineno=-1, offset=-1): - Directive.__init__(self, None, filename, lineno, offset) + def __init__(self, value, namespaces=None, filename=None, lineno=-1, + offset=-1): + Directive.__init__(self, None, namespaces, filename, lineno, offset) self.path = Path(value, filename, lineno) + if namespaces is None: + namespaces = {} + self.namespaces = namespaces.copy() def __call__(self, stream, ctxt, directives): ctxt._match_templates.append((self.path.test(ignore_context=True), - self.path, list(stream), directives)) + self.path, list(stream), self.namespaces, + directives)) return [] def __repr__(self): @@ -698,8 +707,9 @@ ATTRIBUTE = 'vars' - def __init__(self, value, filename=None, lineno=-1, offset=-1): - Directive.__init__(self, None, filename, lineno, offset) + def __init__(self, value, namespaces=None, filename=None, lineno=-1, + offset=-1): + Directive.__init__(self, None, namespaces, filename, lineno, offset) self.vars = [] value = value.strip() try: @@ -975,15 +985,13 @@ if kind is START_NS: # Strip out the namespace declaration for template directives prefix, uri = data - if uri == self.NAMESPACE: - ns_prefix[prefix] = uri - else: + ns_prefix[prefix] = uri + if uri != self.NAMESPACE: stream.append((kind, data, pos)) elif kind is END_NS: - if data in ns_prefix: - del ns_prefix[data] - else: + uri = ns_prefix.pop(data) + if uri != self.NAMESPACE: stream.append((kind, data, pos)) elif kind is START: @@ -997,7 +1005,7 @@ if cls is None: raise BadDirectiveError(tag.localname, pos[0], pos[1]) value = attrib.get(getattr(cls, 'ATTRIBUTE', None), '') - directives.append(cls(value, *pos)) + directives.append(cls(value, ns_prefix, *pos)) strip = True new_attrib = [] @@ -1007,7 +1015,7 @@ if cls is None: raise BadDirectiveError(name.localname, pos[0], pos[1]) - directives.append(cls(value, *pos)) + directives.append(cls(value, ns_prefix, *pos)) else: if value: value = list(self._interpolate(value, *pos)) @@ -1059,7 +1067,6 @@ """ if match_templates is None: match_templates = ctxt._match_templates - nsprefix = {} # mapping of namespace prefixes to URIs tail = [] def _strip(stream): @@ -1084,15 +1091,15 @@ yield kind, data, pos continue - for idx, (test, path, template, directives) in \ + for idx, (test, path, template, namespaces, directives) in \ enumerate(match_templates): - if test(kind, data, pos, nsprefix, ctxt) is True: + if test(kind, data, pos, namespaces, ctxt) is True: # Let the remaining match templates know about the event so # they get a chance to update their internal state for test in [mt[0] for mt in match_templates[idx + 1:]]: - test(kind, data, pos, nsprefix, ctxt) + test(kind, data, pos, namespaces, ctxt) # Consume and store all events until an end event # corresponding to this start event is encountered @@ -1105,12 +1112,12 @@ kind, data, pos = tail[0] for test in [mt[0] for mt in match_templates]: - test(kind, data, pos, nsprefix, ctxt) + test(kind, data, pos, namespaces, ctxt) # Make the select() function available in the body of the # match template def select(path): - return Stream(content).select(path) + return Stream(content).select(path, namespaces, ctxt) ctxt.push(dict(select=select)) # Recursively process the output @@ -1200,7 +1207,7 @@ cls = self._dir_by_name.get(command) if cls is None: raise BadDirectiveError(command) - directive = cls(value, self.filepath, lineno, 0) + directive = cls(value, None, self.filepath, lineno, 0) dirmap[depth] = (directive, len(stream)) depth += 1 diff --git a/genshi/tests/template.py b/genshi/tests/template.py --- a/genshi/tests/template.py +++ b/genshi/tests/template.py @@ -690,6 +690,18 @@

""", str(tmpl.generate(fields=fields, values=values))) + def test_namespace_context(self): + tmpl = MarkupTemplate(""" +
Foo
+ + """) + # FIXME: there should be a way to strip out unwanted/unused namespaces, + # such as the "x" in this example + self.assertEqual(""" +
Foo
+ """, str(tmpl.generate())) + # FIXME #def test_match_after_step(self): # tmpl = MarkupTemplate("""