rework html a bit to move the render_* funcs to IRenderable, add XHTMLDocument (ugh), missing flatten
authorTero Marttila <terom@fixme.fi>
Fri, 05 Jun 2009 21:47:45 +0300
changeset 61 fad360dd01da
parent 60 406da27a4be2
child 62 53e798708413
rework html a bit to move the render_* funcs to IRenderable, add XHTMLDocument (ugh), missing flatten
degal/html.py
--- a/degal/html.py	Fri Jun 05 21:46:43 2009 +0300
+++ b/degal/html.py	Fri Jun 05 21:47:45 2009 +0300
@@ -1,5 +1,7 @@
 """
-    Generating HTML tags
+    Generating XHTML output
+
+    XXX: use a 'real' XML builder?
 """
 
 from cgi import escape
@@ -17,6 +19,55 @@
 
         abstract
 
+    def render_lines (self, indent=u'\t', newline=u'\n') :
+        """
+            Render full output lines with given newlines
+
+            >>> list(Tag('xx', 'yy').render_lines())
+            [u'<xx>\\n', u'\\tyy\\n', u'</xx>\\n']
+        """
+
+        for line in self.render_raw_lines(indent) :
+            yield line + newline
+
+    def render_unicode (self, **render_opts) :
+        """
+            Render full tag as a single unicode string
+
+            >>> Tag('xx', 'yy').render_unicode()
+            u'<xx>\\n\\tyy\\n</xx>\\n'
+        """
+
+        return "".join(self.render_lines(**render_opts))
+
+    def render_str (self, encoding='ascii', **render_opts) :
+        """
+            Render full tag as an encoded string
+
+            >>> Tag('xx', 'yy').render_str()
+            '<xx>\\n\\tyy\\n</xx>\\n'
+        """
+
+        return self.render_unicode(**render_opts).encode(encoding)
+
+    def render_out (self, stream, encoding, **render_opts) :
+        """
+            Render output into the given stream, encoding using the given encoding
+
+            >>> from StringIO import StringIO; buf = StringIO(); Tag('xx', 'yy').render_out(buf, 'ascii'); buf.getvalue()
+            '<xx>\\n\\tyy\\n</xx>\\n'
+        """
+
+        for line in self.render_lines(**render_opts) :
+            stream.write(line.encode(encoding))
+    
+    # default output
+    __str__ = render_str
+    __unicode__ = render_unicode
+
+    # default .render method
+    render = render_unicode
+
 class Tag (IRenderable) :
     """
         A HTML tag, with attributes and contents, which can a mixture of data and other renderables(tags).
@@ -31,6 +82,8 @@
 
             Items that are None will be omitted from the return value.
 
+            XXX: flatten
+
             >>> Tag.process_contents([])
             []
             >>> Tag.process_contents([None])
@@ -179,54 +232,6 @@
             # singleton tag
             yield u"<%s%s />" % (self.name, attrs_stuff)
     
-    def render_lines (self, indent=u'\t', newline=u'\n') :
-        """
-            Render full output lines with given newlines
-
-            >>> list(Tag('xx', 'yy').render_lines())
-            [u'<xx>\\n', u'\\tyy\\n', u'</xx>\\n']
-        """
-
-        for line in self.render_raw_lines(indent) :
-            yield line + newline
-
-    def render_unicode (self, **render_opts) :
-        """
-            Render full tag as a single unicode string
-
-            >>> Tag('xx', 'yy').render_unicode()
-            u'<xx>\\n\\tyy\\n</xx>\\n'
-        """
-
-        return "".join(self.render_lines(**render_opts))
-
-    def render_str (self, charset='ascii', **render_opts) :
-        """
-            Render full tag as an encoded string
-
-            >>> Tag('xx', 'yy').render_str()
-            '<xx>\\n\\tyy\\n</xx>\\n'
-        """
-
-        return self.render_unicode(**render_opts).encode(charset)
-
-    def render_out (self, stream, charset, **render_opts) :
-        """
-            Render output into the given stream, encoding using the given charset
-
-            >>> from StringIO import StringIO; buf = StringIO(); Tag('xx', 'yy').render_out(buf, 'ascii'); buf.getvalue()
-            '<xx>\\n\\tyy\\n</xx>\\n'
-        """
-
-        for line in self.render_lines(**render_opts) :
-            stream.write(line.encode(charset))
-    
-    # default output
-    __str__ = render_str
-    __unicode__ = render_unicode
-
-    # default .render method
-    render = render_unicode
 
     def __repr__ (self) :
         return 'Tag(%s)' % ', '.join(
@@ -254,6 +259,71 @@
     def render_raw_lines (self, indent=u'\t') :
         return self.lines
 
+class XHTMLDocument (IRenderable) :
+    """
+        <?xml version="..." encoding="..." ?>
+        <!DOCTYPE ...>
+        
+        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+            <head>
+                ...
+            </head>
+            <body>
+                ...
+            </body>
+        </html>
+    """
+
+    def __init__ (self, 
+        head, body,
+        xml_version='1.0', xml_encoding='utf-8', 
+        doctype='html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"',
+        html_xmlns='http://www.w3.org/1999/xhtml', html_lang='en'
+    ) :
+        # store
+        self.xml_version = xml_version
+        self.xml_encoding = xml_encoding
+        self.doctype = doctype
+        
+        # build the document
+        self.document = Tag('html', **{'xmlns': html_xmlns, 'xml:lang': html_lang})(
+            Tag('head', head),
+            Tag('body', body),
+        )
+
+    def render_raw_lines (self, **render_opts) :
+        """
+            Render the two header lines, and then the document
+        """
+
+        yield '<?xml version="%s" encoding="%s" ?>' % (self.xml_version, self.xml_encoding)
+        yield '<!DOCTYPE %s>' % (self.doctype)
+
+        for line in self.document.render_raw_lines(**render_opts) :
+            yield line
+    
+    def _check_encoding (self, encoding) :
+        if encoding and encoding != self.xml_encoding :
+            raise ValueError("encoding mismatch: %r should be %r" % (encoding, self.xml_encoding)
+
+    def render_str (self, encoding=None, **render_opts) :
+        """
+            Wrap render_str to verify that the right encoding is used
+        """
+
+        self._check_encoding(encoding)
+        
+        return super(XHTMLDocument, self).render_str(self.xml_encoding, **render_opts)
+
+    def render_out (self, stream, encoding=None, **render_opts) :
+        """
+            Wrap render_out to verify that the right encoding is used
+        """
+
+        self._check_encoding(encoding)
+        
+        return super(XHTMLDocument, self).render_out(stream, self.xml_encoding, **render_opts)
+
 class TagFactory (object) :
     """
         Build Tags with names give as attribute names