Last active
June 12, 2017 16:15
-
-
Save jstimpfle/99ef75a43fea313a93141d3a11f30e62 to your computer and use it in GitHub Desktop.
Poor man's POWERFUL html templating system for python
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 sys | |
class Element: | |
def __init__(self, name, **attributes): | |
self.name = name | |
self.attributes = attributes | |
self.childs = [] | |
def setAttribute(self, key, val): | |
self.attributes[key] = val | |
def setAttributes(self, **kwargs): | |
for key, val in kwargs.items(): | |
self.attributes[key] = val | |
def appendChild(self, child): | |
self.childs.append(child) | |
def writeHTML(self, out=None): | |
if out is None: | |
out = sys.stdout | |
out.write('<') | |
out.write(self.name) | |
for key, val in self.attributes.items(): | |
# TODO: escaping | |
out.write(' %s="%s"' %(key, val)) | |
out.write('>\n') | |
for child in self.childs: | |
child.writeHTML(out) | |
out.write('</') | |
out.write(self.name) | |
out.write('>\n') | |
def __enter__(self): | |
return NestingElements(self) | |
def __exit__(self, *args): | |
pass | |
class TextNode: | |
def __init__(self, text): | |
self.text = text | |
def writeHTML(self, out): | |
out.write(self.text | |
.replace('&', '&') | |
.replace('<', '<') | |
.replace('>', '>') | |
.replace('"', '"')) | |
def nest(self, nesting): | |
return nest(nesting, self) | |
class NestingElements: | |
def __init__(self, root_elem): | |
self.stack = [root_elem] | |
def get_root_elem(self): | |
assert len(self.stack) >= 1 | |
return self.stack[0] | |
def get_current_elem(self): | |
assert len(self.stack) >= 1 | |
return self.stack[-1] | |
def setAttribute(self, key, val): | |
self.get_current_elem().setAttribute(key, val) | |
def setAttributes(self, **kwargs): | |
self.get_current_elem().setAttributes(**kwargs) | |
def nest(self, elem): | |
return nest(self, elem) | |
def nestNewElem(self, *args, **kwargs): | |
return nest(self, Element(*args, **kwargs)) | |
def addText(self, text): | |
self.get_current_elem().appendChild(TextNode(text)) | |
class nest: | |
def __init__(self, nesting, elem): | |
self.nesting = nesting | |
self.elem = elem | |
self.nesting.stack[-1].appendChild(self.elem) | |
def __enter__(self): | |
self.nesting.stack.append(self.elem) | |
def __exit__(self, *args): | |
top = self.nesting.stack.pop() | |
assert top is self.elem | |
# Code: | |
# with Element('div') as ctx: | |
# with ctx.nestNewElem('ul'): | |
# with ctx.nestNewElem('li', style='border: 1px'): | |
# ctx.addText('Item 1') | |
# with ctx.nestNewElem('li'): | |
# ctx.addText('Item 2') | |
# ctx.get_root_elem().writeHTML() | |
# | |
# Output: | |
# <div> | |
# <ul> | |
# <li> | |
# Item 1</li> | |
# <li> | |
# Item 2</li> | |
# </ul> | |
# </div> | |
# test code... | |
class Person: | |
def __init__(self, firstname, lastname): | |
self.firstname = firstname | |
self.lastname = lastname | |
def comma_separated(elems): | |
out = [] | |
for i, elem in enumerate(elems): | |
if i: | |
out.append(TextNode(', ')) | |
out.append(elem) | |
return out | |
def formatPerson(person): | |
return TextNode('%s %s' %(person.firstname, person.lastname)) | |
persons = [ | |
Person('Joe', 'Doe'), | |
Person('Jane', 'Dane') | |
] | |
with Element('p') as ctx: | |
for elem in comma_separated(formatPerson(p) for p in persons): | |
ctx.nest(elem) | |
ctx.get_root_elem().writeHTML() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment