mirror of
https://github.com/djohnlewis/stackdump
synced 2025-01-22 22:51:36 +00:00
191 lines
5.7 KiB
Python
191 lines
5.7 KiB
Python
"""
|
|
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>')
|
|
<a href=\"http://yahoo.com\"><Yahoo></a>
|
|
|
|
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')
|
|
<a href=\"http://www.yahoo.com\">Click Here</a>
|
|
|
|
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!')],
|
|
... ))
|
|
<html><head><title>Page Title</title></head><body bgcolor=\"#000066\" text=\"#ffffff\"><h1>Page Title</h1><p>Hello world!</p></body></html>
|
|
>>> print html.a(href='#top')('return to top')
|
|
<a href=\"#top\">return to top</a>
|
|
|
|
"""
|
|
|
|
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 '<Element %r>' % 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
|