[XML-SIG] pydom

Greg Stein gstein@lyra.org
Thu, 08 Oct 1998 23:55:17 -0700

This is a multi-part message in MIME format.

Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Jeff.Johnson@stn.siemens.com wrote:
> I use pyhtml.py, which uses pydom, to create my HTML elements because it
> saves lots of lines of code.  It may not be quite so readable but I don't
> think it's that bad.  In fact, I think it's more readable than having to
> read the 10 lines of code resulting from using core.py.  I also miss the
> ability to specify the attribute names and values in the core
> createElement().
> Consider the following using pyhtml.py:
>      html =
> HTML(HEAD(TITLE(self.title)),BODY(list,BACKGROUND=self.background))
> Now using core.py (maybe there is a much easier way to do this with
> core.py?):
> ... lengthy example elided ...

I saw this and got to thinking about some text (XML/HTML) generation. I
looked at pyhtml.py and saw that it only encoded a specific subset of
available tags. "There has got to be a clean way to have an open-ended
system," I thought :-)
[ I'm not familiar with HTMLgen, but I'm copying Robin in case he finds
this interesting... (and his xml-sig delivery is disabled for some
reason) ]

Anyhow, I've attached a quick pass at some code to do (XML) generation.
The above example would be written like this:

import xmlgen
f = xmlgen.Factory()

html = f.html[f.head.title(self.title),

The text is retrieved using str(html).

There are two ways to create an element:
- via the factory "f". f.foo creates and returns a <foo> element
- via another element. elem.foo creates, and inserts into a self, a
<foo> element

Given an element, you can insert 0 or more items of CDATA into it:
elem('piece 1', 'piece 2')

Usually used as: elem.subelem('cdata') or as: f.elem('cdata')

Given an element, you can insert attributes into it:
elem(attr1='value 1', attr2='value 2')

Again, usually used when creating them: elem.subelem(attr1='value1')

Given a number of elements, you can insert 1 or more elements as
children of another using index notation:
elem1[elem2, elem3, elem4]

Finally, when creating a child element, the return value can create a
nested child element:

In this last form, the return value is tricky. If you str() it, then you
get "elem" and its children. Otherwise, it acts like the "subsubelem" --
elem creation, insertion, and CDATA/attribute will operate on
subsubelem. This allows you to do things like:

body =
html = f.html[f.head.title('title'), body]

Some relatively simple rules, some funky code, and hopefully a clear
result for assembling text. Comments welcome :-)


Greg Stein (gstein@lyra.org)

Content-Type: text/plain; charset=us-ascii; name="xmlgen.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="xmlgen.py"

# XML generation module

import string

class Element:

  def __init__(self, name, *cdata, **attrs):
    self.__name = name
    self.__children = []
    self.__cdata = cdata
    self.__attrs = attrs

  def __getattr__(self, name):
    if name[:2] == '__':
      raise AttributeError, name
    child = Element(name)
    return Intermediate(self, child)

  def __call__(self, *cdata, **attrs):
    self.__cdata = self.__cdata + cdata
    return self

  def __getitem__(self, items):
    if type(items) == type(()) or type(items) == type([]):
      self.__children = self.__children + list(items)
    return self

  def __str__(self):
    s = '<' + self.__name
    for name, value in self.__attrs.items():
      s = s + ' ' + name + '="' + str(value) + '"'
    if self.__cdata or self.__children:
      s = s + '>' + string.joinfields(self.__cdata, '')
      for child in self.__children:
        s = s + str(child)
      s = s + '</' + self.__name + '>'
      s = s + '/>'
    return s

class Factory:
  def __getattr__(self, name):
    if name[:2] == '__':
      raise AttributeError, name
    return Element(name)

class Intermediate:
  def __init__(self, parent, child):
    self.__parent = parent
    self.__child = child

  def __getattr__(self, name):
    inter = getattr(self.__child, name)
    return Intermediate(self.__parent, inter.__child)

  def __call__(self, *cdata, **attrs):
    apply(self.__child, cdata, attrs)
    return self

  def __getitem__(self, items):
    return self

  def __str__(self):
    return str(self.__parent)

def test():
  f = Factory()
  l = f.ul[f.li('list 1'), f.li('list 2'), f.li('list 3')]
  l[f.li('list 4')]
  top = f.html[f.head.title.i('title'), f.body(bgcolor='#ffffff')[f.h1('heading'),f.p('text'),l]]
  print top