[omniORB] A Stab at Python COM-to-CORBA Binding Generation

W. Eliot Kimber eliot@isogen.com
Fri, 29 Dec 2000 16:46:58 -0600


Having tried to find a free (or even evaluatable) CORBA-to-COM bridge
and failed, I did some experimentation and realized that writing a COM
wrapper in Python for CORBA objects was pretty easy with OmniORB and
omniidl (scarily easy, actually). The only complication I've found so
far is that our "top-level" class cannot be auto-generated, as it is the
one class that establishes the initial CORBA connection (or at least, I
haven't thought about the problem long enough to figure out how to
generate this class).

While the code given below was hacked very quickly to serve the needs of
our project (which doesn't do anything particularly sophisticated in its
IDL), it should be sufficient to COMify other projects with a minimum of
additional hacking. I haven't stepped up to handling CORBA name spaces
in the output because I didn't need it to solve today's problem (it
happens we don't have any name collisions across modules in our current
API). I also need to add some command arguments, for example, to
suppress generation of certain classes and to write directly to a file
rather than stdout. Also need a way to re-use existing COM class GUIDs
(the code currently blindly regenerates the GUIDs each time, which
probably doesn't hurt anything but could possibly cause registration
problems or other subtle and confusing failures).

My thanks to the OmniORB team for the omniidl framework--it made
creating this program as easy as it could possibly be. I won't make any
claims about performance for this bridge approach--I imagine that large
lists going back and forth would start to really drag--but as low-cost
way to get to CORBA from COM I think it's pretty good--I've invested
about 8 hours in this.

Here is the code. Enjoy.

---- cut here ----
#-------------------------------------------------------------
#
# COM-to-IDL Binding Generator
#
#---------------------------------------------------------------

"""
OmniORB back end for generating Python COM server classes that
implement the IDL and delegate to the corresponding Python
CORBA classes.
"""

from omniidl import idlast, idlvisitor, idlutil, idltype
import string
import pythoncom

progidMain = "MyClassFamily"  # FIXME: get this from an argument
declaredClasses = []    # List of classes declared, used to generate COM
registrations

class ComVisitor (idlvisitor.AstVisitor):

    def visitAST(self, node):
        for n in node.declarations():
            n.accept(self)

    def visitModule(self, node):
        for n in node.definitions():
            n.accept(self)

    def visitInterface(self, node):
        name = node.identifier()
        global declaredClasses
        declaredClasses.append(name)
        guid = pythoncom.CreateGuid()
        desc = name # FIXME: generate a better description.
        progid = "%s.%s" % (progidMain, name)
        print """
class %s:

    _reg_clsid_ = "%s"
    _reg_desc_ = "%s"
    _reg_progid_ = "%s"
    _public_attrs_ = []
    _readonly_attrs_ = []
""" % (name, guid, desc, progid)

        # Now generate the list of public methods

        publicMethodList = ""
        callables = node.callables()
        for callable in callables:
            if callable.__class__ == idlast.Operation:
                publicMethodList = "%s'%s',\n%s" % (publicMethodList,
                                                  callable.identifier(),
                                                  " " * 22)

        print "    _public_methods_=[%s]" % publicMethodList

        print """
    def __init__(self, corbaObject = None):
        self.corbaObject = corbaObject
"""        

        for callable in callables:
            if callable.__class__ == idlast.Operation:
                generateMethodForOperation(callable)
            

def generateMethodForOperation(operation):
    """
    Given an operation, generates the corresponding COM-specific
    Python method.
    """

    methodName = operation.identifier()
    comParms = "self"
    parms = operation.parameters()
    corbaParms = ""
    if len(parms) > 0:
        for param in parms:
            if param.is_in():
                comParms = "%s, %s" % (comParms, param.identifier())
    
    print "\n    def %s(%s):" % (methodName, comParms)

    returnType = operation.returnType()
    returnKind = returnType.kind()
    if returnKind == idltype.tk_alias:
        returnType = operation.returnType().unalias()
        returnKind = returnType.kind()

    if returnKind == idltype.tk_void:
        print "        self.corbaObject.%s(%s)" % (methodName,
corbaParms)
    elif returnKind == idltype.tk_objref:
        print "        return wrap(%s(self.corbaObject.%s(%s)))" %
(returnType.name(), methodName, corbaParms)
    elif returnKind == idltype.tk_sequence:
        seqType = returnType.seqType()
        print "        objs = self.corbaObject.%s(%s)" % (methodName,
corbaParms)
        print "        comObjs = []"
        if seqType.kind() == idltype.tk_objref:
            print "        for obj in objs:"
            print "            comObjs.append(wrap(%s(obj)))" %
seqType.name()
        elif seqType.kind() == idltype.tk_string:
            print "        for string in objs:"
            print "            comObjs.append(str(string))"
        else:
            print "        comObjs = objs"
        print "        return wrap(Collection(data = comObjs))"

    else:
        print "        return self.corbaObject.%s(%s)" % (methodName,
corbaParms)

def comparm2corbaparm(corbaParm):
    """
    Given a corba parameter, returns the appropriate Python parameter.
    """
    result = ""
    type = corbaParm.paramType()
    if type.kind() == idltype.tk_string:
        result = "str(%s)" % corbaParm.identifier()
    elif type.kind() == idltype.tk_objref:
        result = "unwrap(%s).corbaObject" % corbaParm.identifier()
    else:
        result = corbaParm.identifier()
    return result
        
def run(tree, args):
    visitor = ComVisitor()
    print "#----------------------------------------------------------"
    print "# Generated by gencombind.py "
    print "#----------------------------------------------------------"

    print """
import sys

from win32com.server.util import wrap, unwrap, Collection"
from win32com.server.exception import COMException

import traceback
import CosNaming
from omniORB import CORBA

from zLOG import LOG, TRACE, DEBUG, BLATHER, INFO, PROBLEM, WARNING,
ERROR, PANIC
"""
    tree.accept(visitor)

    print "\nif __name__ == '__main__':"
    print "    import win32com.server.register"
    for name in declaredClasses:
        print "    win32com.server.register.UseCommandLine(%s)" % name
#--- end of program ---
    
-- 
. . . . . . . . . . . . . . . . . . . . . . . .

W. Eliot Kimber | Lead Brain

1016 La Posada Dr. | Suite 240 | Austin TX  78752
    T 512.656.4139 |  F 512.419.1860 | eliot@isogen.com

w w w . d a t a c h a n n e l . c o m