423 lines
17 KiB
Plaintext
423 lines
17 KiB
Plaintext
The "Suds" web services client is a lightweight soap-based client for python.
|
|
The primary classes in the package are the:
|
|
* ServiceProxy (serviceproxy.py)
|
|
* WSDL (wsdl.py)
|
|
|
|
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:
|
|
|
|
By default, the suds package logs at LEVEL=INFO using a console handler.
|
|
All messages are at level DEBUG or ERROR so, no messages are written
|
|
unless the user does the following: suds.logger(<desired package>).setLevel(logging.<desired-level>)
|
|
A common example (show sent/received soap messages):
|
|
>
|
|
> suds.logger('serviceproxy').setLevel(logging.DEBUG)
|
|
>
|
|
|
|
|
|
Basic usage:
|
|
|
|
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
|
|
>
|
|
> myurl = '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:
|
|
SOAP-ENC = "http://schemas.xmlsoap.org/soap/encoding/"
|
|
SOAP-ENV = "http://schemas.xmlsoap.org/soap/envelope/"
|
|
soap = "http://schemas.xmlsoap.org/wsdl/soap/"
|
|
tns = "http://myservice/namespace"
|
|
wsdl = "http://schemas.xmlsoap.org/wsdl/"
|
|
xsd = "http://www.w3.org/2001/XMLSchema"
|
|
xsi = "http://www.w3.org/2001/XMLSchema-instance"
|
|
methods:
|
|
addPerson(person{tns:person})
|
|
echo(arg0{xsd:string})
|
|
getPersonByName(name{tns:name})
|
|
hello()
|
|
testExceptions()
|
|
updatePerson(person{tns:anotherPerson}, name{tns:name})
|
|
|
|
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 name is listed and followed by it's
|
|
type as {person}. There is a type (or class) named 'person'.
|
|
|
|
So, to get person object to pass as an argument we need to get a person argument
|
|
as follows:
|
|
|
|
>
|
|
> factory = myservice.__factory__
|
|
> person = factory.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 = factory.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 = factory.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 = factory.get_enum('resourceCategory') <--- None if not found.
|
|
> 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 get_instance() 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 = factory.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
|
|
>
|
|
> theurl = 'www.someserver.com/toplevelurl/somepage.htm'
|
|
> protocol = 'http://'
|
|
> username = 'johnny'
|
|
> password = 'XXXXXX'
|
|
> # a great password
|
|
>
|
|
> passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
|
|
> # this creates a password manager
|
|
> passman.add_password(None, theurl, username, password)
|
|
> # because we have put None at the start it will always
|
|
> # use this username/password combination for urls
|
|
> # for which `theurl` is a super-url
|
|
>
|
|
> authhandler = urllib2.HTTPBasicAuthHandler(passman)
|
|
> # create the AuthHandler
|
|
>
|
|
> opener = urllib2.build_opener(authhandler)
|
|
>
|
|
> urllib2.install_opener(opener)
|
|
> # All calls to urllib2.urlopen will now use our handler
|
|
> # Make sure not to include the protocol in with the URL, or
|
|
> # HTTPPasswordMgrWithDefaultRealm will be very confused.
|
|
> # You must (of course) use it when fetching the page though.
|
|
>
|
|
> pagehandle = urllib2.urlopen(protocol + theurl)
|
|
|
|
Since suds uses urllib2.urlopen(), basic http authentication is handled
|
|
automatically for you if you setup the urllib2 library correctly.
|
|
|
|
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/>.
|
|
|
|
|
|
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
|
|
* Cache the ServiceProxy because reading and digesting the WSDL can be expensive
|
|
because some servers generate them on demand.
|
|
|
|
|
|
|
|
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')
|
|
> factory = service.__factory__
|
|
> person = 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).
|