ANN: Lye - a COM to SOAP gateway...

Andrew Dalke dalke at acm.org
Wed Sep 13 03:40:48 EDT 2000


[..and a request for COM help]

Hello,

  I've written a program called Lye which is a simple COM server
that forwards calls to a SOAP server using /F's soaplib.  It's
a small program, so I've attached it to this email.  I can't use
Microsoft's COM/SOAP interface because it requires MS Windows 2000
or NT 4.0 SP6, and I only have 98 and NT 4.0 SP4.  That also means
I haven't been able to do any interoperability tests between Lye
and MS's version.

  I would like some help finishing it off.  This is my first COM
server, and I'm a unix developer, so I'm guessing a lot and leafing
through the Win32 book (chapter 12, DynamicPolicy :).  I can get
the Python COM client to talk to the COM server, but I can't get
VB for Applications to do it.  With debugging I can see a call in
_dynamic_ for a function/attribute named _value_ .  I don't know
what to return, or if I should raise an exception.

  As I minor point, I'm not sure about the standard COM names for
things like factory functions, so advice for that would also be
helpful.

  The example in the module docstring works with the stock echo server
from the soaplib distribution (to be specific, v0.8 from
http://www.secretlabs.com/products/soap/ ).  Speaking of which, I've
fixed a couple problems in soaplib which prevented it from talking to
itself - but I don't know how my fixes match the SOAP spec.  I've
included the diff to this email.  I also sent mail last week to /F
about it.  There seem to be a few other problems with soaplib,
so I'm hoping he'll release a new one soon *wink* *wink*, *nudge*
*nudge*.

                    Andrew Dalke
                    dalke at acm.org

P.S.
  My ISP thinks my attachements are binaries and says that's unacceptable
so here are the two files:

========== Lye.py
# COM <-> SOAP gateway
#   (and my first COM server, so be gentle :)

"""COM to SOAP gateway

This is a Python-based COM server which turns a query into a SOAP
request.

Python example:

from win32com.client import Dispatch
factory = Dispatch("Dalke.LyeFactory")
soap = factory.createSOAP("http://localhost:8000")
print soap.echo("Andrew")

VB example (which doesn't work ):

Sub TestSOAP()
  Set factory = CreateObject("Dalke.LyeFactory")
  soap = factory.createSOAP("http://localhost:8000")
  MsgBox soap.echo("Andrew")
End Sub

I seem to have problems with the "_value_" parameter.

"""


import string
import types
import win32com, pywintypes, winerror
from win32com.server import policy, util
from win32com.server.exception import COMException
import pythoncom
import soaplib

# Copied from the Python-win32 book
def FixArgs(args):
  # Fix the arguments, so Unicode objects are converted
  # to strings.  Does this recursively, to ensure
  # sub-lists (ie, arrays) are also converted
  newArgs = []
  for arg in args:
    if type(arg) == types.TupleType:
      arg = FixArgs(arg)
    elif type(arg) == pywintypes.UnicodeType:
      arg = str(arg)
    newArgs.append(arg)
  return tuple(newArgs)

class COMSoap:
  def __init__(self, url):
    self.server = soaplib.ServerProxy(url)
  def _dynamic_(self, name, lcid, wFlags, args):
    #print "Called with", repr(name), repr(lcid), repr(wFlags), repr(args)
    if (wFlags & pythoncom.DISPATCH_METHOD):
      if name == "_value_":
        # XXX This is wrong.
        # I think I need to do something else here, since VB isn't working
        # If I return, say, "name" then that's the return value
        raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND)
    elif (wFlags & pythoncom.DISPATCH_PROPERTYGET):
      # Get lookups need to fail so the client tries as a method
      raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND)
    else:
      raise COMException(desc = "You can not set any SOAP property",
                         scode = winerror.DISP_E_BADVARTYPE)

    # Pivot the function name and args call to SOAP.
    # Use string.lower because VB doesn't preserve about case
    func = getattr(self.server, string.lower(name))
    ret = apply(func, FixArgs(args))
    if type(ret) == types.ListType:
      # Why doesn't soaplib raise a proper exception?  Workaround for now
      if len(ret) == 2 and ret == [('faultcode', []), ('faultstring', [])]:
        raise COMException(desc = "could not process SOAP call",
                           scode = winerror.DISP_E_EXCEPTION)
    return ret

class LyeFactory:
  _reg_clsid_ = "{AC2541B6-8913-11D4-BE42-000000000000}"
  _reg_progid_ = "Dalke.LyeFactory"
  _public_methods_ = ['createSOAP']

  def createSOAP(self, url):
    url = str(url)
    idTran = util.wrap(COMSoap(url), usePolicy = policy.DynamicPolicy)
    return idTran


if __name__ == "__main__":
  import win32com.server.register
  win32com.server.register.UseCommandLine(LyeFactory)
===========

========= soaplib.diff
*** ../soaplib.py Wed Jun 21 10:36:58 2000
--- soaplib.py Tue Sep 12 18:43:12 2000
***************
*** 73,80 ****
  import string, time, re
  import urllib, xmllib
  from types import *
- from cgi import escape

  try:
      import sgmlop
  except ImportError:
--- 73,89 ----
  import string, time, re
  import urllib, xmllib
  from types import *

+ # XML needs to escape a lot more characters than done by cgi.escape
+ #from cgi import escape
+ _escape_table = {}
+ for i in range(0, 256):
+   _escape_table[chr(i)] = chr(i)
+ for i in range(0, 32) + range(127, 256):
+   _escape_table[chr(i)] = "#%d;" % i
+ def escape(s):
+   return string.join(map(_escape_table.get, s), "")
+
  try:
      import sgmlop
  except ImportError:
***************
*** 549,557 ****
   if type is None and tag in SOAPTAGS:
       pass
   else:
!      try:
!   f = self.dispatch[type]
!      except KeyError:
    self.end_unknown(type)
       else:
    self._mark = mark
--- 558,575 ----
   if type is None and tag in SOAPTAGS:
       pass
   else:
!      # Changed by dalke to support "ur-type[%d]" arrays
!      f = self.dispatch.get(type)
!      if f is None:
!          s = NS_XSD + "ur-type["
!          if type[:len(s)] == s and type[-1:] == "]":
!              n = type[len(s):-1]
!              try:
!                  int(n)
!                  f = Unmarshaller.end_array
!              except ValueError:
!                         pass
!      if f is None:
    self.end_unknown(type)
       else:
    self._mark = mark
========






More information about the Python-list mailing list