#!/usr/bin/python
"""deelan's XHTML generator
Deex is a little Python module that uses nested function lists to render
sequences of XHTML tags. To use Deex you only need to import a couple of
functions: "render" wich takes a tree of nested functions and render the
corresponding XHTML markup and "curry", used to bind parameter values to
functions.
Visit http://www.deelan.com/dev/deex/ for the latest version and examples
"""
__version__ = "0.9"
__author__ = "Andrea Peltrin (deelan AT interplanet DOT it)"
__copyright__ = "Copyright 2003, Andrea Peltrin"
__license__ = "GPL" # see full license statement below
__history__ = """
0.9 - 2003-12-27 - First public release.
"""
# Copyright (C) 2003 Andrea Peltrin
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# curry class from Python Cookbook, see:
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549/
class curry:
def __init__(self, fun, *args, **kwargs):
self.fun = fun
self.pending = args[:]
self.kwargs = kwargs.copy()
# let's trick Python and pretend object is a *function* object
func_name = property(fget=lambda self: self.fun.func_name)
def __call__(self, *args, **kwargs):
if kwargs and self.kwargs:
kw = self.kwargs.copy()
kw.update(kwargs)
else:
kw = kwargs or self.kwargs
return self.fun(*(self.pending + args), **kw)
# @@ is this list complete?
empty = dict.fromkeys('area base basefont br col frame hr img input isindex link meta param'.split())
# new-line after?
newlined = dict.fromkeys('html head body link meta div ul ol li dl p hr h1 h2 h3 h4 h5 h6 table tr thead tbody tfoot'.split())
from cgi import escape
def render(root):
assert callable(root), 'Root element must be a callable'
# list to accumulate partial strings
s = []
def r_render(node):
if node.__doc__:
s.append('\n' % node.__doc__) #@@ or print doc as is?
# forget everything after the underscore
tag = node.func_name.split('_', 1)[0]
s.append('<%s' % tag)
children = node()
start = 0
try:
#check if object supports items() methods
items = children[0].items()
s.append(' ') # space up a bit (@@ there must be a better way!)
s.append(' '.join(['%s="%s"' % (k, escape(str(v), True)) for k, v in items]))
start = 1 # start offset for children list
except AttributeError:
pass
s.append((tag in empty) and ' />' or '>')
#if tag in newlined:
# s.append('\n') # prettify
for child in children[start:]:
#print '*** got', child
if callable(child): # function?
r_render(child)
elif isinstance(child, (list, tuple)):
for c in child: r_render(c) # iterate over
else:
s.append(child)
# close parent tag
if tag not in empty:
s.append('%s>%s' % (tag, tag in newlined and '\n' or '')) # prettify
return s # handy
# start rendering from root node
return ''.join(r_render(root)) # make a string
if __name__=='__main__':
print __doc__