Developer Forums | About Us | Site Map


Useful Lists

Web Host
site hosted by netplex

Online Manuals

Python SOAP libraries, Part 3
By Mike Olson and Uche Ogbuji - 2004-09-28 Page:  1 2 3

ZSI and namespaces

As the next test, you will expand the ZSI client so that it does support methods inside of a namespace. There are some hurdles to overcome to do this; but Rich Salz, the author of the ZSI package, says that support for namespaces on methods is coming soon. He says that his intended approach is to use a new feature in Python 2.2 that allows you to define arbitrary attributes to functions. You would add a namespace attribute on a function definition, and then the ZSI-dispatching code would query for this attribute to determine which namespace the method is defined in. For future reference, you can add any attribute to a function by setting it on the function object. Below is a short example:

>>> def foo(a):
...    pass
>>> foo.namespace = ""
>>> print foo.namespace


Salz also mentioned that he might add support for other attributes on functions besides namespace information, such as argument typing information. This would allow ZSI to typecheck incoming arguments before the function is called.

At any rate, to make the ZSI server namespace aware, you need to add a bit of logic to the getMonth method (and any other method that we would like to be in a namespace). The first thing that you can try is to get a ClientBinding for the current invocation. This contains all of the relevant information about the call, including its namespace. (By default, ZSI ignores the namespace.) You can then compare the namespace from the ClientBinding to the namespace that you are expecting. If there is a match, then you can return the proper results; if not, you can throw an exception to say that the method was not defined. Listing 3 shows an example of how the refactored getMonth method would look.

Listing 3. Refactored getMonth method

def getMonth(year, month):
  cb = dispatch.GetClientBinding()
  if cb.GetNS() != CAL_NS:
    raise TypeError, "Unimplemented method %s %s" % (cb.GetNS(),name)
  return calendar.month(year, month)

Unfortunately, to use this technique, you would need to add this logic to every function that is defined in the module. Listing 4 takes a more general approach that will allow all of the functions that you define in a module to be easily defined inside of a namespace. You can even easily define the same method in multiple namespaces, or multiple methods with different namespaces (but the same method names). To accomplish this, the function called _functionMap will generically map a request using a mapping dictionary. The dictionary is keyed off a namespace, and each entry references a function to be invoked when a request in the corresponding namespace is received. Then, to give the appearance of top-level functions in the module (as required by ZSI), you use a simple lambda that will stand in the place of the actual function definition. In the case of getMonth, the getMonth lambda is called; this calls _functionMap, which looks at the namespace of the call, as well as the mapping of functions available. If a match is found, the function is invoked; if not, an exception is raised. Now, to add new functions to the module, you would define a function and a corresponding lambda.

One thing to note: you need to put a reference to _functionMap in the defaulted arguments of each lambda definition. This is because when you are done creating the lambdas, you want to delete the module's reference to the _functionMap function; if you don't, this function would be exposed over your Web services interface, as ZSI allows any method defined at the top level to be called.

Listing 4 shows the file, which implements the namespace-aware server in ZSI.

Listing 4. A more general approach

#!/usr/bin/env python

import sys, calendar

#Import the ZSI machinery
from ZSI import dispatch

CAL_NS = ""

#The actual implementations
def getMonth(year, month):
    return calendar.month(year, month)

def getYear(year):
  return calendar.calendar(year)

#Generic function to check the namespace
def _functionMap(name,mapping,*args):
  cb = dispatch.GetClientBinding()
  func = mapping.get(cb.GetNS())
  if func is None:
    raise TypeError, "Unimplemented method %s %s" % (cb.GetNS(),name)
  return apply(func,args)

#Publicly defined methods
getMonthMap = {CAL_NS:getMonth,
getMonth = lambda year,month,_functionMap=_functionMap,map=getMonthMap:_functionMap

getYearMap = {CAL_NS:getYear,
getYear = lambda year,_functionMap=_functionMap,map=getYearMap:_functionMap("getYear",map,year)

#Delete this so it is not available as a service.
del _functionMap

print "Starting server..."

Coming soon

In the next installment of this column, we will compare some different distributed programming technologies and look at some of the performance characteristics of each. We will compare SOAP, CORBA (Common Object Request Broker Architecture), XML-RPC, and a roll-your-own implementation written with the pickle library. We will look at the message overhead, application memory footprint, message transmission times, and relative source code size needed when implementing each of these methods.

View Python SOAP libraries, Part 3 Discussion

Page:  1 2 3 Next Page: Resources

First published by IBM developerWorks

Copyright 2004-2024 All rights reserved.
Article copyright and all rights retained by the author.