Saturday, October 25, 2008

haml.py

Combining YAML and (X)HTML via Python gets this script, haml.py. It takes in a specific set of YAML items, then turns it into (X)HTML based on HTTP_ACCEPT.

haml.py

#!/usr/bin/python
import cgi
import cgitb
cgitb.enable()
import sys
import os
import re
sys.path.append('/homes/abstract/nehodges')
import yaml
xhtmlmode = 'application/xhtml+xml' in os.environ['HTTP_ACCEPT']
query = cgi.FieldStorage()


def ErrorString(title, content = []):
ret = """<head>
<title>%s</title>
</head>\n<body\n>""" % cgi.escape(title, 1)
ret += '\n'.join(['<p>%s</p>' % cgi.escape(i, 1) for i in content])
ret += """</body>
</html>\n"""
return ret



def printHamlDocument(content, fname, xhtml):
def getHamlString(st):
st = st.replace("':'", ":")
st = st.replace("'''", "'")
st = st.replace("&", "&quot;")
p = re.compile('<([^<]+)>')
for match in p.finditer(st):
src = match.group()
inner = match.group(1)
parts = inner.partition('::')
if len(parts[2]) > 0: # We have a <..::..> syntax
st = st.replace(src, u'<a href="%s">%s</a>' % (parts[0], parts[2]))
else: # We have a <..> syntax
st = st.replace(src, u'<a href="%s">%s</a>' % (inner, inner))
return st
def printHamlListItem(content, xhtml, depth=1):
ret = ''
if unicode(content) == content or str(content) == content:
ret = '\t<li>%s</li>' % getHamlString(content)
else:
ret = '\t<li>%s</li>' % printHaml(content, xhtml, depth)
return ret
def printHaml(content, xhtml, depth=1):
ret = ''
try:
# For complex objects
if content.has_key('content'):
ret += '<div>\n'
if content.has_key('title'):
ret += '\t<h%d>%s</h%d>\n' % (depth, content['title'], depth)
ret += printHaml(content['content'], xhtml, depth + 1) + '\n'
ret += '</div>\n'
elif content.has_key('image'):
ret += '<img src="%s"' % content['image']
style = ''
if content.has_key('width'):
style += 'width:%s;' % content['width']
if content.has_key('height'):
style += 'height:%s;' % content['height']
if len(style) > 0:
ret += ' style="%s"' % style
ret += (xhtml and ' />' or '>') + '\n'
elif content.has_key('items'):
ret += '<ul>\n%s\n</ul>\n' % '\n'.join([printHamlListItem(i, xhtml, depth + 1) for i in content['items']])
except AttributeError:
if unicode(content) == content or str(content) == content:
# For strings
ret = '<p>%s</p>\n' % getHamlString(content)
else:
# For lists
ret += u'\n'.join([printHaml(i, xhtml, depth) for i in content])
ret += '\n'
return ret
ret = "<head>\n\t<title>%s</title>\n" % (content.has_key('title') and content['title'] or fname)
if content.has_key('style'):
ret += u''.join(('\t<link rel="stylesheet" href="%s"' % content['style'], xhtml and ' />' or '>', '\n'))
ret += "</head>\n"
if content.has_key('content'):
ret += '<body>\n%s</body>' % printHaml(content['content'], xhtml)
elif xhtml:
ret += '<body />\n'
else:
ret += '<body></body>\n'
ret += '</html>\n'
return ret



if xhtmlmode:
print "Content-Type: application/xhtml+xml; charset=utf-8\n"
print """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd" xml:lang="en">
"""
else:
print "Content-Type: text/html; charset=utf-8\n"
print """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en-us">"""
if not query.has_key('file'):
print ErrorString('No Document Provided', ['Not much to see here.']);
elif len(query['file'].value) == 0 or not os.path.isfile(query['file'].value):
print ErrorString('No Such Document', ['Not much to see here'])
else:
fname = query['file'].value
f = open(fname, 'r')
try:
content = yaml.load(f)
# print ErrorString('All is Good!', [cgi.escape(repr(content), 1)])
print printHamlDocument(content, fname, xhtmlmode)
except yaml.YAMLError, e:
print ErrorString('Failed to Load Document', ['Looks like there\'s a problem here.', repr(e)])
finally:
f.close()
Here's a sample YAML file with all of the features:---
title: Foo balls
style: "test-style.css"
content:
- title: Test Section 1
content:
- Some useless content. What'''s new?
- Each paragraph is represented in an item here. Other optional types are':'
- content:
- Sub-divs
- title: Super 2
content:
- Sub-divs with titles
- 'For links, use the syntax. For more advanced links, try .'
- image: 'http://www.google.com/intl/en_ALL/images/logo.gif'
alt: The Google image!
- title: Test Section 2
content:
- Nothing special
- items:
- Don'''t forget an awesome list!
- Can'''t live without lists.
- title: Even Better!
content:
- Divs inside of lists!
- Likely not standard, but oh well.
...

It represents what I started earlier regarding Python object to HTML rendering, but I switched to YAML since those files are quicker to write.

Post a Comment