Element Builder

XML Element container and builder.

Trivial stuff

>>> exml.Element("tag")
Element(<tag/>)
>>> exml.Element("node", attr="value", intval=1)
Element(<node attr="value" intval="1"/>)

“call” element to set children

>>> Element("text")("body")
Element(<text>body</text>)
>>> print Element("nobody", expects=True)(
...     "Spanish Inquisition",
...     " ",
...     Element("not", at="all")
... )
<nobody expects="True">Spanish Inquisition <not at="all"/></nobody>

List-like append for adding children nodes

>>> list = Element("list")
>>> list.append(Element("item")("lorem"))
>>> list.append(Element("item")("ipsum"))
>>> print list
<list><item>lorem</item><item>ipsum</item></list>

Alternatively, start with an empty Element

>>> e = Element()
>>> e.node = "test"
>>> e['from'] = "scratch"
>>> e.append("ok")
>>> print e
<test from="scratch">ok</test>

Some NameSpace examples

From http://www.w3.org/TR/REC-xml-names/#ns-decl

>>> print Element("x", xmlns__edi="http://ecommerce.example.org/schema")('''
...   <!-- the "edi" prefix is bound to http://ecommerce.example.org/schema
...        for the "x" element and contents -->
... ''')
<x xmlns:edi="http://ecommerce.example.org/schema">
  <!-- the "edi" prefix is bound to http://ecommerce.example.org/schema
       for the "x" element and contents -->
</x>
>>> print Element("book", xmlns="urn:loc.gov:books", xmlns__isbn="urn:ISBN:0-395-36341-6")(
...     Element("title")("Cheaper by the Dozen"),
...     Element("isbn:number")("1568491379"))
<book xmlns="urn:loc.gov:books" xmlns:isbn="urn:ISBN:0-395-36341-6"><title>Cheaper by the Dozen</title><isbn:number>1568491379</isbn:number></book>

Using “E the Builder” shortcut

Node name goes into attribute name and then there are only clean attributes in parens’.

>>> E = Builder()
>>> E.use_getattr  
<function Element("use_getattr", ...) at 0x...>

A little more complex example:

>>> print E.tree(x="mas")(
...     E.stump(),
...     E.trunk()(
...         E.branch()(
...            E.ball(color="red"),
...            E.ball(color="gold"),
...         ),
...         E.branch()(
...            E.ball(color="green"),
...         ),
...     ),
...     E.tip()(
...         E.star(points="5")
...     )
... )
<tree x="mas"><stump/><trunk><branch><ball color="red"/><ball color="gold"/></branch><branch><ball color="green"/></branch></trunk><tip><star points="5"/></tip></tree>

Double-underscores-to-namespace trick works here too:

>>> print E.ns__are(fine="too")
<ns:are fine="too"/>

SOAP Requests

As an example (and my primary use case) soap.py contains rather simplistic WSDL-ignorant request generator.

Service assumes it is you, who knows schema definitions and correct types, so just set it to endpoint and pass a request namespace.

>>> service = BasicSOAP("https://bathroom.hostname.tld/Cleaning.aspx", 'Cleaning-Service')

BasicSOAP is used to simply generate a SOAP 1.1 request and does not process server response.

>>> service._request_xml('SomeMethod', Some="parameters", EverythingGoesRaw=1, TakeCareOf={'your': 'types'})
Element(<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Header/><soap:Body><SomeMethod xmlns="Cleaning-Service"><request><EverythingGoesRaw>1</EverythingGoesRaw><Some>parameters</Some><TakeCareOf>{'your': 'types'}</TakeCareOf></request></SomeMethod></soap:Body></soap:Envelope>)

You can subclass SOAP/BasicSOAP and override header and/or body methods (envelope too, but..).

>>> class AuthSOAP(SOAP):
...     def _header(self):
...         return E.soap__Header()(
...             E.AuthenticationHeader(xmlns=self.request_NS)(
...                 E.User()("Me"),
...                 E.Pwd()("Cat")))
>>> AuthSOAP("http://knock-knock.com", "SecureService")._request_xml('Hello', lets='party')
Element(<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Header><AuthenticationHeader xmlns="SecureService"><User>Me</User><Pwd>Cat</Pwd></AuthenticationHeader></soap:Header><soap:Body><Hello xmlns="SecureService"><request><lets>party</lets></request></Hello></soap:Body></soap:Envelope>)

If lxml is installed, there is a SOAP class, which utilizes etree parser to process SOAP response.

LxmlProcessor mixin lifts value from trivial result nodes. You can override its behavior in _result_map/_result_reduce methods.

>>> service = SOAP("http://www.kirupafx.com/WebService/TopMovies.asmx", "http://www.kirupafx.com")
>>> service.GetMovieAtNumber(input=1)
'The Godfather (1972)'

Result nodes that have same name are wrapped into lists. Check your classes!

>>> service = SOAP("http://www.kirupafx.com/WebService/TopMovies.asmx", "http://www.kirupafx.com")
>>> service.GetTop10()
{'string': ['The Godfather (1972)', 'The Shawshank Redemption (1994)', 'The Godfather: Part II (1974)', 'The Lord of the Rings: The Return of the King (2003)', 'Casablanca', "Schindler's List", 'Shichinin no samurai (1954)', 'Buono, il brutto, il cattivo, Il (1966)', 'Pulp Fiction (1994)', 'Star Wars: Episode V - The Empire Strikes Back (1980)']}