created cybertools.xml package

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1477 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2006-11-06 20:46:40 +00:00
parent 97a4f080e4
commit 589238c76f
5 changed files with 368 additions and 0 deletions

94
xml/README.txt Normal file
View file

@ -0,0 +1,94 @@
XML (and XHTML) Generation
==========================
The elements generator lets you easily create snippets of XML or XHTML:
>>> from cybertools.xml.element import elements as e
>>> doc = e.html(
... e.head(e.title(u'Page Title')),
... e.body(
... e.div(u'The top bar', class_=u'top'),
... e.div(u'The body stuff', class_=u'body'),
... ))
>>> print doc.render()
<html>
<head>
<title>
Page Title
</title>
</head>
<body>
<div class="top">
The top bar
</div>
<div class="body">
The body stuff
</div>
</body>
</html>
An XML element thus created may be converted to an ElementTree:
>>> doc.renderTree()
'<html><head><title>Page Title</title></head><body><div class="top">The top bar</div><div class="body">The body stuff</div></body></html>'
>>> tree = doc.makeTree()
>>> tree.findtext('head/title')
'Page Title'
>>> xml = ('<html><head><title>Page Title</title></head><body>'
... '<div class="top">The top bar</div>'
... '<div class="body">The body stuff</div></body></html>')
>>> from cybertools.xml.element import fromXML
>>> doc = fromXML(xml)
>>> print doc.render()
<html>
<head>
<title>
Page Title
</title>
</head>
<body>
<div class="top">
The top bar
</div>
<div class="body">
The body stuff
</div>
</body>
</html>
Alternative Notation
--------------------
We can also create such a structure by successively adding elements
just by accessing an element's attributes:
>>> doc = e.html
>>> dummy = doc.head.title(u'Page Title')
>>> body = doc.body
>>> div1 = body.div(u'The top bar', class_=u'top')
>>> div2 = body.div(u'The body stuff', class_=u'body')
>>> print doc.render()
<html>
<head>
<title>
Page Title
</title>
</head>
<body>
<div class="top">
The top bar
</div>
<div class="body">
The body stuff
</div>
</body>
</html>
>>> for text in (u'Welcome', u'home'):
... p = div2.p(text, style='font-size: 80%;')
>>> print doc.render()
<html>...<p...>...</p>...

4
xml/__init__.py Normal file
View file

@ -0,0 +1,4 @@
# makes this directory a package
#
# $Id$
#

109
xml/element.py Normal file
View file

@ -0,0 +1,109 @@
"""
Generation and manipulation of XML trees.
$Id$
"""
from cStringIO import StringIO
from lxml import etree
class Generator(object):
def __getitem__(self, name):
return Element(etree.Element(name))
def __getattr__(self, name):
return self[name]
elements = Generator()
class Element(object):
def __init__(self, baseElement):
self.baseElement = baseElement
@property
def __name__(self):
return self.baseElement.tag
@property
def children(self):
base = self.baseElement
result = []
if base.text:
result.append(base.text)
for c in base.getchildren():
result.append(Element(c))
if base.tail:
result.append(base.tail)
return result
@property
def attributes(self):
return self.baseElement.attrib
def __getattr__(self, name):
if name.endswith('_'):
name = name[:-1]
return self[name]
def __getitem__(self, name):
elem = etree.Element(name)
self.baseElement.append(elem)
return Element(elem)
def __call__(self, *children, **attributes):
base = self.baseElement
for c in children:
if isinstance(c, Element):
base.append(c.baseElement)
elif not base:
base.text = base.text and '\n'.join(base.text, c) or c
else:
base.tail = base.tail and '\n'.join(base.tail, c) or c
for a in attributes:
if a.endswith('_'):
attr = a[:-1]
attributes[attr] = attributes[a]
del attributes[a]
for a in attributes:
base.attrib[a] = attributes[a]
return self
def render(self, level=0):
out = StringIO()
out.write(' ' * level)
out.write('<' + self.__name__)
for a in self.attributes:
attr = a
if attr.endswith('_'):
attr = attr[:-1]
out.write(' %s="%s"' % (attr, self.attributes[a]))
out.write('>\n')
for e in self.children:
if isinstance(e, Element):
out.write(e.render(level+1))
else:
out.write(' ' * (level+1))
out.write(e)
out.write('\n')
out.write(' ' * level)
out.write('</%s>' % self.__name__)
out.write('\n')
return out.getvalue()
def makeTree(self):
return etree.ElementTree(self.baseElement)
def renderTree(self):
out = StringIO()
tree = self.makeTree()
tree.write(out)
return out.getvalue()
def fromXML(xml):
elem = etree.XML(xml)
return Element(elem)

112
xml/element.sav.py Normal file
View file

@ -0,0 +1,112 @@
"""
Generation and manipulation of XML trees.
$Id$
"""
from cStringIO import StringIO
from lxml import etree
class Generator(object):
def __getitem__(self, name):
return Element(name)
def __getattr__(self, name):
return self[name]
elements = Generator()
class Element(object):
def __init__(self, name):
self.__name__ = name
self.children = []
self.attributes = {}
def __getattr__(self, name):
if name.endswith('_'):
name = name[:-1]
return self[name]
def __getitem__(self, name):
el = Element(name)
self.children.append(el)
return el
def __call__(self, *children, **attributes):
self.children.extend(list(children))
for a in attributes:
if a.endswith('_'):
attr = a[:-1]
attributes[attr] = attributes[a]
del attributes[a]
self.attributes.update(attributes)
return self
def render(self, level=0):
out = StringIO()
out.write(' ' * level)
out.write('<' + self.__name__)
for a in self.attributes:
attr = a
if attr.endswith('_'):
attr = attr[:-1]
out.write(' %s="%s"' % (attr, self.attributes[a]))
out.write('>\n')
for e in self.children:
if isinstance(e, Element):
out.write(e.render(level+1))
else:
out.write(' ' * (level+1))
out.write(e)
out.write('\n')
out.write(' ' * level)
out.write('</%s>' % self.__name__)
out.write('\n')
return out.getvalue()
def makeTree(self):
elem = etree.Element(self.__name__)
makeSubTree(elem, self)
return etree.ElementTree(elem)
def renderTree(self):
out = StringIO()
tree = self.makeTree()
tree.write(out)
return out.getvalue()
def makeSubTree(elem, content):
for a in content.attributes:
elem.set(a, content.attributes[a])
subElem = None
for c in content.children:
if isinstance(c, Element):
subElem = etree.SubElement(elem, c.__name__)
makeSubTree(subElem, c)
elif subElem is None:
elem.text = elem.text and '\n'.join(elem.text, c) or c
else:
subElem.tail = subElem.tail and '\n'.join(subElem.tail, c) or c
def getElementsFromTree(elem):
content = Element(elem.tag)
for key, value in elem.items():
content.attributes[key] = value
if elem.text:
content.children.append(elem.text)
for child in elem.getchildren():
content.children.append(getElementsFromTree(child))
if child.tail:
content.children.append(child.tail)
return content
def fromXML(xml):
elem = etree.XML(xml)
return getElementsFromTree(elem)

49
xml/tests.py Executable file
View file

@ -0,0 +1,49 @@
#! /usr/bin/python
"""
Tests for the 'cyberdev.xml' package.
$Id$
"""
import unittest, doctest
from zope.testing.doctestunit import DocFileSuite
from cStringIO import StringIO
from cybertools.xml.element import elements as e, fromXML
class TestXml(unittest.TestCase):
"Basic tests for the xml package."
baseHtml = e.html(
e.head(e.title(u'Page Title')),
e.body(
e.div(u'The top bar', class_='top'),
e.div(u'The body stuff', class_='body'),
))
def testBasicStuff(self):
doc = self.baseHtml
tree = doc.makeTree()
out = StringIO()
tree.write(out)
text = out.getvalue()
def testParsing(self):
xml = ('<html><head><title>Page Title</title></head>'
'<body><div class="top">The top bar</div>'
'<div class="body">The body stuff</div></body></html>')
doc = fromXML(xml)
text = doc.render()
def test_suite():
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
return unittest.TestSuite((
DocFileSuite('README.txt', optionflags=flags),
unittest.makeSuite(TestXml),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')