1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20   
 21   
 22   
 23   
 24   
 25   
 26   
 27   
 28   
 29   
 30   
 31   
 32   
 33   
 34   
 35   
 36   
 37   
 38  """ 
 39  The ``E`` Element factory for generating XML documents. 
 40  """ 
 41   
 42  from __future__ import absolute_import 
 43   
 44  import lxml.etree as ET 
 45   
 46  from functools import partial 
 47   
 48  try: 
 49      basestring 
 50  except NameError: 
 51      basestring = str 
 52   
 53  try: 
 54      unicode 
 55  except NameError: 
 56      unicode = str 
 57   
 58   
 60      """Element generator factory. 
 61   
 62      Unlike the ordinary Element factory, the E factory allows you to pass in 
 63      more than just a tag and some optional attributes; you can also pass in 
 64      text and other elements.  The text is added as either text or tail 
 65      attributes, and elements are inserted at the right spot.  Some small 
 66      examples:: 
 67   
 68          >>> from lxml import etree as ET 
 69          >>> from lxml.builder import E 
 70   
 71          >>> ET.tostring(E("tag")) 
 72          '<tag/>' 
 73          >>> ET.tostring(E("tag", "text")) 
 74          '<tag>text</tag>' 
 75          >>> ET.tostring(E("tag", "text", key="value")) 
 76          '<tag key="value">text</tag>' 
 77          >>> ET.tostring(E("tag", E("subtag", "text"), "tail")) 
 78          '<tag><subtag>text</subtag>tail</tag>' 
 79   
 80      For simple tags, the factory also allows you to write ``E.tag(...)`` instead 
 81      of ``E('tag', ...)``:: 
 82   
 83          >>> ET.tostring(E.tag()) 
 84          '<tag/>' 
 85          >>> ET.tostring(E.tag("text")) 
 86          '<tag>text</tag>' 
 87          >>> ET.tostring(E.tag(E.subtag("text"), "tail")) 
 88          '<tag><subtag>text</subtag>tail</tag>' 
 89   
 90      Here's a somewhat larger example; this shows how to generate HTML 
 91      documents, using a mix of prepared factory functions for inline elements, 
 92      nested ``E.tag`` calls, and embedded XHTML fragments:: 
 93   
 94          # some common inline elements 
 95          A = E.a 
 96          I = E.i 
 97          B = E.b 
 98   
 99          def CLASS(v): 
100              # helper function, 'class' is a reserved word 
101              return {'class': v} 
102   
103          page = ( 
104              E.html( 
105                  E.head( 
106                      E.title("This is a sample document") 
107                  ), 
108                  E.body( 
109                      E.h1("Hello!", CLASS("title")), 
110                      E.p("This is a paragraph with ", B("bold"), " text in it!"), 
111                      E.p("This is another paragraph, with a ", 
112                          A("link", href="http://www.python.org"), "."), 
113                      E.p("Here are some reserved characters: <spam&egg>."), 
114                      ET.XML("<p>And finally, here is an embedded XHTML fragment.</p>"), 
115                  ) 
116              ) 
117          ) 
118   
119          print ET.tostring(page) 
120   
121      Here's a prettyprinted version of the output from the above script:: 
122   
123          <html> 
124            <head> 
125              <title>This is a sample document</title> 
126            </head> 
127            <body> 
128              <h1 class="title">Hello!</h1> 
129              <p>This is a paragraph with <b>bold</b> text in it!</p> 
130              <p>This is another paragraph, with <a href="http://www.python.org">link</a>.</p> 
131              <p>Here are some reserved characters: <spam&egg>.</p> 
132              <p>And finally, here is an embedded XHTML fragment.</p> 
133            </body> 
134          </html> 
135   
136      For namespace support, you can pass a namespace map (``nsmap``) 
137      and/or a specific target ``namespace`` to the ElementMaker class:: 
138   
139          >>> E = ElementMaker(namespace="http://my.ns/") 
140          >>> print(ET.tostring( E.test )) 
141          <test xmlns="http://my.ns/"/> 
142   
143          >>> E = ElementMaker(namespace="http://my.ns/", nsmap={'p':'http://my.ns/'}) 
144          >>> print(ET.tostring( E.test )) 
145          <p:test xmlns:p="http://my.ns/"/> 
146      """ 
147   
148 -    def __init__(self, typemap=None, 
149                   namespace=None, nsmap=None, makeelement=None): 
 178   
179          def add_cdata(elem, cdata): 
180              if elem.text: 
181                  raise ValueError("Can't add a CDATA section. Element already has some text: %r" % elem.text) 
182              elem.text = cdata 
 183   
184          if str not in typemap: 
185              typemap[str] = add_text 
186          if unicode not in typemap: 
187              typemap[unicode] = add_text 
188          if ET.CDATA not in typemap: 
189              typemap[ET.CDATA] = add_cdata 
190   
191          def add_dict(elem, item): 
192              attrib = elem.attrib 
193              for k, v in item.items(): 
194                  if isinstance(v, basestring): 
195                      attrib[k] = v 
196                  else: 
197                      attrib[k] = typemap[type(v)](None, v) 
198          if dict not in typemap: 
199              typemap[dict] = add_dict 
200   
201          self._typemap = typemap 
202   
203 -    def __call__(self, tag, *children, **attrib): 
 204          typemap = self._typemap 
205   
206          if self._namespace is not None and tag[0] != '{': 
207              tag = self._namespace + tag 
208          elem = self._makeelement(tag, nsmap=self._nsmap) 
209          if attrib: 
210              typemap[dict](elem, attrib) 
211   
212          for item in children: 
213              if callable(item): 
214                  item = item() 
215              t = typemap.get(type(item)) 
216              if t is None: 
217                  if ET.iselement(item): 
218                      elem.append(item) 
219                      continue 
220                  for basetype in type(item).__mro__: 
221                       
222                      t = typemap.get(basetype) 
223                      if t is not None: 
224                          break 
225                  else: 
226                      raise TypeError("bad argument type: %s(%r)" % 
227                                      (type(item).__name__, item)) 
228              v = t(elem, item) 
229              if v: 
230                  typemap.get(type(v))(elem, v) 
231   
232          return elem 
 233   
235          return partial(self, tag) 
 236   
237   
238   
239  E = ElementMaker() 
240