00001
00002 from elementtree.SimpleXMLWriter import XMLWriter
00003 from cStringIO import StringIO
00004
00005 class _XmlElement(list):
00006 def __init__(self, tag):
00007 self.tag = tag
00008 self.text = None
00009 self.attrib = {}
00010
00011 def __call__(self, text='', **kwargs):
00012 self.text = text
00013 self.attrib = kwargs
00014 return self
00015
00016 def __repr__(self):
00017 return "<_XmlElement %s>" % self.tag
00018
00019 def append(self, value):
00020 list.append(self, value)
00021 return value
00022
00023 def __getattr__(self, value):
00024 if value == "add_comment":
00025 return self.append( _XmlComment() )
00026 elif value == "add_text":
00027 return self.append( _XmlText() )
00028 else:
00029 return self.append( _XmlElement(value) )
00030
00031 def _write(self, writer):
00032
00033
00034 class_ = None
00035 for key in self.attrib.iterkeys():
00036 if key == 'class_':
00037 class_ = self.attrib[key]
00038 del self.attrib[key]
00039 break
00040 if class_ is not None:
00041 self.attrib['class'] = class_
00042
00043 if not self.__len__() and not self.text:
00044 writer.element( self.tag, None, self.attrib )
00045 else:
00046 writer.start(self.tag, self.attrib)
00047 if self.text is not None:
00048 writer.data(self.text)
00049
00050 for node in self:
00051 node._write(writer)
00052 writer.end()
00053
00054 class _XmlText(_XmlElement):
00055
00056 def __init__(self):
00057 self.text = ''
00058
00059 def __call__(self, text=''):
00060 self.text = text
00061 return self
00062
00063 def _write(self, writer):
00064 writer.data(self.text)
00065 for node in self:
00066 node._write(writer)
00067
00068 def __repr__(self):
00069 return "<_XmlText>"
00070
00071 class _XmlComment(_XmlText):
00072
00073 def _write(self, writer):
00074 writer.comment(self.text)
00075
00076 def __repr__(self):
00077 return "<_XmlComment>"
00078
00079 class XmlFragment(_XmlElement):
00080 '''
00081 >>> xml = XmlFragment()
00082 >>> root = xml.div()
00083 >>> print root
00084 <_XmlElement div>
00085 >>> print xml
00086 <div />
00087 >>> firstchild = root.p("Some text", id="1")
00088 >>> print xml
00089 <div><p id="1">Some text</p></div>
00090 >>> firstchild.br()
00091 <_XmlElement br>
00092 >>> print xml
00093 <div><p id="1">Some text<br /></p></div>
00094 >>> firstchild.add_text("more text, ")
00095 <_XmlText>
00096 >>> print xml
00097 <div><p id="1">Some text<br />more text, </p></div>
00098 >>> firstchild.add_text("and more.").br()
00099 <_XmlElement br>
00100 >>> print xml
00101 <div><p id="1">Some text<br />more text, and more.<br /></p></div>
00102 >>> root.add_comment("COMMENT")
00103 <_XmlComment>
00104 >>> print xml
00105 <div><p id="1">Some text<br />more text, and more.<br /></p><!-- COMMENT -->
00106 </div>
00107 '''
00108 def __init__(self, encoding="utf-8"):
00109 self.encoding = encoding
00110
00111 def _write(self, writer):
00112 for node in self:
00113 node._write(writer)
00114
00115 def write(self, outfile):
00116 w = XMLWriter(outfile, self.encoding)
00117 self._write( w )
00118
00119 def __str__(self):
00120 s = StringIO()
00121 self.write( s )
00122 ret = s.getvalue()
00123 s.close()
00124 return ret
00125
00126 class MissingDocumentRootException(Exception): pass
00127
00128 class XmlDocument(XmlFragment):
00129 '''
00130 >>> xml = XmlDocument("us-ascii")
00131 >>> print xml
00132 Traceback (most recent call last):
00133 ...
00134 MissingDocumentRootException
00135 >>> root = xml.items(catalog="R&D")
00136 >>> print xml
00137 <?xml version='1.0'?>
00138 <items catalog="R&D" />
00139 >>> root.item("Item 1", id="1")
00140 <_XmlElement item>
00141 >>> root.item("Item 2", id="2")
00142 <_XmlElement item>
00143 >>> print xml
00144 <?xml version='1.0'?>
00145 <items catalog="R&D"><item id="1">Item 1</item><item id="2">Item 2</item></items>
00146 '''
00147 def _write(self, writer):
00148 if not len(self):
00149 raise MissingDocumentRootException
00150 writer.declaration()
00151 for node in self:
00152 node._write(writer)
00153
00154 def _test():
00155 import doctest
00156 doctest.testmod()
00157
00158 if __name__ == '__main__':
00159 _test()