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