15 Render the indented lines for tag and contents, without newlines |
17 Render the indented lines for tag and contents, without newlines |
16 """ |
18 """ |
17 |
19 |
18 abstract |
20 abstract |
19 |
21 |
|
22 def render_lines (self, indent=u'\t', newline=u'\n') : |
|
23 """ |
|
24 Render full output lines with given newlines |
|
25 |
|
26 >>> list(Tag('xx', 'yy').render_lines()) |
|
27 [u'<xx>\\n', u'\\tyy\\n', u'</xx>\\n'] |
|
28 """ |
|
29 |
|
30 for line in self.render_raw_lines(indent) : |
|
31 yield line + newline |
|
32 |
|
33 def render_unicode (self, **render_opts) : |
|
34 """ |
|
35 Render full tag as a single unicode string |
|
36 |
|
37 >>> Tag('xx', 'yy').render_unicode() |
|
38 u'<xx>\\n\\tyy\\n</xx>\\n' |
|
39 """ |
|
40 |
|
41 return "".join(self.render_lines(**render_opts)) |
|
42 |
|
43 def render_str (self, encoding='ascii', **render_opts) : |
|
44 """ |
|
45 Render full tag as an encoded string |
|
46 |
|
47 >>> Tag('xx', 'yy').render_str() |
|
48 '<xx>\\n\\tyy\\n</xx>\\n' |
|
49 """ |
|
50 |
|
51 return self.render_unicode(**render_opts).encode(encoding) |
|
52 |
|
53 def render_out (self, stream, encoding, **render_opts) : |
|
54 """ |
|
55 Render output into the given stream, encoding using the given encoding |
|
56 |
|
57 >>> from StringIO import StringIO; buf = StringIO(); Tag('xx', 'yy').render_out(buf, 'ascii'); buf.getvalue() |
|
58 '<xx>\\n\\tyy\\n</xx>\\n' |
|
59 """ |
|
60 |
|
61 for line in self.render_lines(**render_opts) : |
|
62 stream.write(line.encode(encoding)) |
|
63 |
|
64 # default output |
|
65 __str__ = render_str |
|
66 __unicode__ = render_unicode |
|
67 |
|
68 # default .render method |
|
69 render = render_unicode |
|
70 |
20 class Tag (IRenderable) : |
71 class Tag (IRenderable) : |
21 """ |
72 """ |
22 A HTML tag, with attributes and contents, which can a mixture of data and other renderables(tags). |
73 A HTML tag, with attributes and contents, which can a mixture of data and other renderables(tags). |
23 |
74 |
24 Provides various kinds of rendering output |
75 Provides various kinds of rendering output |
28 def process_contents (contents) : |
79 def process_contents (contents) : |
29 """ |
80 """ |
30 Postprocess contents iterable to return new list. |
81 Postprocess contents iterable to return new list. |
31 |
82 |
32 Items that are None will be omitted from the return value. |
83 Items that are None will be omitted from the return value. |
|
84 |
|
85 XXX: flatten |
33 |
86 |
34 >>> Tag.process_contents([]) |
87 >>> Tag.process_contents([]) |
35 [] |
88 [] |
36 >>> Tag.process_contents([None]) |
89 >>> Tag.process_contents([None]) |
37 [] |
90 [] |
177 |
230 |
178 else : |
231 else : |
179 # singleton tag |
232 # singleton tag |
180 yield u"<%s%s />" % (self.name, attrs_stuff) |
233 yield u"<%s%s />" % (self.name, attrs_stuff) |
181 |
234 |
182 def render_lines (self, indent=u'\t', newline=u'\n') : |
|
183 """ |
|
184 Render full output lines with given newlines |
|
185 |
|
186 >>> list(Tag('xx', 'yy').render_lines()) |
|
187 [u'<xx>\\n', u'\\tyy\\n', u'</xx>\\n'] |
|
188 """ |
|
189 |
|
190 for line in self.render_raw_lines(indent) : |
|
191 yield line + newline |
|
192 |
|
193 def render_unicode (self, **render_opts) : |
|
194 """ |
|
195 Render full tag as a single unicode string |
|
196 |
|
197 >>> Tag('xx', 'yy').render_unicode() |
|
198 u'<xx>\\n\\tyy\\n</xx>\\n' |
|
199 """ |
|
200 |
|
201 return "".join(self.render_lines(**render_opts)) |
|
202 |
|
203 def render_str (self, charset='ascii', **render_opts) : |
|
204 """ |
|
205 Render full tag as an encoded string |
|
206 |
|
207 >>> Tag('xx', 'yy').render_str() |
|
208 '<xx>\\n\\tyy\\n</xx>\\n' |
|
209 """ |
|
210 |
|
211 return self.render_unicode(**render_opts).encode(charset) |
|
212 |
|
213 def render_out (self, stream, charset, **render_opts) : |
|
214 """ |
|
215 Render output into the given stream, encoding using the given charset |
|
216 |
|
217 >>> from StringIO import StringIO; buf = StringIO(); Tag('xx', 'yy').render_out(buf, 'ascii'); buf.getvalue() |
|
218 '<xx>\\n\\tyy\\n</xx>\\n' |
|
219 """ |
|
220 |
|
221 for line in self.render_lines(**render_opts) : |
|
222 stream.write(line.encode(charset)) |
|
223 |
|
224 # default output |
|
225 __str__ = render_str |
|
226 __unicode__ = render_unicode |
|
227 |
|
228 # default .render method |
|
229 render = render_unicode |
|
230 |
235 |
231 def __repr__ (self) : |
236 def __repr__ (self) : |
232 return 'Tag(%s)' % ', '.join( |
237 return 'Tag(%s)' % ', '.join( |
233 [ |
238 [ |
234 repr(self.name) |
239 repr(self.name) |
252 self.lines = [line] |
257 self.lines = [line] |
253 |
258 |
254 def render_raw_lines (self, indent=u'\t') : |
259 def render_raw_lines (self, indent=u'\t') : |
255 return self.lines |
260 return self.lines |
256 |
261 |
|
262 class XHTMLDocument (IRenderable) : |
|
263 """ |
|
264 <?xml version="..." encoding="..." ?> |
|
265 <!DOCTYPE ...> |
|
266 |
|
267 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
|
268 <head> |
|
269 ... |
|
270 </head> |
|
271 <body> |
|
272 ... |
|
273 </body> |
|
274 </html> |
|
275 """ |
|
276 |
|
277 def __init__ (self, |
|
278 head, body, |
|
279 xml_version='1.0', xml_encoding='utf-8', |
|
280 doctype='html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"', |
|
281 html_xmlns='http://www.w3.org/1999/xhtml', html_lang='en' |
|
282 ) : |
|
283 # store |
|
284 self.xml_version = xml_version |
|
285 self.xml_encoding = xml_encoding |
|
286 self.doctype = doctype |
|
287 |
|
288 # build the document |
|
289 self.document = Tag('html', **{'xmlns': html_xmlns, 'xml:lang': html_lang})( |
|
290 Tag('head', head), |
|
291 Tag('body', body), |
|
292 ) |
|
293 |
|
294 def render_raw_lines (self, **render_opts) : |
|
295 """ |
|
296 Render the two header lines, and then the document |
|
297 """ |
|
298 |
|
299 yield '<?xml version="%s" encoding="%s" ?>' % (self.xml_version, self.xml_encoding) |
|
300 yield '<!DOCTYPE %s>' % (self.doctype) |
|
301 |
|
302 for line in self.document.render_raw_lines(**render_opts) : |
|
303 yield line |
|
304 |
|
305 def _check_encoding (self, encoding) : |
|
306 if encoding and encoding != self.xml_encoding : |
|
307 raise ValueError("encoding mismatch: %r should be %r" % (encoding, self.xml_encoding) |
|
308 |
|
309 def render_str (self, encoding=None, **render_opts) : |
|
310 """ |
|
311 Wrap render_str to verify that the right encoding is used |
|
312 """ |
|
313 |
|
314 self._check_encoding(encoding) |
|
315 |
|
316 return super(XHTMLDocument, self).render_str(self.xml_encoding, **render_opts) |
|
317 |
|
318 def render_out (self, stream, encoding=None, **render_opts) : |
|
319 """ |
|
320 Wrap render_out to verify that the right encoding is used |
|
321 """ |
|
322 |
|
323 self._check_encoding(encoding) |
|
324 |
|
325 return super(XHTMLDocument, self).render_out(stream, self.xml_encoding, **render_opts) |
|
326 |
257 class TagFactory (object) : |
327 class TagFactory (object) : |
258 """ |
328 """ |
259 Build Tags with names give as attribute names |
329 Build Tags with names give as attribute names |
260 """ |
330 """ |
261 |
331 |