changeset 54:01981cbc7575

Fix a number of escaping problems: * `Markup` instances were get escaped * Expressions in text nodes no longer escape quotes (#9)
author cmlenz
date Tue, 04 Jul 2006 13:09:36 +0000
parents 60f1a556690e
children 5f0b2976f8c4
files markup/core.py markup/filters.py markup/template.py markup/tests/template.py
diffstat 4 files changed, 74 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/markup/core.py
+++ b/markup/core.py
@@ -238,8 +238,8 @@
     def __repr__(self):
         return '<%s "%s">' % (self.__class__.__name__, self)
 
-    def join(self, seq):
-        return Markup(unicode(self).join([escape(item, quotes=False)
+    def join(self, seq, escape_quotes=True):
+        return Markup(unicode(self).join([escape(item, quotes=escape_quotes)
                                           for item in seq]))
 
     def stripentities(self, keepxmlentities=False):
--- a/markup/filters.py
+++ b/markup/filters.py
@@ -118,7 +118,7 @@
             if kind is Stream.TEXT:
                 textbuf.append(data)
             elif prev_kind is Stream.TEXT:
-                text = Markup('').join(textbuf)
+                text = Markup('').join(textbuf, escape_quotes=False)
                 text = self._TRAILING_SPACE.sub('', text)
                 text = self._LINE_COLLAPSE.sub('\n', text)
                 yield Stream.TEXT, Markup(text), pos
@@ -128,7 +128,9 @@
                 yield kind, data, pos
 
         if textbuf:
-            text = self._LINE_COLLAPSE.sub('\n', ''.join(textbuf))
+            text = Markup('').join(textbuf, escape_quotes=False)
+            text = self._TRAILING_SPACE.sub('', text)
+            text = self._LINE_COLLAPSE.sub('\n', text)
             yield Stream.TEXT, Markup(text), pos
 
 
--- a/markup/template.py
+++ b/markup/template.py
@@ -157,9 +157,10 @@
 class Directive(object):
     """Abstract base class for template directives.
     
-    A directive is basically a callable that takes two parameters: `ctxt` is
-    the template data context, and `stream` is an iterable over the events that
-    the directive applies to.
+    A directive is basically a callable that takes three positional arguments:
+    `ctxt` is the template data context, `stream` is an iterable over the
+    events that the directive applies to, and `directives` is is a list of
+    other directives on the same stream that need to be applied.
     
     Directives can be "anonymous" or "registered". Registered directives can be
     applied by the template author using an XML attribute with the
@@ -470,7 +471,7 @@
     """
     __slots__ = []
 
-    def __call__(self, stream, ctxt, directives=None):
+    def __call__(self, stream, ctxt, directives):
         kind, data, pos = stream.next()
         yield Template.EXPR, self.expr, pos
 
@@ -507,7 +508,7 @@
     """
     __slots__ = []
 
-    def __call__(self, stream, ctxt, directives=None):
+    def __call__(self, stream, ctxt, directives):
         if self.expr:
             strip = self.expr.evaluate(ctxt)
         else:
@@ -580,7 +581,7 @@
     
     See the documentation of `py:choose` for usage.
     """
-    def __call__(self, stream, ctxt, directives=None):
+    def __call__(self, stream, ctxt, directives):
         choose = ctxt['_choose']
         if choose.matched:
             return []
@@ -602,7 +603,7 @@
     
     See the documentation of `py:choose` for usage.
     """
-    def __call__(self, stream, ctxt, directives=None):
+    def __call__(self, stream, ctxt, directives):
         choose = ctxt['_choose']
         if choose.matched:
             return []
@@ -809,7 +810,7 @@
                 # succeeds, and the string will be chopped up into individual
                 # characters
                 if isinstance(result, basestring):
-                    yield Stream.TEXT, unicode(result), pos
+                    yield Stream.TEXT, result, pos
                 else:
                     # Test if the expression evaluated to an iterable, in which
                     # case we yield the individual items
--- a/markup/tests/template.py
+++ b/markup/tests/template.py
@@ -15,7 +15,7 @@
 import unittest
 import sys
 
-from markup.core import Stream
+from markup.core import Markup, Stream
 from markup.template import BadDirectiveError, Context, Template, \
                             TemplateSyntaxError
 
@@ -35,6 +35,30 @@
           <elem id="1" class="foo"/><elem id="2" class="bar"/>
         </doc>""", str(tmpl.generate(Context(items=items))))
 
+    def test_update_existing_attr(self):
+        """
+        Verify that an attribute value that evaluates to `None` removes an
+        existing attribute of that name.
+        """
+        tmpl = Template("""<doc xmlns:py="http://purl.org/kid/ns#">
+          <elem class="foo" py:attrs="{'class': 'bar'}"/>
+        </doc>""")
+        self.assertEqual("""<doc>
+          <elem class="bar"/>
+        </doc>""", str(tmpl.generate()))
+
+    def test_remove_existing_attr(self):
+        """
+        Verify that an attribute value that evaluates to `None` removes an
+        existing attribute of that name.
+        """
+        tmpl = Template("""<doc xmlns:py="http://purl.org/kid/ns#">
+          <elem class="foo" py:attrs="{'class': None}"/>
+        </doc>""")
+        self.assertEqual("""<doc>
+          <elem/>
+        </doc>""", str(tmpl.generate()))
+
 
 class ChooseDirectiveTestCase(unittest.TestCase):
     """Tests for the `py:choose` template directive and the complementary
@@ -339,6 +363,40 @@
                 self.assertEqual(2, e.lineno)
                 self.assertEqual(10, e.offset)
 
+    def test_markup_noescape(self):
+        """
+        Verify that outputting context data that is a `Markup` instance is not
+        escaped.
+        """
+        tmpl = Template("""<div xmlns:py="http://purl.org/kid/ns#">
+          $myvar
+        </div>""")
+        self.assertEqual("""<div>
+          <b>foo</b>
+        </div>""", str(tmpl.generate(Context(myvar=Markup('<b>foo</b>')))))
+
+    def test_text_noescape_quotes(self):
+        """
+        Verify that outputting context data in text nodes doesn't escape quotes.
+        """
+        tmpl = Template("""<div xmlns:py="http://purl.org/kid/ns#">
+          $myvar
+        </div>""")
+        self.assertEqual("""<div>
+          "foo"
+        </div>""", str(tmpl.generate(Context(myvar='"foo"'))))
+
+    def test_attr_escape_quotes(self):
+        """
+        Verify that outputting context data in attribtes escapes quotes.
+        """
+        tmpl = Template("""<div xmlns:py="http://purl.org/kid/ns#">
+          <elem class="$myvar"/>
+        </div>""")
+        self.assertEqual("""<div>
+          <elem class="&#34;foo&#34;"/>
+        </div>""", str(tmpl.generate(Context(myvar='"foo"'))))
+
 
 def suite():
     suite = unittest.TestSuite()
Copyright (C) 2012-2017 Edgewall Software