changeset 278:d6c58473a9d0 trunk

Fix the handling of namespace context for match templates.
author cmlenz
date Mon, 02 Oct 2006 10:49:32 +0000
parents 7e30bfa966ab
children a99666402b12
files ChangeLog genshi/core.py genshi/template.py genshi/tests/template.py
diffstat 4 files changed, 54 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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
--- 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 @@
       </span>
     </div>
     """
-    __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
 
--- a/genshi/tests/template.py
+++ b/genshi/tests/template.py
@@ -690,6 +690,18 @@
           </p></form>
         </html>""", str(tmpl.generate(fields=fields, values=values)))
 
+    def test_namespace_context(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+                                       xmlns:x="http://www.example.org/">
+          <div py:match="x:foo">Foo</div>
+          <foo xmlns="http://www.example.org/"/>
+        </html>""")
+        # FIXME: there should be a way to strip out unwanted/unused namespaces,
+        #        such as the "x" in this example
+        self.assertEqual("""<html xmlns:x="http://www.example.org/">
+          <div>Foo</div>
+        </html>""", str(tmpl.generate()))
+
     # FIXME
     #def test_match_after_step(self):
     #    tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
Copyright (C) 2012-2017 Edgewall Software