Instantly share code, notes, and snippets.
Created
May 23, 2014 10:50
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save rmamba/855acd1a68f6dfe2bb81 to your computer and use it in GitHub Desktop.
Based on http://code.activestate.com/recipes/366000/ and modified to be included in py files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cStringIO | |
class TAG: | |
"""Generic class for tags""" | |
def __init__(self, inner_HTML="", **attrs): | |
self.tag = self.__class__.__name__ | |
self.inner_HTML = inner_HTML | |
self.attrs = attrs | |
self.children = [] | |
self.brothers = [] | |
def toString(self): | |
return self.__str__() | |
def __str__(self): | |
res=cStringIO.StringIO() | |
w=res.write | |
if self.tag != "TEXT": | |
w("<%s" %self.tag) | |
# attributes which will produce arg = "val" | |
attr1 = [ k for k in self.attrs | |
if not isinstance(self.attrs[k],bool) ] | |
w("".join([' %s="%s"' | |
%(k.replace('_','-'),self.attrs[k]) for k in attr1])) | |
# attributes with no argument | |
# if value is False, don't generate anything | |
attr2 = [ k for k in self.attrs if self.attrs[k] is True ] | |
w("".join([' %s' %k for k in attr2])) | |
w(">") | |
if self.tag in ONE_LINE: | |
w('\n') | |
w(str(self.inner_HTML)) | |
for child in self.children: | |
w(str(child)) | |
if self.tag in CLOSING_TAGS: | |
w("</%s>" %self.tag) | |
if self.tag in LINE_BREAK_AFTER: | |
w('\n') | |
if hasattr(self,"brothers"): | |
for brother in self.brothers: | |
w(str(brother)) | |
return res.getvalue() | |
def __le__(self,other): | |
"""Add a child""" | |
if isinstance(other,str): | |
other = TEXT(other) | |
self.children.append(other) | |
other.parent = self | |
return self | |
def __add__(self,other): | |
"""Return a new instance : concatenation of self and another tag""" | |
res = TAG() | |
res.tag = self.tag | |
res.inner_HTML = self.inner_HTML | |
res.attrs = self.attrs | |
res.children = self.children | |
res.brothers = self.brothers + [other] | |
return res | |
def __radd__(self,other): | |
"""Used to add a tag to a string""" | |
if isinstance(other,str): | |
return TEXT(other)+self | |
else: | |
raise ValueError,"Can't concatenate %s and instance" %other | |
def __mul__(self,n): | |
"""Replicate self n times, with tag first : TAG * n""" | |
res = TAG() | |
res.tag = self.tag | |
res.inner_HTML = self.inner_HTML | |
res.attrs = self.attrs | |
for i in range(n-1): | |
res += self | |
return res | |
def __rmul__(self,n): | |
"""Replicate self n times, with n first : n * TAG""" | |
return self*n | |
# list of tags, from the HTML 4.01 specification | |
CLOSING_TAGS = ['A', 'ABBR', 'ACRONYM', 'ADDRESS', 'APPLET', | |
'B', 'BDO', 'BIG', 'BLOCKQUOTE', 'BUTTON', | |
'CAPTION', 'CENTER', 'CITE', 'CODE', | |
'DEL', 'DFN', 'DIR', 'DIV', 'DL', | |
'EM', 'FIELDSET', 'FONT', 'FORM', 'FRAMESET', | |
'H1', 'H2', 'H3', 'H4', 'H5', 'H6', | |
'I', 'IFRAME', 'INS', 'KBD', 'LABEL', 'LEGEND', | |
'MAP', 'MENU', 'NOFRAMES', 'NOSCRIPT', 'OBJECT', | |
'OL', 'OPTGROUP', 'PRE', 'Q', 'S', 'SAMP', | |
'SCRIPT', 'SELECT', 'SMALL', 'SPAN', 'STRIKE', | |
'STRONG', 'STYLE', 'SUB', 'SUP', 'TABLE', | |
'TEXTAREA', 'TITLE', 'TT', 'U', 'UL', | |
'VAR', 'BODY', 'COLGROUP', 'DD', 'DT', 'HEAD', | |
'HTML', 'LI', 'P', 'TBODY','OPTION', | |
'TD', 'TFOOT', 'TH', 'THEAD', 'TR'] | |
NON_CLOSING_TAGS = ['AREA', 'BASE', 'BASEFONT', 'BR', 'COL', 'FRAME', | |
'HR', 'IMG', 'INPUT', 'ISINDEX', 'LINK', | |
'META', 'PARAM'] | |
# create the classes | |
for tag in CLOSING_TAGS + NON_CLOSING_TAGS + ['TEXT']: | |
exec("class %s(TAG): pass" %tag) | |
def Sum(iterable): | |
"""Return the concatenation of the instances in the iterable | |
Can't use the built-in sum() on non-integers""" | |
it = [ item for item in iterable ] | |
if it: | |
return reduce(lambda x,y:x+y, it) | |
else: | |
return '' | |
# whitespace-insensitive tags, determines pretty-print rendering | |
LINE_BREAK_AFTER = NON_CLOSING_TAGS + ['HTML','HEAD','BODY', | |
'FRAMESET','FRAME', | |
'TITLE','SCRIPT', | |
'TABLE','TR','TD','TH','SELECT','OPTION', | |
'FORM', | |
'H1', 'H2', 'H3', 'H4', 'H5', 'H6', | |
] | |
# tags whose opening tag should be alone in its line | |
ONE_LINE = ['HTML','HEAD','BODY', | |
'FRAMESET' | |
'SCRIPT', | |
'TABLE','TR','TD','TH','SELECT','OPTION', | |
'FORM', | |
] | |
''' | |
#Usage in usage.py: | |
import HTMLTags | |
if __name__ == '__main__': | |
head = HTMLTags.HEAD(HTMLTags.TITLE('Test document')) | |
body = HTMLTags.BODY() | |
body <= HTMLTags.H1('This is a test document') | |
body <= 'First line' + HTMLTags.BR() + 'Second line' | |
print HTMLTags.HTML(head + body) | |
# OR | |
# html = HTMLTags.HTML(head + body) | |
# file.write( html.toString() ) | |
''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Based on http://code.activestate.com/recipes/366000/
"""Classes to generate HTML in Python
The HTMLTags module defines a class for all the valid HTML tags, written in
uppercase letters. To create a piece of HTML, the general syntax is :
t = TAG(inner_HTML, key1=val1,key2=val2,...)
so that "print t" results in :
<TAG key1="val1" key2="val2" ...>inner_HTML
For instance :
print A('bar', href="foo") ==> bar
To generate HTML attributes without value, give them the value True :
print OPTION('foo',SELECTED=True,value=5) ==>
The inner_HTML argument can be an instance of an HTML class, so that
you can nest tags, like this :
print B(I('foo')) ==> foo
TAG instances support addition :
print B('bar')+INPUT(name="bar") ==> bar
and repetition :
print TH(' ')*3 ==>
For complex expressions, a tag can be nested in another using the operator <=
Considering the HTML document as a tree, this means "add child" :
If you have a list (or any iterable) of instances, you can't concatenate the
items with sum(instance_list) because sum takes only numbers as arguments. So
there is a function called Sum() which will do the same :
generates the rows of a table showing the squares of integers from 0 to 99
A simple document can be produced by :
print HTML( HEAD(TITLE('Test document')) +
BODY(H1('This is a test document')+
'First line'+BR()+
'Second line'))
This will be rendered as :
<TITLE>Test document</TITLE>
This is a test document
First line
Second line
If the document is more complex it is more readable to create the elements
first, then to print the whole result in one instruction. For example :
head = HEAD()
head <= TITLE('Record collection')
head <= LINK(rel="Stylesheet",href="doc.css")
title = H1('My record collection')
table = TABLE()
table <= TR(TH('Title')+TH('Artist'))
for rec in records:
row = TR()
# note the attribute key Class with leading uppercase
# because "class" is a Python keyword
row <= TD(rec.title,Class="title")+TD(rec.artist,Class="artist")
table <= row
print HTML(head+BODY(title+table))
"""