1 #!/usr/bin/env python
\r
2 #-------------------------------------------------------------------------------
\r
3 from __future__ import with_statement
\r
4 #-------------------------------------------------------------------------------
\r
5 from xml.etree.ElementTree import TreeBuilder,tostring
\r
6 #-------------------------------------------------------------------------------
\r
7 __all__ = ["XMLBuilder"]
\r
9 XMLBuilder is simple library build on top of ElementTree.TreeBuilder to
\r
10 simplify xml files creation as much as possible. Althow it can produce
\r
11 structured result with identated child tags. `XMLBuilder` use python `with`
\r
12 statement to define xml tag levels and `<<` operator for simple cases -
\r
13 text and tag without childs.
\r
15 from __future__ import with_statement
\r
16 from xmlbuilder import XMLBuilder
\r
17 x = XMLBuilder(format=True)
\r
20 [x << ('node',{'val':i}) for i in range(10)]
\r
25 #-------------------------------------------------------------------------------
\r
26 class _XMLNode(object):
\r
27 """Class for internal usage"""
\r
28 def __init__(self,parent,name,builder):
\r
29 self.builder = builder
\r
33 self.entered = False
\r
34 self.parent = parent
\r
35 def __call__(self,*dt,**mp):
\r
38 self.builder.data(text)
\r
40 self.text.append(text)
\r
42 raise ValueError("Can't add attributes to already opened element")
\r
43 smp = dict((k,str(v)) for k,v in mp.items())
\r
44 self.attrs.update(smp)
\r
46 def __enter__(self):
\r
48 self.builder.start(self.name,self.attrs)
\r
49 self.builder.data("".join(self.text))
\r
52 def __exit__(self,x,y,z):
\r
54 self.builder.end(self.name)
\r
56 #-------------------------------------------------------------------------------
\r
57 class XMLBuilder(object):
\r
58 """XmlBuilder(encoding = 'utf-8', # result xml file encoding
\r
59 builder = None, #etree.TreeBuilder or compatible class
\r
60 tab_level = None, #current tabulation level - string
\r
61 format = False, # make formatted output
\r
62 tab_step = " " * 4) # tabulation step
\r
63 use str(builder) or unicode(builder) to get xml text or
\r
64 ~builder to obtaine etree.ElementTree
\r
66 def __init__(self,encoding = 'utf-8',
\r
70 tab_step = " " * 4):
\r
71 self.__builder = builder or TreeBuilder()
\r
72 self.__encoding = encoding
\r
74 if tab_level is None:
\r
76 if tab_level is not None:
\r
78 raise ValueError("format is False, but tab_level not None")
\r
79 self.__tab_level = tab_level # current format level
\r
80 self.__tab_step = tab_step # format step
\r
81 self.__has_sub_tag = False # True, if current tag had childrens
\r
83 # called from _XMLNode when tag opened
\r
84 def __iadd__(self,val):
\r
85 self.__has_sub_tag = False
\r
86 if self.__tab_level is not None:
\r
87 self.__builder.data("\n" + self.__tab_level)
\r
88 self.__tab_level += self.__tab_step
\r
90 # called from XMLNode when tag closed
\r
91 def __isub__(self,val):
\r
92 if self.__tab_level is not None:
\r
93 self.__tab_level = self.__tab_level[:-len(self.__tab_step)]
\r
94 if self.__has_sub_tag:
\r
95 self.__builder.data("\n" + self.__tab_level)
\r
96 self.__has_sub_tag = True
\r
98 def __getattr__(self,name):
\r
99 return _XMLNode(self,name,self.__builder)
\r
100 def __call__(self,name,*dt,**mp):
\r
101 x = _XMLNode(self,name,self.__builder)
\r
104 #create new tag or add text
\r
105 #possible shift values
\r
107 #tuple(string1,string2,dict) - new tag with name string1,attrs = dict,and text string2
\r
108 #dict and string2 are optional
\r
109 def __lshift__(self,val):
\r
110 if isinstance(val,basestring):
\r
111 self.__builder.data(val)
\r
113 self.__has_sub_tag = True
\r
114 assert hasattr(val,'__len__'),\
\r
115 'Shifted value should be tuple or list like object not %r' % val
\r
116 assert hasattr(val,'__getitem__'),\
\r
117 'Shifted value should be tuple or list like object not %r' % val
\r
122 elif len(val) == 1:
\r
125 elif len(val) == 2:
\r
126 if isinstance(val[1],basestring):
\r
132 if self.__tab_level is not None:
\r
133 self.__builder.data("\n" + self.__tab_level)
\r
134 self.__builder.start(name,
\r
135 dict((k,str(v)) for k,v in attrs.items()))
\r
137 self.__builder.data(text)
\r
138 self.__builder.end(name)
\r
139 return self # to allow xml << some1 << some2 << some3
\r
141 def __invert__(self):
\r
142 if self.__node is not None:
\r
144 self.__node = self.__builder.close()
\r
147 """return generated xml"""
\r
148 return tostring(~self,self.__encoding)
\r
149 def __unicode__(self):
\r
150 """return generated xml"""
\r
151 res = tostring(~self,self.__encoding)
\r
152 return res.decode(self.__encoding)
\r
153 #-------------------------------------------------------------------------------
\r