SOAP.py

Graham Dumpleton grahamd at dscpl.com.au
Wed Dec 19 06:08:56 CET 2001


Duncan Grisby <dgrisby at uk.research.att.com> wrote in message news:<9vnpn1$t45$1 at pea.uk.research.att.com>...
> I've just started trying some experiments with SOAP.py 0.9.7. I'm
> having a problem with the server side that is so fundamental that I'm
> sure I must be doing something wrong, but I can't see what.
>
> ...
>
> Not exactly complicated. Now, when I try a client, I get:
> 
> ---------------
> Python 2.2c1 (#1, Dec 17 2001, 19:20:56) 
> [GCC egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
> >>> import SOAP
> >>> server = SOAP.SOAPProxy("http://localhost:8000/")
> >>> server.echo("Duncan")
> "{'Result': 'Hello Duncan'}"
> ---------------
> 
> Note that that is a _string_ containing the Python representation of a
> dictionary. Looking at the XML sent by the server:
> 
> *** Outgoing SOAP ******************************************************
> <?xml version="1.0" encoding="UTF-8"?>
> <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
> <SOAP-ENV:Body>
> <echoResponse xsi:type="xsd:dict" SOAP-ENC:root="1">{'Result': 'Hello Duncan'}</echoResponse>
> </SOAP-ENV:Body>
> </SOAP-ENV:Envelope>
> ************************************************************************

Your example code works fine with Python 2.0.

> I'm sure that can't be right. The parsing code doesn't seem to
> understand "xsd:dict". I'm sure it must be wrong to be transmitting a
> dictionary using its Python representation.

SOAP.py has some code in it which when generating a SOAP element, defaults
to the following when indicating what type the element is:

                t = self.genns(ns_map, self.config.typesNamespaceURI)[0] + \
                    type(sample).__name__

Ie., it uses Python introspection functionality to get the name of the
type as a string. This is the only place I can think of where "dict" is
coming from as "dict" is not a standard type in the XML Schema Datatypes
specification and there is nothing in the code that hard codes that string
as a type. But the question is why isn't it "dictionary" in that case
instead of "dict". Has something changed in this respect in 2.2?

Now one has to ask why it even runs this particular code, as it would only
get there after failing:

            if (isinstance(sample, structType)) or \
                type(sample) == DictType: # force to urn struct

if that is the case.

I know I've seen the following generated by SOAP.py:

<echoResponse SOAP-ENC:root="1">
<Result SOAP-ENC:arrayType="xsd:list[1]" xsi:type="SOAP-ENC:Array">
<item SOAP-ENC:arrayType="xsd:list[1]" xsi:type="SOAP-ENC:Array">
<item SOAP-ENC:arrayType="xsd:int[3]" xsi:type="SOAP-ENC:Array">
<item>1</item>
<item>2</item>
<item>3</item>
</item>
</item>
</Result>
</echoResponse>

Like "dict" use of "list" is also wrong and seems to be because it is dropping
down and getting the name of the type for the list objects it is encoding.
This BTW is with Python 2.0 and is reasonable as the code doesn't check for
list or tuple types whereas it does for a dict type. That it doesn't check
for list or tuple types and generate some other SOAP type is wrong in my mind.

Anyway, that is the best idea I can come up with.

> If I apply the following patch, everything behaves the way the
> documentation claims it should:
> 
> --- old_SOAP.py Mon Dec 17 20:26:09 2001
> +++ SOAP.py     Mon Dec 17 20:24:42 2001
> @@ -3769,7 +3769,7 @@
>                                  config = self.server.config)
>                          else:
>                              resp = buildSOAP(kw =
> -                                {'%sResponse' % method: {'Result': fr}},
> +                                {'%sResponse' % method: fr},
>                                  encoding = self.server.encoding,
>                                  config = self.server.config)
>                      except Exception, e:
> 
> 
> Is this the right thing to do?  Am I barking up the wrong tree?

I think such a change is possibly masking the real problem, something
being caused by Python 2.2c1. I don't have 2.2c1 to try anything out
though. Can you try an older version of Python or try instrumenting
the code in the area I indicate above. Note that the problem may still be
elsewhere, but start with the code in that area.

> Does anyone have a suggestion of any other Python SOAP implementation
> that is as simple to use as SOAP.py?

If the simplicity is that you only want to deal with positional argument
style remote procedure calls, rather than named parameters and named returned
values, you might look at OSE. OSE isn't strictly another implementation
as it uses ZSI underneath, and can also be made to use SOAP.py although
you are then stuck with the various shortcomings of SOAP.py again.

In short, what OSE does is provide an alternate API for using these packages
which fits within a bigger framework for implementing such systems.
 
Specifically in respect of ZSI, it makes it a bit more useful because it
provides some Python classes and encoders for basic types such as Boolean,
Date, Time etc. If using ZSI direct, you would need to implement them
yourself. I have provided some standalone versions of these classes and
encoders which will work with ZSI outside of OSE to Rich Salz (ZSI author),
whether he will include them as an demo example I don't know.
 
Do note however that OSE is much more than just a framework for implementing
a SOAP server. In that respect it may be much more than what you actually
require. OSE as a whole provides a C++ library framework for writing event
driven and distributed applications. It has its own messaging framework as
well as bridges for XML-RPC and SOAP. Coding in Python is possible through
Python wrappers around major functionality provided by the library.
 
For more information on OSE see:
 
  http://ose.sourceforge.net
 
There is an up to date manual on the Python wrappers (more than can be said
for the C++ interfaces :-)), with various Python examples on the web site
as well.

Do make sure however you pick up the patch related to the SOAP gateway from
the bug reports area of the OSE site though. Without the fix, you can use the
ZSI SOAP based client, but other clients such as Perl::Lite don't work with
the server.

BTW, below is how you would write your example with OSE. Use the URL:

  http://localhost:8000/example

import netsvc
import signal
 
class Service(netsvc.Service):
  def __init__(self,name="service"):
    netsvc.Service.__init__(self,name)
    self.joinGroup("web-services")
    self.exportMethod(self.echo)
  def echo(self,s):
    return "Hello " + s
 
dispatcher = netsvc.Dispatcher()
dispatcher.monitor(signal.SIGINT)
 
daemon = netsvc.HttpDaemon(8000)
 
import netsvc.soap
gateway = netsvc.soap.RpcGateway("web-services")

daemon.attach("/",gateway)
daemon.start()
 
service = Service("example")
 
dispatcher.run()



More information about the Python-list mailing list