""" Kind of like htmlgen, only much simpler. The only important symbol that is exported is ``html``. This builds ElementTree nodes, but with some extra useful methods. (Open issue: should it use ``ElementTree`` more, and the raw ``Element`` stuff less?) You create tags with attribute access. I.e., the ``A`` anchor tag is ``html.a``. The attributes of the HTML tag are done with keyword arguments. The contents of the tag are the non-keyword arguments (concatenated). You can also use the special ``c`` keyword, passing a list, tuple, or single tag, and it will make up the contents (this is useful because keywords have to come after all non-keyword arguments, which is non-intuitive). Or you can chain them, adding the keywords with one call, then the body with a second call, like:: >>> print html.a(href='http://yahoo.com')('') <Yahoo> Note that strings will be quoted; only tags given explicitly will remain unquoted. If the value of an attribute is None, then no attribute will be inserted. So:: >>> print html.a(href='http://www.yahoo.com', name=None, ... c='Click Here') Click Here If the value is None, then the empty string is used. Otherwise str() is called on the value. ``html`` can also be called, and it will produce a special list from its arguments, which adds a ``__str__`` method that does ``html.str`` (which handles quoting, flattening these lists recursively, and using '' for ``None``). ``html.comment`` will generate an HTML comment, like ``html.comment('comment text')`` -- note that it cannot take keyword arguments (because they wouldn't mean anything). Examples:: >>> print html.html( ... html.head(html.title(\"Page Title\")), ... html.body( ... bgcolor='#000066', ... text='#ffffff', ... c=[html.h1('Page Title'), ... html.p('Hello world!')], ... )) Page Title

Page Title

Hello world!

>>> print html.a(href='#top')('return to top') return to top """ from cgi import escape try: import xml.etree.ElementTree as ET except ImportError: # Python < 2.5 import elementtree.ElementTree as ET __all__ = ['html'] default_encoding = 'utf-8' class _HTML: def __getattr__(self, attr): if attr.startswith('_'): raise AttributeError attr = attr.lower() if attr.endswith('_'): attr = attr[:-1] if attr.find('__') != -1: attr = attr.replace('__', ':') if attr == 'comment': return Element(ET.Comment, {}) else: return Element(attr, {}) def __call__(self, *args): return ElementList(args) def quote(self, arg): if arg is None: return '' return escape(unicode(arg).encode(default_encoding), 1) def str(self, arg, encoding=None): if isinstance(arg, str): return arg elif arg is None: return '' elif isinstance(arg, unicode): return arg.encode(default_encoding) elif isinstance(arg, (list, tuple)): return ''.join(map(self.str, arg)) elif isinstance(arg, Element): return str(arg) else: return unicode(arg).encode(default_encoding) html = _HTML() class Element(ET._ElementInterface): def __call__(self, *args, **kw): el = self.__class__(self.tag, self.attrib) if 'c' in kw: if args: raise ValueError( "You may either provide positional arguments or a " "'c' keyword argument, but not both") args = kw.pop('c') if not isinstance(args, (list, tuple)): args = (args,) for name, value in kw.items(): if value is None: del kw[name] continue kw[name] = unicode(value) if name.endswith('_'): kw[name[:-1]] = value del kw[name] if name.find('__') != -1: new_name = name.replace('__', ':') kw[new_name] = value del kw[name] el.attrib.update(kw) el.text = self.text last = None for item in self.getchildren(): last = item el.append(item) for arg in flatten(args): if arg is None: continue if not ET.iselement(arg): if last is None: if el.text is None: el.text = unicode(arg) else: el.text += unicode(arg) else: if last.tail is None: last.tail = unicode(arg) else: last.tail += unicode(arg) else: last = arg el.append(last) return el def __str__(self): return ET.tostring(self, default_encoding) def __unicode__(self): # This is lame! return str(self).decode(default_encoding) def __repr__(self): content = str(self) if len(content) > 25: content = repr(content[:25]) + '...' else: content = repr(content) return '' % content class ElementList(list): def __str__(self): return html.str(self) def __repr__(self): return 'ElementList(%s)' % list.__repr__(self) def flatten(items): for item in items: if isinstance(item, (list, tuple)): for sub in flatten(item): yield sub else: yield item