HElement May 30, 2006
HElement, or HTML-element, is a simple, straight-forward, and non-magical HTML generation engine.
An HElement is made of two parts: attrs
- the attributes of the tag, and elems
- the
sub-elements of the tag. Sub-elements can be either HElements
by themselves, or plain objects
whose str()
is used to embed them into the enclosing element.
No validations are performed on the structure – it’s up to you to create something the browser
can chew. If you want to do Table(Html(TextField(name = "blah")))
, be my guest.
Both pretty (indentation and newlines) and compact HTML can be generated.
Code
#
# module helement.py
# requires the AttrDict recipe
#
from AttrDict import AttrDict
class HElementError ( Exception ):
pass
_html_mapping = (
( "&" , "&" ),
( " " , " " ),
( ">" , ">" ),
( "<" , "<" ),
( '"' , """ ),
)
def encode_html ( obj ):
text = str ( obj )
for chr , enc in _html_mapping :
text = text . replace ( chr , enc )
return text
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 )
AttrDict . update ( self . attrs , 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 ))
def __repr__ ( self ):
return self . render ( pretty = False )
__str__ = 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"
Exampel
>>> 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
<form action="login" method="post">some text<table width="100%"><tr><td bgcolo
r="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></table></form>
>>> print f
<form action="login" method="post">
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>
</table>
</form>
>>> 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>