Source

suds /

Filename Size Date modified Message
suds
tests
358 B
446 B
7.5 KB
44.8 KB
2.1 KB
3.4 KB
23 B
1.2 KB
OVERVIEW

The "Suds" web services client is a lightweight soap-based client for python
the is licensed under LGPL.
   
 Basic features:
 
  * no class generation
  * provides an object-like API.
  * reads wsdl at runtime for encoding/decoding
  * provides for the following SOAP (style) binding/encoding:
     * Document/Literal
     * RPC/Literal
     * RPC/Encoded (section 5)
  
Logging:

The suds package use the Python standard lib logging package: 
all messages are at level DEBUG or ERROR.

To register a console handler you can use basicConfig:
  >
  > import logging
  > logging.basicConfig(level=logging.INFO)
  >

Once the console handler is configured, the user can enable module 
specific debugging doing the following:
logging.getLogger(<desired package>).setLevel(logging.<desired-level>)
A common example (show sent/received soap messages):
  >
  > logging.getLogger('suds.serviceproxy').setLevel(logging.DEBUG)
  >

BASIC USAGE (2nd generation):

The "suds" Client class provides a consolidated API for consuming
web services.  The object contains a number of namespaces:

The (service) namespace provides a proxy for the consumed service.  This object
is used to invoke operations (methods) provided by the service endpoint.

The (factory) namespace provides a factory that may be used to create instances
of objects and types defined in the WSDL.

You will need to know the url for WSDL for each service used.  Simply create
a client for that service as follows:

  > from client import Client
  >
  > url = 'http://localhost:7080/webservices/WebServiceTestBean?wsdl'
  >
  > client = Client(url)


You can inspect service object with: __str()__ as follows to get a list of
methods provide by the service:

  >
  > print client
  >
service (WebServiceTestBeanService)
   prefixes (1):
      ns0 = "http://test.server.enterprise.rhq.org/"
   ports (1):
      (Soap)
         methods:
            addPerson(person person, )
            echo(xs:string arg0, )
            getList(xs:string str, xs:int length, )
            getPersonByName(name name, )
            hello()
            testExceptions()
            testListArg(xs:string[] list, )
            testVoid()
            updatePerson(anotherPerson person, name name, )
   types (23):
      hello
      testListArg
      person
      getListResponse
      getList
      addPersonResponse
      name
      testListArgResponse
      testExceptions
      anotherPerson
      phone
      updatePerson
      getPersonByName
      helloResponse
      testVoid
      addPerson
      getPersonByNameResponse
      testVoidResponse
      TestException
      testExceptionsResponse
      echo
      updatePersonResponse
      echoResponse
      
      ** See example of service with multiple ports below.
  
 The sample ouput lists that the service named "WebServiceTestBeanService"
 has methods such as addPerson() which takes a 'person' argument of type: 'person'.
 This is listed as:
 
addPerson(person person, ) where parameter type is printed and followed by it's
 name.  There is a type (or class) named 'person'.  Or in the case of
 getList(xs:string str, xs:int length, ) the parameters are 'str' of type xs:string 
 and 'length' of type xs:int.
 
 So, to get person object to pass as an argument we need to get a person argument
 as follows:
 
 >
 > person = client.factory.create('person')
 > print person
 >
  {
    phone = []
    age = NONE
    name(Name) = 
        {
            last = NONE
            first = NONE
        }
   }
 
 As you can see, the object is created as defined by the WSDL.  The list of phone
 number is empty so we'll have to create one:
 
  >
  > phone = client.factory.create('phone')
  > phone.npa = 202
  > phone.nxx = 555
  > phone.number = 1212
  >
 
 ... and the name and age need to be set and we need to create a
 name object first:
 
  >
  > name = client.factory.create('name')
  > name.first = 'Elmer'
  > name.last = 'Fudd'
  >

Now, let's set the properties of our person object

  >
  > person.name = name
  > person.age = 35
  > person.phone = [phone]
   --- or --
  > person.phone.append(phone)
  >

... and invoke our method named addPerson() as follows:

  >
  > try:
  >    person_added = client.service.addPerson(person)
  > except WebFault, e:
  >    print e
  >

It's that easy.

The Client can be configured to throw web faults as WebFault or
to return a tuple (<status>, <returned-value>) instead as follows:

  >
  > client = client(url, faults=False)
  > result = client.service.addPerson(person)
  > print result
  ( 200, person ...)

Enumerations are handled as follows:

Let's say the wsdl defines the following enumeration,

<xs:simpleType name="resourceCategory">
  <xs:restriction base="xs:string">
    <xs:enumeration value="PLATFORM"/>
    <xs:enumeration value="SERVER"/>
    <xs:enumeration value="SERVICE"/>
  </xs:restriction>
</xs:simpleType>

 The client can instantiate the enumeration so it can be 
 used.  Misspelled references to elements of the enum will raise a
 AttrError exception as:
 
  >
  > resourceCategory = client.factory.create('resourceCategory')
  > client.service.getResourceByCategory(resourceCategory.PLATFORM)
  >

The create() method should always be used becuase it returns objects that already
have the proper structure and schema-type information.  Since xsd supports nested type
definition, so does create() using the (.) dot notation.  For example suppose the (name)
type was not defined as a top level "named" type but rather defined within the (person) type.
In this case creating a (name) object would have to be quanified by it's parent's name using the
dot notation as follows:

  >
  > name = client.factory.create('person.name') 
  >

If the type is in the same namespace as the wsdl (targetNamespace) then it may be referenced
without any namespace qualification.  If not, the type must be qualifed by either a namespace
prefix such as:

  >
  > name = client.factory.create('ns0:person') 
  >
 
 Or, the name can be fully qualified by the namespace itself using the full 
 qualification syntax as (as of 0.2.6):
 
  >
  > name = client.factory.create('{http://test.server.enterprise.rhq.org/}person') 
  >
 
 Qualified names can only be used for the *first* part of the name, when using (.) dot
 notation to specify a path.
 
SERVICES WITH MULTIPLE PORTS:

Some services are defined with multiple ports as:

    <wsdl:service name="BLZService">
        <wsdl:port name="soap" binding="tns:BLZServiceSOAP11Binding">
            <soap:address location="http://www.thomas-bayer.com:80/axis2/services/BLZService"/>
        </wsdl:port>
        <wsdl:port name="soap12" binding="tns:BLZServiceSOAP12Binding">
            <soap12:address location="http://www.thomas-bayer.com:80/axis2/services/BLZService"/>
    </wsdl:service>
    
And are reported by suds as:

>
> url = 'http://www.thomas-bayer.com/axis2/services/BLZService?wsdl'
> client = Client(url) 
> print client

suds ( version=0.3 )

service ( BLZService )
   prefixes (1)
      ns0 = "http://thomas-bayer.com/blz/"
   ports (2):
         (soap)
            methods (1):
               getBank(xs:string blz, )
         (soap12)
            methods (1):
               getBank(xs:string blz, )
   types (5):
      getBankType
      getBankResponseType
      getBankType
      getBankResponseType
      detailsType
      
This example only has (1) method defined for each port but it could very likely have
may methods defined.  Suds does not require the method invocation to be qualifed
(as shown above) by the port as:

   client.service.<port>.getBank()

in less the user wants to specify a particular port.  In most cases, the server will
work properly with any of the soap ports.  However, if you want to invoke the
getBank() method on this service the user may qualify the method name with the port.

There are (1) ways to do this:

1) Select a default port using the Client.setport() method before invoking the
   method as:
   
   >
   > client.setport('soap')
   > client.service.getBank()
   >
   -or-
   >
   > client.setport(0)
   > client.service.getBank()
   >
   
2) fully qualify the method as:

   >
   > client.service.soap.getBank()
   >

 
SOAP HEADERS
  
SOAP headers may be passed during the service invocation by using the
special (soapheaders) keyword as follows:

 >
 > client = client(url)
 > token = client.factory.create('AuthToken')
 > token.username = 'Elvis'
 > token.password = 'TheKing'
 > result = client.service.addPerson(person, soapheaders=token)
 >
 
 OR
 
 > client = client(url)
 > userid = client.factory.create('Auth.UserID')
 > userid.set('Elvis')
 > password = client.factory.create('Auth.Password')
 > password.set('TheKing')
 > result = client.service.addPerson(person, soapheaders=(userid,password))
 >
 
 OR
 
  > client = client(url)
 > userid = client.factory.create('Auth.UserID')
 > userid.set('Elvis')
 > password = client.factory.create('Auth.Password')
 > password.set('TheKing')
 > headers = (userid,password)
 > client.setheaders(headers)
 > result = client.service.addPerson(person)
 >
 
 The keyword (soapheaders) *must* value must be a ( or list/tuple of ) Object
 (or Property) object(s) obtained from the client.factory when types are *not*
 simple types such as (xs:string, xs:int, etc ).
 
 The Client.setheaders() method may be used to set the soap headers for *all*
 method invocations.
 
 
MULTI-DOCUMENT Docuemnt/Literal:

In most cases, services defined using the document/literal SOAP binding style
will define a single document as the message payload.  The <message/> will only
have (1) <part/> which references an <element/> in the schema.  In this case, suds
presents a RPC view of that method by displaying the method signature as the
contents (nodes) of the document.
Eg:

<schema>
...
<xs:element name="Foo" type = "tns:Foo"/>
<xs:complexType name="Foo">
  <xs:sequence>
    <xs:element name="name" type="xs:string"/>
    <xs:element name="age" type="xs:int"/>
  </xs:sequence>
</xs:complexType>
...
</schema>

<definitions>
...
<message name="FooMessage">
  <part name="parameters" element="Foo">
</message>
...
</definitions>

Suds will report the method "foo" signature as:

   foo(xs:string name, xs:int age,)
   
This provides an RPC feel to the document/literal soap binding style.

Now, if the wsdl defines:

<schema>
...
<xs:element name="Foo" type = "tns:Foo"/>
<xs:element name="Bar" type = "xs:string"/>
<xs:complexType name="Foo">
  <xs:sequence>
    <xs:element name="name" type="xs:string"/>
    <xs:element name="age" type="xs:int"/>
  </xs:sequence>
</xs:complexType>
...
</schema>

<definitions>
...
<message name="FooMessage">
  <part name="foo" element="Foo">
  <part name="bar" element="Bar">
</message>
...
</definitions>

Suds will be forced to report the method "foo" signature as:

   foo(Foo foo, xs:int bar)
   
The message has (2) parts which defines that the message payload contains
(2) documents.  In this case, suds must present a /Document/ view of the method.

  
BASIC USAGE (legacy):

** THIS API MAY ONLY BE USED WITH WSDLs THAT DEFINE SERVICES
     WITH ONLY (1) PORT.  IT LACKS THE TOOLS TO DEAL WITH MULIPORT
     SERVICES.

You will need to know the url for WSDL for each service used.  Simply create
a proxy for that service as follows:

  > from serviceproxy import ServiceProxy
  >
  > url = 'http://localhost:7080/webservices/WebServiceTestBean?wsdl'
  >
  > myservice = ServiceProxy(url)


You can inspect service object with: __str()__ as follows to get a list of
methods provide by the service:

  >
  > print myservice
  >
service (WebServiceTestBeanService)
   prefixes (1):
      ns0 = "http://test.server.enterprise.rhq.org/"
   ports (1):
      (Soap)
         methods:
            addPerson(person person, )
            echo(xs:string arg0, )
            getList(xs:string str, xs:int length, )
            getPersonByName(name name, )
            hello()
            testExceptions()
            testListArg(xs:string[] list, )
            testVoid()
            updatePerson(anotherPerson person, name name, )
   types (23):
      hello
      testListArg
      person
      getListResponse
      getList
      addPersonResponse
      name
      testListArgResponse
      testExceptions
      anotherPerson
      phone
      updatePerson
      getPersonByName
      helloResponse
      testVoid
      addPerson
      getPersonByNameResponse
      testVoidResponse
      TestException
      testExceptionsResponse
      echo
      updatePersonResponse
      echoResponse
 
 The sample ouput lists that the service named "WebServiceTestBeanService"
 has methods such as addPerson() which takes a 'person' argument of type: 'person'.
 This is listed as:
 
addPerson(person person, ) where parameter type is printed and followed by it's
 name.  There is a type (or class) named 'person'.  Or in the case of
 getList(xs:string str, xs:int length, ) the parameters are 'str' of type xs:string 
 and 'length' of type xs:int.
 
 So, to get person object to pass as an argument we need to get a person argument
 as follows:
 
 >
 > person = myservice.get_instance('person')
 > print person
 >
  {
    phone = []
    age = NONE
    name = 
        {
            last = NONE
            first = NONE
        }
   }
 
 As you can see, the object is created as defined by the WSDL.  The list of phone
 number is empty so we'll have to create one:
 
  >
  > phone = myservice.get_instance('phone')
  > phone.npa = 202
  > phone.nxx = 555
  > phone.number = 1212
  >
 
 ... and the name and age need to be set and we need to create a
 name object first:
 
  >
  > name = myservice.get_instance('name')
  > name.first = 'Elmer'
  > name.last = 'Fudd'
  >

Now, let's set the properties of our person object

  >
  > person.name = name
  > person.age = 35
  > person.phone = [phone]
  >

... and invoke our method named addPerson() as follows:

  >
  > try:
  >    person_added = myservice.addPerson(person)
  > except WebFault, e:
  >    print e
  >

It's that easy.

The ServiceProxy can be configured to throw web faults as WebFault or
to return a tuple (<status>, <returned-value>) instead as follows:

  >
  > myservice = ServiceProxy(url, faults=False)
  > result = myservice.addPerson(person)
  > print result
  ( 200, person ...)

Enumerations are handled as follows:

Let's say the wsdl defines the following enumeration,

<xs:simpleType name="resourceCategory">
  <xs:restriction base="xs:string">
    <xs:enumeration value="PLATFORM"/>
    <xs:enumeration value="SERVER"/>
    <xs:enumeration value="SERVICE"/>
  </xs:restriction>
</xs:simpleType>

 The service proxy can instantiate the enumeration so it can be 
 used.  Misspelled references to elements of the enum will raise a
 AttrError exception as:
 
  >
  > resourceCategory = myservice.get_enum('resourceCategory')
  > myservice.getResourceByCategory(resourceCategory.PLATFORM)
  >

The get_instance() method should always be used becuase it returns objects that already
have the proper structure and xsi:type="" attribute defined.  Since xsd supports nested type
definition, so does create() using the (.) dot notation.  For example suppose the (name)
type was not defined as a top level "named" type but rather defined within the (person) type.
In this case creating a (name) object would have to be quanified by it's parent's name using the
dot notation as follows:

  >
  > name = myservice.get_instance('person.name') 
  >

NOTE FOR AXIS USERS

Axis, by default, uses MultiRefs when marshalling objects,
but suds does not yet support multirefs. If you are using a SOAP
service provided by Axis, you must change the Axis global configuration and
set the global parameter "sendMultiRefs" to false.

See http://ws.apache.org/axis/java/reference.html#GlobalAxisConfiguration for
an explanation about Axis global configuration.


AUTHENTICATION

Revision 63+ (and release 0.1.8+) includes the migration from httplib to urllib2
which enables users to leverage all of the authentication features provided
by urllib2.  For example basic HTTP authentication could be implemented
as follows:

>
> import urllib2
> baseurl = 'http://localhost:7080/'
> username = 'myuser'
> password = 'mypassword'
> passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
> passman.add_password(None, baseurl, username, password)
> authhandler = urllib2.HTTPBasicAuthHandler(passman)
> opener = urllib2.build_opener(authhandler)
>
> myurl = 'http://localhost:7080/webservices/WebServiceTestBean?wsdl'
> client = Client(myurl, opener=opener)
>

Since suds uses urllib2.urlopen(), basic http authentication is handled
automatically if you create the urlopener correctly and set the urlopener
using the 'urlopener' keyword argument.

PROXIES

  Suds handles proxies using urllib2.Request.set_proxy().  The proxy
  flag can be passed to the ServiceProxy constructor as kwarg.  The proxy
  arg must contain a dictionary where keys=protocols and values are
  the hostname (or IP) and port of the proxy. 

> ...
> proxy = dict(http='host:80', https='host:443', ...)
> myservice = ServiceProxy(url, proxy=proxy)
> ...

NIL VALUES

 Some web service endpoints can handle nil values as <tag xsi:nil="true"/> and
 others cannot.  The nil_supported flag (default: True) on the
 ServiceProxy specifies weather Object values = None are sent to the
 WS server in the soap message as <tag xsi:nil="true"/> or <tag/>.
 
 MESSAGE INJECTION (Testing)
 
 The service API provides for message/reply injection.  To inject either a soap
 message to be sent or to inject a reply to be processed as if returned by the
 soap server, simply specify the 'inject' keyword argument with a value of a 
 dictionary containing either {msg:'<message string>'} | {reply:'<reply string>'}
 when invoking the service.  Eg:
 
>
> reply = \
> """
> <SOAP-ENV:Envelope xmlns="HelloWorldService.HelloWorldService"
>  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
>  xmlns:tns="HelloWorldService.HelloWorldService"
>  xmlns:xs="http://www.w3.org/2001/XMLSchema"
>  xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">
>  <SOAP-ENV:Body>
>    <testResponse>
>      <retval xmlns="" xsi:type="tns:stringArray">
>        <string xmlns="" xsi:type="xs:string">str1</string>
>        <string xmlns="" xsi:type="xs:string">str2</string>
>      </retval>
>    </testResponse>
>  </SOAP-ENV:Body>
> </SOAP-ENV:Envelope>
>"""
>
> print client.service.test(inject={'reply':reply})
> 
 
 TECHNICAL (FYI) NOTES
 
 * XML namespaces are represented as a tuple (prefix, URI).
   The default namespace is (None,None).
 * The suds.sax module was written becuase elementtree and other
    python XML packages either: have a DOM API which is very unfriendly
    or: (in the case of elementtree) do not deal with namespaces and
    especially prefixes sufficiently.
 * A qualified reference is a type that is referenced in the WSDL such as <tag type="tns:Person/>
    where the qualified reference is a tuple ('Person', ('tns','http://myservce/namespace'))
    where the namespace is the 2nd part of the tuple.  When a prefix is not supplied as in
    <tag type="Person/>, the namespace is the targetNamespace for the defining fragment.
    This ensures that all lookups and comparisons are fully qualified.
    
 TIPS & TRICKS
 
  * Cache the suds Client because reading and digesting the WSDL can be expensive
     because some servers generate them on demand.

  * Some WSDL(s) schemas import as: <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
     without schemaLocation="" and expect processor to use the namespace URI as the
     schema location for the namespace.  The specifications for processing <import/>
     leave the resolution of the imported namespace to a schema to the descession of the processor (in
     this case suds) when @schemaLocation is not specified.  Suds always looks within the WSDL for a
     schema but does not look outside unless:
        1) A schemaLocation is specified, or 
        2) A static binding is specified using the following syntax:
            >
            > from suds.xsd.sxbasic import Import
            > ns = 'http://schemas.xmlsoap.org/soap/encoding/'
            > location = 'http://schemas.xmlsoap.org/soap/encoding/'
            > Import.bind(ns, location)
            >
     
            Or, the shorthand (when location is the same as the namespace URI)
     
            > 
            > Import.bind(ns)
            >



RELEASE-NOTES:
=================================================

version-0.1.1 (12-17-07):

  This release marks the first release in fedora.

version-0.1.2 (12-18-07):

  This release contains an update to property adds:
    * metadata support
    * overrides: __getitem__, __setitem__, __contans__
    * changes property(reader|writer) to use the property.metadata
       to handle namespaces for XML documents.
    * fixes setup.py requires.
    
version-0.1.3 (12-19-07):

  * Fixes problem where nodes marked as a collection (maxOccurs > 1) not
     creating property objects with value=[] when mapped-in with < 2
     values by the DocumentReader.  Caused by missing the 
     bindings.Document.ReplyHint.stripns() (which uses the DocumentReader.stripns())
     conversion to DocumentReader.stripn() now returning a tuple (ns,tag) as
     of 0.1.2.
     
version-0.1.4 (12-21-07):

  * Provides for service method parameters to be None.
  * Add proper handling of method params that are lists of property
    objects.

version-0.1.5( 02-21-08 ):

 * Provides better logging in the modules get logger by hierarchal names.
 * Refactored as needed to truely support other bindings.
 * Add sax module which replaces ElementTree.  This is faster, simpler and
   handles namespaces (prefixes) properly.
   
version-0.1.6 (03-06-08):

 * Provides proper handling of wsdls that contain schema sections containing
    xsd schema imports: <import namespace="" schemaLocation=""?>.  The
    referenced schemas are imported when a schemaLocation is specified.
* Raises exceptions for http status codes not already handled.

version-0.1.7 (04-08-08):

  * Added Binding.nil_supported to controls how property values (out) = None and empty tag (in) are processed.
    * service.binding.nil_supported = True -- means that property values = None are marshalled (out) as
      <x xsi:nil=true/> and <x/> is unmarshalled as '' and <x xsi:nil/> is unmarshalled as None.
    * service.binding.nil_supported = False -- means that property values = None are marshalled (out) as
      <x/> and <x/> *and* <x xsi:nil=true/> is unmarshalled as None.
      The xsi:nil is really ignored.
    * THE DEFAULT IS (TRUE)

  * Sax handler updated to handle multiple character() callbacks when the sax parser "chunks" the text.
    When the node.text is None, the node.text is set to the characters.  Else, the characters are appended.
    Thanks - andrea.spinelli@imteam.it
  
  * Replaced special (text) attribute with __text__ to allow for natural elements named "text"
  
  * Add unicode support by:
    * Add __unicode__ to all classes with __str__
    * Replace all str() calls with unicode().
    * __str__() returns UTF-8 encoded result of __unicode__.

  * XML output encoded as UTF-8 which matches the HTTP header and supports unicode.
  
  * SchemaCollection changed to provide the builtin() and custom() methods.  To support this, findPrefixes() was added to the
    Element in sax.py.  This is a better approach anyway since the wsdl and schemas may have many prefixes
    to http://www.w3.org/2001/XMLSchema.  Tested with both doc/lit and rpc/lit bindings
  
  * Refactored bindings packages from document & rpc to literal & encoded
  
  * Contains the completion of *full* namespace support as follows:
  
    * Namespace prefixes are no longer stripped from attribute values that
      reference types defined in the wsdl.
    * Schema's imported using <import/> should properly handle namespace and prefix
      mapping and re-mapping as needed.
    * All types are resolved, using fully qualified (w/ namespaces) lookups.
    * Schema.get_type() supports paths with and without ns prefixes.  When no prefix
      is specified the type is matched using the schema's target namespace.
  
  * Property maintains attribute names (keys) in the order added. This also means
    that get_item() and get_names() return ordered values.
     ( Although, I suspect ordering really needs to be done in the marshaller using the
        order specified in the wsdl/schema )
  
  Major refactoring of the schema.py. The primary goals is perparation for type lookups that are
  fully qualified by namespace.  Once completed, the prefixes on attribute values will not longer
  be stripped (purged).
  Change Summary:
    1) SchemaProperty overlay classes created at __init__ instead of on-demand.
    2) schema imports performed by new Import class instead of by Schema.
    3) Schema loads top level properties using a factory.
    4) All SchemaProperty /children/ lists are sorted by __cmp__ in SchemaProperty derived classes.
       This ensures that types with the same name are resolved in the following order (Import, Complex, Simple, Element).
    5) All /children/ SchemaProperty lists are constructed at __init__ instead of on-demand.
    6) The SchemaGroup created and WSDL class updated.  This works better then having the wsdl aggregate the <schema/>
       nodes which severs linkage to the wsdl parent element that have namespace prefix mapping.
    7) <import/> element handles properly in that both namespace remapping and prefix re-mapping of the imported schema's
       targetNamespace and associated prefix mapping - is performed.
         Eg: SCHMEA-A has prefix (tns) mapped as xmlns:tns=http://nsA and has targetNamespace=http://nsA.
         SCHEMA-B is importing schema A and has prefix (abc) mapped as xmlns:abc=http://nsABC.
         SCHEMA-B imports A as <import namespace=http://nsB xxx schemaLocation=http://nsA/schema-a.xsd>.
         So, since SCHEMA-B will be referencing elements of SCHEMA-A with prefix (abc) such as abc:something, SCHEMA-A's
         targetNamespace must be updated as http://nsABC and all element with type=tns:something must be updated to be
         type=abc:something so then can be resolved.

  * Fixes unmarshalling problem where nodes are added to property as (text, value).  This as introduced when the
    bindings were refactored.

  * Fixed various Property print problems.

  Notes:

    Thanks to Jesper Noehr of Coniuro for the majority of the rpc/literal binding and
    for lots of collaboration on #suds.


version-0.2 (04-28-08):

  * Contains the first cut at the rpc/encoded soap style.
 
  * Replaced Property class with suds.sudsobject.Object.  The Property class was developed a long time
     ago with a slightly different purpose.  The suds Object is a simpler (more straight forward) approach that 
     requires less code and works better in the debugger. 

  * The Binding (and the encoding) is selected on a per-method basis which is more consistent with the wsdl.
     In <= 0.1.7, the binding was selected when the ServiceProxy was constructed and used for all service
     methods.  The binding was stored as self.binding.  Since the WSDL provides for a separate binding style and
     encoding for each operation, Suds needed to be change to work the same way.
 
  * The (nil_supported) and (faults) flag(s) passed into the service proxy using **kwargs.  In addition to these
     flags, a (http_proxy) flag has been added and is passed to the urllib2.Request object.  The following args
     are supported:
       * faults = Raise faults raised by server (default:True), else return tuple from service method invocation
                        as (http code, object).
       * nil_supported = The bindings will set the xsi:nil="true" on nodes that have a value=None when this
                                      flag is True (default:True).  Otherwise, an empty node <x/> is sent.
       * proxy = An http proxy to be specified on requests (default:{}).
                       The proxy is defined as {protocol:proxy,}
                                      
  * Http proxy supported (see above).
  
  * ServiceProxy refactored to delegate to a SoapClient.  Since the service proxy exposes web services via getattr(),
     any attribute (including methods) provided by the ServiceProxy class hides WS operations defined by the
     wsdl.  So, by moving everything to the SoapClient, wsdl operations are no longer hidden without
     having to use *hoky* names for attributes and methods in the service proxy.  Instead, the service proxy has
     __client__ and __factory__ attributes (which really should be at low risk for name collision).  For now the 
     get_instance() and get_enum() methods have not been moved to preserve backward compatibility.  Although,
     the prefered API change would to replace:
     
     > service = ServiceProxy('myurl')
     > person = service.get_instance('person')
     
     ... with something like ...
     
     > service = ServiceProxy('myurl')
     > person = service.__factory__.get_instance('person')
     
     After a few releases giving time for users to switch the new API, the get_instance() and get_enum()
     methods may be removed with a notice in big letters.
     
   * Fixed problem where a wsdl doesn't define a <schema/> section and Suds can't resolve the prefixes for the
      http://www.w3.org/2001/XMLSchema namespace to detect builtin types such as (xs:string).

version 0.2.1 (5-8-08):

   * Update the schema.py SchemaProperty loading sequence so that the schema is loaded in 3 steps: 
         1) build the raw tree. 
         2) resolve dependancies such as @ref and @base.  
         3) promote grandchildren as needed to flatten (denormalize) the tree.
         
       The wsdl was also changed to only load the schema once and store it.  The schema collection was 
       changed to load schemas in 2 steps:
         1) create all raw schema objects.
         2) load schemas.  
       
       This ensure that local <import/>'d schemas can be found when referenced out of order.  
       The sax.py Element interface changed: attribute() replaced by get() and set().  
       Also, __getitem__ and __setitem__ can be used to access attribute values.  
       Epydocs updated for sax.py.  And ... last <element ref=/> now supported properly.

   * fix logging by: NOT setting to info in suds.__init__.logger(); set handler on root logger 
      only; moved logger (log) from classes to modules and use __name__ for logger name.
      NOTE: This means that to enable soap message logging:
           >
           > logger('suds.serviceproxy').setLevel(logging.DEBUG)
           > 
         -- instead of --
           >
           > logger('serviceproxy').setLevel(logging.DEBUG)
           >
      
   * Add support for (xsd) schema <attribute/> nodes which primarily affects objects returned by the Builder
   
   * Update serviceproxy.py:set_proxies() to log DEBUG instead of INFO.
   
   * Enhance schema __str__ to show both the raw xml and the model (mostly for debugging).

version 0.2.2 (7-8-08):

   * Update exceptions to be more /standard/ python by using Exception.__init__() to set Exception.message as
     suggested by Ticket #14; update bindings to raise WebFault passing (p)
   
   * Add capability in bindings to handle multiple root nodes in the returned values;
     returned as a composite object unlike when lists are returned
   
   * Fix soapAction to be enclosed by quotes
   
   * Add support for <xs:all/>
   
   * Fix unbounded() method in SchemaObject
   
   * Refactored schema into new (xsd) package.  Files just getting too big. Added execute() to
     Query and retrofitted suds to execute() query instead of using Schema.find() directly.  
     Also, move hokey start() methods from schema, as well as, query incrementation.
   
   * Add inject keyword used to inject outbound soap messages and/or inbound reply messages.
      Refactor SoapClient and 
        1) rename send() to invoke()
        2) split message sending from invoke() and place in send();
      Add TestClient which allows for invocation kwargs to have inject={'msg=, and reply='} for message
      and reply injection
     
   * Add Namespace class to sax for better management of namespace behavior;
     retrofix suds to import and use Namespace
   
   * Change the default namespace used to resolve referenced types (having attribues @base="",@type="")
      so that when no prefix is specified: uses XML (node) namesapce instead of the targetNamespace.
   
   * Apply fix as defined by davidglick@onenw.org in ticket #13
   
   * Update service definition to print to display service methods as ' my_method(xs:int arg0, Person arg1) '
      instead of ' my_method(arg0{xs:int}, arg1{Person}) ' which is more like traditional method signatures
   
   * Add xsd/python type converstion to unmarshaller (XBoolean only); refactor unmarshaller to use Content
      class which makes apis cleaner, adds symmetry between marshaller(s) and unmarshaller(s),
      provides good mechanism for schema-property based type conversions
   
   * Refactor marshaller with Appenders; add nobuiltin flag to resolve() to support fix for
      returned_type() and returnes_collection() in bindings.
   
   * Add support for (202,204) http codes
   
   * Add XBoolean and mappings; add findattr() to TreeResolver in preparation for type conversions
   
   * Updated schema and schema property loading (deep recusion stopped); Changed Imported schemas so then no
      longer copy imported schemas, rather the import proxies find requests; Add ServiceDefinition class which
      provides better service inspection; also provides namespace mapping and show types; schema property api simplified;
      support for xs:any and xs:anyType added; Some schema lookup problems fixed; Binding classes refactored slightly;
      A lot of debug logging added (might have to comment some out for performance - some of the args are expensive).
   
   * Add sudsobject.Property; a property is a special Object that contains a (value) attributeand is returned by the
      Builder (factory) for schema-types without children such as: <element/> and <simpleType/>; Builder, Marshallers
      and Resolvers updated to handle Properties; Resolver, Schema also updated to handle attribute lookups (this was missing)
   
   * Add groundwork for user defined soap headers.
   
   * Fix elementFormDefault per ticket #7
   
   * Remove unused kwargs from bindings; cache bindings in wsdl; retrofit legacy ServiceProxy to delegate to {new} Client API;
      remove keyword nil_supported in favor of natural handling by 'nillable' attribute on <element/> within schemas
   
   * Add support for <element/> attribute flags (nillable and form)
   
   * Add the Proxy (2nd generation API) class
   
   * Add accessor/conversion functions to that user don't need to access __x__ attributes.
      Also add todict() and get_items() for easy conversion to dictionary and iteration
   
   * Search top-level elements for @ref before looking deeper
   
   * Add derived() to SchemaObject.  This is needed to ensure that all derived types (wsdl classes)
      are qualified by xsi:type without specifying the xsi:type for all custom types as did in earlier
      releases of suds.  Update the literal marshaller to only add the xsi:type when the type needs
      to be specified.
   
   * Change ns promotion in sax to prevent ns promoted to parent when parent has the prefix.
   
   * Changed binding returned_type() to return the (unresolved) Element 
   
   * In order to support the new features and fix reported bugs,
      I'm in the process of refactoring and hopefully evolving the components
      in Suds that provide the input/output translations:
   
     * Builder ( translates: XSD objects => python objects )
     * Marshaller ( translates: python objects => XML/SOAP )
     * Unmarshaller ( translates: soap/xml => python objects )
   
     This evolution will provide better symmetry between these components as follows:
   
     The Builder and Unmarshaller will produce python
     (subclass of sudsobject.Object) objects with:
   
     *  __metadata__.__type__ = XSD type (SchemaObject)
     * subclass name ( __class__.__name__ ) = schema-type name.
   
     and
   
     The Marshaller(s), while consuming python objects produced by the Builder or
     Unmarshaller, will leverage this standard information to
     produce the appropriate output ( XML/SOAP ).
   
     The 0.2.1 code behaves *mostly* like this but ... not quite.
     Also, the implementations have some redundancy.
   
     While doing this, it made sense to factor out the common schema-type "lookup"
     functionality used by the Builder, Marshallers and Unmarshaller classes into a
     hierarchy of "Resolver" classes.  This reduces the complexity and redundancy
     of the Builder, Marshallers and Unmarshaller classes and allows for better
     modularity.  Once this refactoring was complete, the difference between the
     literal/encoded Marshallers became very small.  Given that the amount of code
     in the bindings.literal and bindings.encoded packages was small (and getting smaller)
     and in the interest of keeping the Suds code base compact, I moved all of the
     marshalling classes to the bindings.marshaller module.
     All of the bindings.XX sub-packages will be removed.
   
     The net effect:
   
     All of the Suds major components:
   
     * client (old: service proxy)
     * wsdl
       * schema (xsd package)
       * resolvers
     * output (marshalling)
     * builder
     * input (unmarshalling)
   
     Now have better:
   
      * modularity
      * symmetry with regard to Object metadata.
      * code re-use (< 1% code duplication --- i hope)
      * looser coupling
   
     ... and better provide for the following features/bug-fixes:
   
     * (fix) Proper level of XML element qualification based on
        <schema elementFormDefault=""/> attribute.  This will ensure that when
        elementFormDefault="qualified", Suds will include the proper namespace on
        root elements for both literal and encoded bindings.  In order for this to
        work properly, the literal marshaller (like the encoded marshaller) needed
        to be schema-type aware.  Had i added the same schema-type lookup as the
        encoded marshaller instead of the refactoring described above, the two
        classes would have been almost a complete duplicate of each other :-(
   
   * The builder and unmarshaller used the schema.Schema.find()
     to resolve schema-types.  They constructed a path as "person.name.first"
     to resolve types in proper context.  Since the Schema.find() was stateless,
     it resolved the intermediate path elements on every call.  The new resolver
     classes are statefull and resolve child types *much* more efficiently.
   
   * Prevent name collisions in sudsobject.Object like the items() method.  I've moved all
     methods (including class methods) to a Factory class that is included in the Object class
     as a class attr ( __factory__ ).  Now that *all* attributes have python built-in naming,
     we should not have any more name collisions.  This of course assumes that no wsdl/schema
     entity names will have a name with the python built-in naming convention
     but I have to draw the line somewhere :-) 
     
version 0.2.3 (7-23-08):

   * Optimizations.
   
version 0.2.4 (7-28-08):

   * Added support for WSDL imports: <wsdl:import/>
   * Added support for xsd<->python type conversions (thanks: Nathan Van Gheem) for:
      * xs:date
      * xs:time
      * xs:dateTime
   * Fixed:
      * Bug: Schema <import/> with schemaLocation specified.
      * Bug: Namespaces specified in service description not valid until client/proxy is printed.
      
version 0.2.5 (8-01-08):

   * Overhauled the (XSD) package.  This new (merging) approach is simpler and should be
      more reliable and maintainable.  Also, should provide better performance since the merged
      schema performes lookups via dictionary lookup.
      
      This overhaul should fix current TypeNotFound and <xs:extension/> problems, I hope :-).
      
   * Fixed dateTime printing bug.

   * Added infinite recursion prevention in builder.Builder for xsd types that contain themselves.
   
version 0.2.6 (8-5-08):

   * Fix ENUMs broken during xsd package overhaul.
   * Fix type as defined in ticket #24.
   * Fix duplicate param names in method signatures as reported in ticket #30.
   * Suds licensed as LGPL.
   * Remove logging setup in suds.__init__() as suggested by patch in ticket #31.  Users will
      now need to configure the logger.
   * Add support for Client.Factory.create() alt: syntax for fully qualifying the type to be
      built as: {namespace}name.  Eg: client.factory.create('{http://blabla.com/ns}Person') 
      
version 0.2.7 (8-11-08):

   * Add detection/support for document/literal - wrapped and unwrapped.
   * Update document/literal {wrapped} to set document root (under <body/>) to be the
      wrapper element (w/ proper namespace).
   * Add support for <sequence/>, <all/> and <choice/> having maxOccurs and have the
      which causes the unmarshaller to set values for elements contained in an unbounded
      collection as a list.
   * Update client.factory (builder) to omit children of <choice/> since the 'user' really needs
      to decide which children to include.
   * Update flattening algorithm to prevent re-flattening of types from imported schemas.
   * Adjustments to flattening/merging algorithms.

version 0.2.8 (8-28-08):

   * Update document/literal binding to always send the document root referenced by the <part/>.
      After yet another review of the space and user input, seems like the referenced 
      element is ALWAYS the document root.

   * Add support for 'binding' schemaLocations to namespace-uri.  
     This is for imports that don's specify a schemaLocation and still expect the schema 
     to be downloaded.  Eg: Axis references 'http://schemas.xmlsoap.org/soap/encoding/' 
     without a schemaLocation.  So, by doing this:
        >
        > from suds.xsd.sxbasic import Import.
        > Import.bind('http://schemas.xmlsoap.org/soap/encoding/')
        >
     The schema is bound to a schemaLocation and it is downloaded.

   * Basic unmarshaller doesn't need a /schema/. 
      Should have been removed during refactoring but was missed.

   * Update client to pass kwargs to send() and add /location/ kwarg for overriding the
      service location in the wsdl.

   * Update marshaller to NOT emit XML for object attributes that represent elements and/or attributes that
      are *both* optional and value=None.
         * Update factory (builder) to include all attributes.
         * Add optional() method to SchemaObject.

   * Update wsdl to override namespace in operation if specified.
   
   * Fix schema loading issue - build all schemas before processing imports.
   
   * Update packaging in preparation of submission to fedora
   
version 0.2.9 (9-09-08):
   
   * Support for multiple ports within a service.
   * Attribute references <xs:attribute ref=""/>
   * Make XML special character encoder in sax package - pluggable 
   
version 0.3 (9-30-08):

  * Extends the support for multi-port services introduced in 0.2.9.  This addition,
     provides for multiple services to define the *same* method and suds will
     handle it properly.  See section 'SERVICES WITH MULTIPLE PORTS:'
     
  * Add support for multi-document document/literal soap binding style.
     See section 'MULTI-DOCUMENT Docuemnt/Literal:'
  
  * Add support for (xs:group, xs:attributeGroup) tags.
  
  * Add Client.last_sent() and Client.last_received().
  
version 0.3.1 (10-1-08):

 * Quick follow up to the 0.3 release that made working multi-port service definitions
    harder then necessary.  After consideration (and a good night sleep),
    it seemed obvious that a few changes would make this much easier: 1) filter out 
    the non-soap bindings - they were causing the real trouble;  2) since most servers
    are happy with any of the soap bindings (soap 1.1 and 1.2), ambigious references to methods 
    when invoking then without the port qualification will work just fine in almost every
    case.  So, why not just allow suds to select the port.  Let's not make the user do it 
    when it's not necessary.  In most cases, uses on 0.2.9 and earlier will not have to
    update there code when moving to 0.3.1 as they might have in 0.3.
    
version 0.3.2 (11-7-08):
       * SOAP {{{MultiRef}}} support ''(1st pass added r300)''
       * Add support for new schema tags:
          * <xs:include/>
          * <xs:simpleContent/>
          * <xs:group/>
          * <xs:attributeGroup/>
       * Added support for new xs <--> python type conversions:
          * xs:int
          * xs:long
          * xs:float
          * xs:double
       * Revise marshaller and binding to further sharpen the namespacing of nodes produced.
       * Infinite recursion fixed in ''xsd'' package dereference() during schema loading.
       * Add support for <wsdl:import/> of schema files into the wsdl root <definitions/>.
       * Fix double encoding of (&)
       * Add Client API:
          * setheaders() - same as keyword but works for all invocations.
          * addprefix() - mapping of namespace prefixes.
          * setlocation() - Override the location in the wsdl; same as keyword except for all calls.
          * setproxy() - same as proxy keyword but for all invocations.
       * Add proper namespace prefix for soap headers.
       * Fixed Tickets: #5, #12, #34, #37, #40, #44, #45, #46, #48, #49, #50, #51