_html_mapping = ( ("&", "&"), (" ", " "), (">", ">"), ("<", "<"), ('"', """), ) def encode_html(obj): text = str(obj) for chr, enc in _html_mapping: text = text.replace(chr, enc) return text class AttrDict(dict): __getattr__ = dict.__getitem__ __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ __iter__ = dict.iteritems class HElement(object): _name = None _enclosing = True _defaults = {} _pretty = True def __init__(self, *elems, **attrs): if self._name is None: self.name = self.__class__.__name__.lower() else: self.name = self._name if self._enclosing: self.elems = list(elems) elif elems: raise ElementError("element is not a container") self.attrs = AttrDict(self._defaults) self.attrs.update(attrs) def _render(self, nesting, pretty): items = [] if pretty: indent = " " * nesting subindent = " " * (nesting + 1) items.append(indent) items.append("<") items.append(self.name) for k, v in self.attrs: if isinstance(v, bool): if v: items.append(" ") items.append(k) else: items.append(" ") items.append(k) items.append('="') items.append(encode_html(v)) items.append('"') items.append(">") if pretty: items.append("\n") if self._enclosing: for elem in self.elems: if isinstance(elem, HElement): items.extend(elem._render(nesting + 1, pretty)) elif pretty: for line in encode_html(elem).splitlines(): items.append(subindent) items.append(line) items.append("\n") else: items.append(" ".join(encode_html(elem).splitlines())) if pretty: items.append(indent) items.append("</") items.append(self.name) items.append(">") if pretty: items.append("\n") return items def render(self, nesting = 0, pretty = None): if pretty is None: pretty = self._pretty return "".join(self._render(nesting, pretty)).strip() __repr__ = render class SimpleElement(HElement): _enclosing = False # headers class Root(HElement): _name = "html" class Head(HElement): pass class Title(HElement): pass class Meta(HElement): pass class Body(HElement): pass # formatting class Break(SimpleElement): _name = "br" class Para(HElement): _name = "p" class Bold(HElement): _name = "b" class Italics(HElement): _name = "i" class Underline(HElement): _name = "u" class Font(HElement): pass class BulletGroup(HElement): _name = "ul" class Bullet(HElement): _name = "li" class Quote(HElement): _name = "q" class Pre(HElement): pass class Mono(HElement): _name = "tt" # forms class Form(HElement): pass class Field(SimpleElement): _name = "input" class TextField(Field): _defaults = {"type" : "text"} class PasswordField(Field): _defaults = {"type" : "password"} class CheckboxField(Field): _defaults = {"type" : "checkbox"} class RadioButton(Field): _defaults = {"type" : "radio"} class SubmitButton(Field): _defaults = {"type" : "submit"} class ResetButton(Field): _defaults = {"type" : "reset"} class SelectField(HElement): _name = "select" class OptionField(HElement): _name = "option" class TextArea(HElement): pass # tables class Table(HElement): pass class Row(HElement): _name = "tr" class Col(HElement): _name = "td" # test if __name__ == "__main__": f = Form( "some text", Table( Row( Col( "hello moshe", Bold("kaki\npipi"), TextField(name = "moshe"), Break(), "lala", bgcolor = "white"), ), Row( Col( SubmitButton(value = "send") ) ), width = "100%", ), action = "login", method = "post" ) f.elems[1].elems.append("and some more text") f.attrs.method = "get" print f # <form action="login" method="get"> # some text # <table width="100%"> # <tr> # <td bgcolor="white"> # hello moshe # <b> # kaki # pipi # </b> # <input type="text" name="moshe"> # <br> # lala # </td> # </tr> # <tr> # <td> # <input type="submit" value="send"> # </td> # </tr> # and some more text # </table> # </form>