Commits

Rick Copeland committed 54b45c7

XML language is feature-complete

  • Participants
  • Parent commits 4bd65a7

Comments (0)

Files changed (5)

-* XML attribute support (non-py:-namespaced)
-* XML attributes: py:content, py:replace, py:strip, py:attrs
 * XML serialization to HTML4, HTML5
 * Track line number information in Text  templates
 * Write CSS => IR compiler (and document language)

fastpt/tests/v2/test_xml.py

 <!--! This comment is stripped. -->
 </div>''')
         rsp = tpl(dict(name='Rick')).__fpt__.render()
-        print rsp
+        assert rsp == '''<div>
+<!--  This comment is preserved.  -->
+
+</div>''', rsp
+
+class TestAttributes(TestCase):
+
+    def test_basic(self):
+        tpl = XMLTemplate('''<div id="foo"/>''')
+        rsp = tpl(dict(name='Rick')).__fpt__.render()
+        assert rsp == '<div id="foo"/>', rsp
         
+    def test_content(self):
+        tpl = XMLTemplate('''<div py:content="'foo'"/>''')
+        rsp = tpl(dict(name='Rick')).__fpt__.render()
+        assert rsp == '<div>foo</div>', rsp
+        
+    def test_replace(self):
+        tpl = XMLTemplate('''<div py:replace="'foo'"/>''')
+        rsp = tpl(dict(name='Rick')).__fpt__.render()
+        assert rsp == 'foo', rsp
+
+    def test_attrs(self):
+        tpl = XMLTemplate('''<div py:attrs="dict(a=5, b=6)"/>''')
+        rsp = tpl(dict(name='Rick')).__fpt__.render()
+        assert rsp == '<div a="5" b="6"/>'
+        tpl = XMLTemplate('''<div py:attrs="[('a', 5), ('b', 6)]"/>''')
+        rsp = tpl(dict(name='Rick')).__fpt__.render()
+        assert rsp == '<div a="5" b="6"/>'
+
+    def test_strip(self):
+        tpl = XMLTemplate('''<div><h1 py:strip="header">Header</h1></div>''')
+        rsp = tpl(dict(header=True)).__fpt__.render()
+        assert rsp == '<div><h1>Header</h1></div>', rsp
+        rsp = tpl(dict(header=False)).__fpt__.render()
+        assert rsp == '<div>Header</div>', rsp
 
 if __name__ == '__main__':
     main()
 
 class TextNode(Node):
 
-    def __init__(self, text):
+    def __init__(self, text, guard=None):
         super(TextNode, self).__init__()
         self.text = text
+        self.guard = guard
 
     def py(self):
-        yield self.line('yield %r' % self.text)
+        s = 'yield %r' % self.text
+        if self.guard:
+            yield self.line('if %s: %s' % (self.guard, s))
+        else:
+            yield self.line(s)
 
 class ExprNode(Node):
 
     def py(self):
         yield self.line('yield self.__fpt__.escape(%s)' % self.text)
 
+class AttrNode(Node):
+
+    def __init__(self, attr, value, guard=None):
+        super(AttrNode, self).__init__()
+        self.attr = attr
+        self.value = value
+        self.guard = guard
+
+    def py(self):
+        s = 'yield \' %s="%s"\'' % (self.attr, self.value)
+        if self.guard:
+            yield self.line('if %s: %s' % (self.guard, s))
+        else:
+            yield self.line(s)
+
+class AttrsNode(Node):
+
+    def __init__(self, attrs, guard=None):
+        super(AttrsNode, self).__init__()
+        self.attrs = attrs
+        self.guard = guard
+
+    def py(self):
+        k,v = gen_name(), gen_name()
+        def _body():
+            yield self.line('for %s,%s in self.__fpt__.iter_attrs(%s):' % (k, v, self.attrs))
+            yield self.line('    yield \' %%s="%%s"\' %% (%s, %s)' % (k,v))
+        if self.guard:
+            yield self.line('if %s:' % self.guard)
+            for l in _body():
+                yield l.indent()
+        else:
+            for l in _body(): yield l
+
 class PythonNode(Node):
 
     def __init__(self, *body):

fastpt/v2/template.py

             literal=literal,
             __builtins__=__builtins__,
             __fpt__=fastpt.v2)
-        self._buffer = StringIO()
         for k,v in self.__methods__:
             v = v.bind_instance(self)
             setattr(self, k, v)
             case=self._case,
             import_=self._import,
             escape=self._escape,
-            write=self._buffer.write)
+            iter_attrs=self._iter_attrs)
         self._switch_stack = []
         self.__globals__.update(context)
 
         else:
             return escape(unicode(value))
 
+    def _iter_attrs(self, attrs):
+        if hasattr(attrs, 'items'):
+            attrs = attrs.items()
+        for k,v in attrs:
+            yield k, self._escape(v)
+
 def Template(ns):
     dct = {}
     methods = dct['__methods__'] = []

fastpt/v2/xml_template.py

             return self._compile_html(node)
 
     def _compile_xml(self, node):
-        yield ir.TextNode(u'<%s' % node.tagName)
+        content = attrs = guard = None
+        if node.hasAttribute('py:strip'):
+            guard = node.getAttribute('py:strip')
+            node.removeAttribute('py:strip')
+        yield ir.TextNode(u'<%s' % node.tagName, guard)
         for k,v in node.attributes.items():
-            if k.startswith('py:'):
-                raise NotImplemented, '_compile_attr(%s)' % k
-            yield ir.AttrNode(k, list(self._compile_text(v)))
-        if node.childNodes:
-            yield ir.TextNode(u'>')
-            for cn in node.childNodes:
-                for x in self._compile_node(cn):
-                    yield x
-            yield ir.TextNode(u'</%s>' % node.tagName)
+            tc = _TextCompiler(self.filename, v, node.lineno)
+            v = u''.join(n.text for n in tc)
+            if k == 'py:content':
+                content = node.getAttribute('py:content')
+                continue
+            elif k == 'py:attrs':
+                attrs = node.getAttribute('py:attrs')
+                continue
+            yield ir.AttrNode(k, v, guard)
+        if attrs:
+            yield ir.AttrsNode(attrs, guard)
+        if content:
+            yield ir.TextNode(u'>', guard)
+            yield ir.ExprNode(content)
+            yield ir.TextNode(u'</%s>' % node.tagName, guard)
         else:
-            yield ir.TextNode(u'/>')
+            if node.childNodes:
+                yield ir.TextNode(u'>', guard)
+                for cn in node.childNodes:
+                    for x in self._compile_node(cn):
+                        yield x
+                yield ir.TextNode(u'</%s>' % node.tagName, guard)
+            else:
+                yield ir.TextNode(u'/>', guard)
+
+    def _compile_replace(self, node):
+        yield ir.ExprNode(node.getAttribute('value'))
 
     def _compile_pi(self, node):
         body = ir.TextNode(node.data.strip())