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:
parent
97a4f080e4
commit
589238c76f
5 changed files with 368 additions and 0 deletions
94
xml/README.txt
Normal file
94
xml/README.txt
Normal 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
4
xml/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
# makes this directory a package
|
||||
#
|
||||
# $Id$
|
||||
#
|
109
xml/element.py
Normal file
109
xml/element.py
Normal 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
112
xml/element.sav.py
Normal 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
49
xml/tests.py
Executable 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')
|
Loading…
Add table
Reference in a new issue