Quick Start to Server side COM and Python

Introduction

This documents how to quickly start implementing COM objects in Python. It is not a thorough discussion of the COM system, or of the concepts introduced by COM.

For more details information on Python and COM, please see the COM Tutorial given by Greg Stein and Mark Hammond at SPAM 6 (HTML format) or download the same tutorial in PowerPoint format.

For information on using external COM objects from Python, please see a Quick Start to Client side COM and Python

Implement the core functionality

Implement a stand-alone Python class with your functionality

class HelloWorld:

def __init__(self):

self.softspace = 1

self.noCalls = 0

def Hello(self, who):

self.noCalls = self.noCalls + 1

# insert "softspace" number of spaces

return "Hello" + " " * self.softspace + who

This is obviously a very simple server. In particular, custom error handling would be needed for a production class server. In addition, there are some contrived properties just for demonstration purposes.

Make Unicode concessions

At this stage, Python and Unicode don’t really work well together. All strings which come from COM will actually be Unicode objects rather than string objects.

To make this code work in a COM environment, the last line of the "Hello" method must become:

return "Hello" + " " * self.softspace + str(who)

Note the conversion of the "who" to "str(who)". This forces the Unicode object into a native Python string object.

For details on how to debug COM Servers to find this sort of error, please see

Annotate the class with win32com specific attributes

This is not a complete list of names, simply a list of properties used by this sample.

Property Name

Description

_public_methods_

List of all method names exposed to remote COM clients

_public_attrs_

List of all attribute names exposed to remote COM clients

_readonly_attrs_

List of all attributes which can be accessed, but not set.

We change the class header to become:

class HelloWorld:

_public_methods_ = ['Hello']

_public_attrs_ = ['softspace', 'noCalls']

_readonly_attrs_ = ['noCalls']

def __init__(self):

[Same from here…]

Registering and assigning a CLSID for the object

COM requires that all objects use a unique CLSID and be registered under a "user friendly" name. This documents the process.

Generating the CLSID

Microsoft Visual C++ comes with various tools for generating CLSID's, which are quite suitable. Alternatively, the pythoncom module exports the function CreateGuid() to generate these identifiers.

>>> import pythoncom
>>> print pythoncom.CreateGuid()
{7CC9F362-486D-11D1-BB48-0000E838A65F}

Obviously the GUID that you get will be different than that displayed here.

Preparing for registration of the Class

The win32com package allows yet more annotations to be applied to a class, allowing registration to be effected with 2 lines in your source file. The registration annotations used by this sample are:

Property Name

Description

_reg_clsid_

The CLSID of the COM object

_reg_progid_

The "program ID", or Name, of the COM Server. This is the name the user usually uses to instantiate the object

_reg_desc_

The description of the COM Server. Used primarily for COM browsers.

_reg_class_spec_

A string which represents how Python can create the class instance. The string is of format
[package.subpackage.]module.class

The portion up to the class name must be valid for Python to "import", and the class portion must be a valid attribute in the specified class.

This is (will be!) optional in Python 1.5.

Note there are quite a few other keys available. Also note that these annotations are not required - they just make registration simple. Helper functions in the module win32com.server.register allow you to explicitly specify each of these attributes without attaching them to the class.

The header of our class now becomes:

class HelloWorld:

_reg_clsid_ = "{7CC9F362-486D-11D1-BB48-0000E838A65F}"

_reg_desc_ = "Python Test COM Server"

_reg_progid_ = "Python.TestServer"

# Next line assumes file is "testcomserver.py"

_reg_class_spec_ = "testcomserver.HelloWorld"

_public_methods_ = ['Hello']

[same from here]

Registering the Class

The idiom that most Python COM Servers use is that they register themselves when run as a script (ie, when executed from the command line.) Thus the standard "if __name__=='__main___':" technique works well.

win32com.server.register contains a number of helper functions. The easiest to use is "UseCommandLine".

Registration becomes as simple as:

if __name__=='__main__':
# ni only for 1.4!
import ni, win32com.server.register
win32com.server.register.UseCommandLine(HelloWorld)

Running the script will register our test server.

Testing our Class

For the purposes of this demonstration, we will test the class using Visual Basic. This code should run under any version of Visual Basic, including VBA found in Microsoft Office. Any COM compliant package could be used alternatively. VB has been used just to prove there is no "smoke and mirrors. For information on how to test the server using Python, please see the Quick Start to Client side COM documentation.

This is not a tutorial in VB. The code is just presented! Run it, and it will work!

Debugging the COM Server

When things go wrong in COM Servers, there is often nowhere useful for the Python traceback to go, even if such a traceback is generated.

Rather than discuss how it works, I will just present the procedure to debug your server:

To register a debug version of your class, run the script (as above) but pass in a "--debug" parameter. Eg, for the server above, use the command line "testcomserver.py -debug".

To see the debug output generated (and any print statements you may choose to add!) simply run the script "win32traceutil.py". You can do this simply by double-clicking on it from Windows Explorer.