Welcome, guest | Sign In | My Account | Store | Cart

HTML-element tree declaration that emits nice HTML

Python, 182 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
_html_mapping = (
    ("&", "&"),
    ("  ", "  "),
    (">", ">"),
    ("<", "&lt;"),
    ('"', "&quot;"),
)

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>

I'm sure there are plenty of libraries like that out there, but for people who only want a clean way to generate HTML, there's no need for power tools. This is a straight-forward, magic-free and fast HTML generator. No validation is done, so you can generate crazy HTML like , this library doesn't care. It only generates HTML for you -- it's up to the browser to chew on it.

Can be combined with templite (also in the cookbook) for inline html-generation.

Created by tomer filiba on Fri, 26 May 2006 (PSF)
Python recipes (4591)
tomer filiba's recipes (12)

Required Modules

  • (none specified)

Other Information and Tasks