emitting HTML and XML in Python

Kragen Sitaker kragen at dnaco.net
Tue Sep 26 23:12:00 CEST 2000

Hoping this little module will be useful to someone else:

# Define a little bit of XHTML-compatible XML tag syntax for Python.
# See the test() definition at the bottom for some example usage.

# I wrote this because John Klassa complained that he couldn't find an
# equivalent to CGI.pm in Python, just "a small module that'll grab
# CGI parameters for you, and then another huge one that very clumsily
# lets you emit HTML."  So here's a tiny module that very agilely
# lets you emit HTML.  :)

# I hereby disclaim any copyright interest in this work, my intent in
# so doing being to thereby put it in the public domain.
# Kragen Sitaker, 2000-09-26

# turn a dictionary into a string formatted with HTML/XML attribute syntax
# XXX: doesn't turn embedded quotes in values into entities
# (&quot; or whatever) and likewise doesn't turn <>& into their
# equivalent entities.  You have to do that yourself.  Also won't check
# to see if you're passing it keys that aren't valid XML attribute names.
def attributes(args):
    rv = ""
    for key in args.keys(): rv = rv + (' %s="%s"' % (key, args[key]))
    return rv

# Somewhat misnamed; put the end on a tag, given that its content is 'text'.
# Empty tags (e.g. <br />) are specified with content of None; tags with no
# content (which are equivalent from XML's point of view, but not from the
# point of view of existing HTML parsers) are specified with content of ''.
# Empty tags (e.g. <br />) have a space inserted before the slash to keep
# most HTML parsers happily ignorant of the complexities of XML.
def closetag(tagname, text):
    if text is None: return ' />'
    else: return ">%s</%s>" % (text, tagname)

# Call this to emit a tag with specified content and attributes.
# Doesn't encode what you pass to it in any way; do that yourself if
# you need it.

# We have to create a class rather than using a lambda so that we can
# have both keyword arguments and data from the creation context.
class Tag:
    def __init__(self, tagname): self.tagname = tagname
    def __call__(self, text=None, **args):
        return ("<" + self.tagname + attributes(args) +
                closetag(self.tagname, text))

# Convenience class so you can say Xml().foo instead of Tag("foo").
class Xml: def __getattr__(self, name): return Tag(name)
# Convenience instance so you can say xml.foo instead of Xml().foo
xml = Xml()

# Declare an exception to signal test failure.
TestFailure = "test failure"

def stringtest(expected, got):
    if expected != got: raise TestFailure, "%s != %s" % (expected, got)

# Example usage; if this routine doesn't throw an exception, it presumably
# means that this module works.
def test():
    stringtest(xml.b(''), '<b></b>')
    stringtest(xml.a('hello', href='http://www.oclc.org/'),
               '<a href="http://www.oclc.org/">hello</a>')
    stringtest(xml.br(), '<br />')
    stringtest(xml.img(src="about:logo"), '<img src="about:logo" />')
    stringtest(xml.small(xml.b('boo')), '<small><b>boo</b></small>')

<kragen at pobox.com>       Kragen Sitaker     <http://www.pobox.com/~kragen/>
Perilous to all of us are the devices of an art deeper than we ourselves
                -- Gandalf the Grey [J.R.R. Tolkien, "Lord of the Rings"]

More information about the Python-list mailing list