1#!/usr/bin/env python 2import inspect 3from cStringIO import StringIO 4import ZSI, string, sys, getopt, urlparse, types, warnings 5from ZSI.wstools import WSDLTools 6from ZSI.ServiceContainer import ServiceSOAPBinding, SimpleWSResource, WSAResource 7 8from ZSI.generate import WsdlGeneratorError, Wsdl2PythonError 9from utility import TextProtect, GetModuleBaseNameFromWSDL, \ 10 NCName_to_ClassName, GetPartsSubNames, TextProtectAttributeName 11from containers import BindingDescription 12from wsdl2python import MessageWriter, WriteServiceModule,\ 13 MessageTypecodeContainer, SchemaDescription 14 15# Split last token 16rsplit = lambda x,sep,: (x[:x.rfind(sep)], x[x.rfind(sep)+1:],) 17if sys.version_info[0:2] == (2, 4, 0, 'final', 0)[0:2]: 18 rsplit = lambda x,sep,: x.rsplit(sep, 1) 19 20 21class SOAPService: 22 def __init__(self, service): 23 self.classdef = StringIO() 24 self.initdef = StringIO() 25 self.location = '' 26 self.methods = [] 27 28 def newMethod(self): 29 '''name -- operation name 30 ''' 31 self.methods.append(StringIO()) 32 return self.methods[-1] 33 34 35class ServiceModuleWriter: 36 '''Creates a skeleton for a SOAP service instance. 37 ''' 38 indent = ' '*4 39 server_module_suffix = '_server' 40 func_aname = TextProtectAttributeName 41 func_aname = staticmethod(func_aname) 42 separate_messages = False 43 44 def __init__(self, base=ServiceSOAPBinding, prefix='soap', 45 service_class=SOAPService): 46 ''' 47 parameters: 48 base -- either a class definition, or a str representing a qualified 49 class name (eg. module.name.classname) 50 prefix -- method prefix. 51 ''' 52 if inspect.isclass(base): 53 self.base_class_name = base.__name__ 54 self.base_module_name = inspect.getmodule(base).__name__ 55 else: 56 self.base_module_name, self.base_class_name = base.rsplit('.', 1) 57 58 self.wsdl = None 59 self.method_prefix = prefix 60 self._service_class = SOAPService 61 62 self.header = None 63 self.imports = None 64 self.messages = [] 65 self._services = None 66 self.types_module_path = None 67 self.types_module_name = None 68 self.messages_module_name = None 69 70 def reset(self): 71 self.header = StringIO() 72 self.imports = StringIO() 73 self.message = [] 74 self._services = {} 75 76 def getIndent(self, level=1): 77 '''return indent. 78 ''' 79 assert 0 < level < 10, 'bad indent level %d' %level 80 return self.indent*level 81 82 def getMethodName(self, method): 83 '''return method name. 84 ''' 85 return '%s_%s' %(self.method_prefix, TextProtect(method)) 86 87 def getClassName(self, name): 88 '''return class name. 89 ''' 90 return NCName_to_ClassName(name) 91 92 def setTypesModuleName(self, name): 93 self.types_module_name = name 94 95 # Backwards compatibility 96 setClientModuleName = setTypesModuleName 97 98 def getTypesModuleName(self): 99 '''return module name. 100 ''' 101 assert self.wsdl is not None, 'initialize, call fromWSDL' 102 if self.types_module_name is not None: 103 return self.types_module_name 104 105 wsm = WriteServiceModule(self.wsdl) 106 return wsm.getTypesModuleName() 107 108 def getServiceModuleName(self): 109 '''return module name. 110 ''' 111 name = GetModuleBaseNameFromWSDL(self.wsdl) 112 if not name: 113 raise WsdlGeneratorError, 'could not determine a service name' 114 115 if self.server_module_suffix is None: 116 return name 117 return '%s%s' %(name, self.server_module_suffix) 118 119 def getTypesModulePath(self): 120 return self.types_module_path 121 getClientModulePath = getTypesModulePath 122 123 def setTypesModulePath(self, path): 124 '''setup module path to where client module before calling fromWSDL. 125 ''' 126 self.types_module_path = path 127 setClientModulePath = setTypesModulePath 128 129 def setUpClassDef(self, service): 130 '''set class definition and class variables. 131 service -- ServiceDescription instance 132 ''' 133 assert isinstance(service, WSDLTools.Service) is True,\ 134 'expecting WSDLTools.Service instance.' 135 136 s = self._services[service.name].classdef 137 138 print >>s, 'class %s(%s):' %(self.getClassName(service.name), self.base_class_name) 139 140 print >>s, '%ssoapAction = {}' % self.getIndent(level=1) 141 print >>s, '%sroot = {}' % self.getIndent(level=1) 142 143 def setUpImports(self): 144 '''set import statements 145 ''' 146 i = self.imports 147 print >>i, 'from ZSI.schema import GED, GTD' 148 print >>i, 'from ZSI.TCcompound import ComplexType, Struct' 149 150 module = self.getTypesModuleName() 151 package = self.getTypesModulePath() 152 if package: 153 module = '%s.%s' %(package, module) 154 155 print >>i, 'from %s import *' %(module) 156 157 print >>i, 'from %s import %s' %(self.base_module_name, self.base_class_name) 158 159 def setUpInitDef(self, service): 160 '''set __init__ function 161 ''' 162 assert isinstance(service, WSDLTools.Service), \ 163 'expecting WSDLTools.Service instance.' 164 165 sd = self._services[service.name] 166 d = sd.initdef 167 168 if sd.location is not None: 169 scheme,netloc,path,params,query,fragment = urlparse.urlparse(sd.location) 170 print >>d, '%sdef __init__(self, post=\'%s\', **kw):' %(self.getIndent(level=1), path) 171 else: 172 print >>d, '%sdef __init__(self, post, **kw):' %self.getIndent(level=1) 173 174 # Require POST initialization value for test implementation 175 if self.base_module_name == inspect.getmodule(ServiceSOAPBinding).__name__: 176 print >>d, '%s%s.__init__(self, post)' %(self.getIndent(level=2), self.base_class_name) 177 return 178 179 # No POST initialization value, obtained from HTTP Request in twisted or wsgi 180 print >>d, '%s%s.__init__(self)' %(self.getIndent(level=2), self.base_class_name) 181 182 def mangle(self, name): 183 return TextProtect(name) 184 185 def getAttributeName(self, name): 186 return self.func_aname(name) 187 188 def setUpMethods(self, port): 189 '''set up all methods representing the port operations. 190 Parameters: 191 port -- Port that defines the operations. 192 ''' 193 assert isinstance(port, WSDLTools.Port), \ 194 'expecting WSDLTools.Port not: ' %type(port) 195 196 sd = self._services.get(port.getService().name) 197 assert sd is not None, 'failed to initialize.' 198 199 binding = port.getBinding() 200 portType = port.getPortType() 201 action_in = '' 202 for bop in binding.operations: 203 try: 204 op = portType.operations[bop.name] 205 except KeyError, ex: 206 raise WsdlGeneratorError,\ 207 'Port(%s) PortType(%s) missing operation(%s) defined in Binding(%s)' \ 208 %(port.name,portType.name,bop.name,binding.name) 209 210 for ext in bop.extensions: 211 if isinstance(ext, WSDLTools.SoapOperationBinding): 212 action_in = ext.soapAction 213 break 214 else: 215 warnings.warn('Port(%s) operation(%s) defined in Binding(%s) missing soapAction' \ 216 %(port.name,op.name,binding.name) 217 ) 218 219 msgin = op.getInputMessage() 220 msgin_name = TextProtect(msgin.name) 221 method_name = self.getMethodName(op.name) 222 223 m = sd.newMethod() 224 print >>m, '%sdef %s(self, ps, **kw):' %(self.getIndent(level=1), method_name) 225 if msgin is not None: 226 print >>m, '%srequest = ps.Parse(%s.typecode)' %(self.getIndent(level=2), msgin_name) 227 else: 228 print >>m, '%s# NO input' %self.getIndent(level=2) 229 230 msgout = op.getOutputMessage() 231 if msgout is not None: 232 msgout_name = TextProtect(msgout.name) 233 print >>m, '%sreturn request,%s()' %(self.getIndent(level=2), msgout_name) 234 else: 235 print >>m, '%s# NO output' % self.getIndent(level=2) 236 print >>m, '%sreturn request,None' % self.getIndent(level=2) 237 238 print >>m, '' 239 print >>m, '%ssoapAction[\'%s\'] = \'%s\'' %(self.getIndent(level=1), action_in, method_name) 240 print >>m, '%sroot[(%s.typecode.nspname,%s.typecode.pname)] = \'%s\'' \ 241 %(self.getIndent(level=1), msgin_name, msgin_name, method_name) 242 243 return 244 245 def setUpHeader(self): 246 print >>self.header, '#'*50 247 print >>self.header, '# file: %s.py' %self.getServiceModuleName() 248 print >>self.header, '#' 249 print >>self.header, '# skeleton generated by "%s"' %self.__class__ 250 print >>self.header, '# %s' %' '.join(sys.argv) 251 print >>self.header, '#' 252 print >>self.header, '#'*50 253 254 def write(self, fd=sys.stdout): 255 '''write out to file descriptor, 256 should not need to override. 257 ''' 258 print >>fd, self.header.getvalue() 259 print >>fd, self.imports.getvalue() 260 261 print >>fd, '# Messages ', 262 for m in self.messages: 263 print >>fd, m 264 265 print >>fd, '' 266 print >>fd, '' 267 print >>fd, '# Service Skeletons' 268 for k,v in self._services.items(): 269 print >>fd, v.classdef.getvalue() 270 print >>fd, v.initdef.getvalue() 271 for s in v.methods: 272 print >>fd, s.getvalue() 273 274 def fromWSDL(self, wsdl): 275 '''setup the service description from WSDL, 276 should not need to override. 277 ''' 278 assert isinstance(wsdl, WSDLTools.WSDL), 'expecting WSDL instance' 279 280 if len(wsdl.services) == 0: 281 raise WsdlGeneratorError, 'No service defined' 282 283 self.reset() 284 self.wsdl = wsdl 285 self.setUpHeader() 286 self.setUpImports() 287 288 for service in wsdl.services: 289 sd = self._service_class(service.name) 290 self._services[service.name] = sd 291 292 for port in service.ports: 293 desc = BindingDescription(wsdl=wsdl) 294 try: 295 desc.setUp(port.getBinding()) 296 except Wsdl2PythonError, ex: 297 continue 298 299 for soc in desc.operations: 300 if not soc.hasInput(): continue 301 302 self.messages.append(MessageWriter()) 303 self.messages[-1].setUp(soc, port, input=True) 304 if soc.hasOutput(): 305 self.messages.append(MessageWriter()) 306 self.messages[-1].setUp(soc, port, input=False) 307 308 for e in port.extensions: 309 if isinstance(e, WSDLTools.SoapAddressBinding): 310 sd.location = e.location 311 312 self.setUpMethods(port) 313 314 self.setUpClassDef(service) 315 self.setUpInitDef(service) 316 317 318class WSAServiceModuleWriter(ServiceModuleWriter): 319 '''Creates a skeleton for a WS-Address service instance. 320 ''' 321 def __init__(self, base=WSAResource, prefix='wsa', service_class=SOAPService, 322 strict=True): 323 ''' 324 Parameters: 325 strict -- check that soapAction and input ws-action do not collide. 326 ''' 327 ServiceModuleWriter.__init__(self, base, prefix, service_class) 328 self.strict = strict 329 330 def createMethodBody(msgInName, msgOutName, **kw): 331 '''return a tuple of strings containing the body of a method. 332 msgInName -- None or a str 333 msgOutName -- None or a str 334 ''' 335 body = [] 336 if msgInName is not None: 337 body.append('request = ps.Parse(%s.typecode)' %msgInName) 338 339 if msgOutName is not None: 340 body.append('return request,%s()' %msgOutName) 341 else: 342 body.append('return request,None') 343 344 return tuple(body) 345 createMethodBody = staticmethod(createMethodBody) 346 347 def setUpClassDef(self, service): 348 '''use soapAction dict for WS-Action input, setup wsAction 349 dict for grabbing WS-Action output values. 350 ''' 351 assert isinstance(service, WSDLTools.Service), \ 352 'expecting WSDLTools.Service instance' 353 354 s = self._services[service.name].classdef 355 print >>s, 'class %s(%s):' %(self.getClassName(service.name), self.base_class_name) 356 print >>s, '%ssoapAction = {}' % self.getIndent(level=1) 357 print >>s, '%swsAction = {}' % self.getIndent(level=1) 358 print >>s, '%sroot = {}' % self.getIndent(level=1) 359 360 def setUpMethods(self, port): 361 '''set up all methods representing the port operations. 362 Parameters: 363 port -- Port that defines the operations. 364 ''' 365 assert isinstance(port, WSDLTools.Port), \ 366 'expecting WSDLTools.Port not: ' %type(port) 367 368 binding = port.getBinding() 369 portType = port.getPortType() 370 service = port.getService() 371 s = self._services[service.name] 372 for bop in binding.operations: 373 try: 374 op = portType.operations[bop.name] 375 except KeyError, ex: 376 raise WsdlGeneratorError,\ 377 'Port(%s) PortType(%s) missing operation(%s) defined in Binding(%s)' \ 378 %(port.name, portType.name, op.name, binding.name) 379 380 soap_action = wsaction_in = wsaction_out = None 381 if op.input is not None: 382 wsaction_in = op.getInputAction() 383 if op.output is not None: 384 wsaction_out = op.getOutputAction() 385 386 for ext in bop.extensions: 387 if isinstance(ext, WSDLTools.SoapOperationBinding) is False: continue 388 soap_action = ext.soapAction 389 if not soap_action: break 390 if wsaction_in is None: break 391 if wsaction_in == soap_action: break 392 if self.strict is False: 393 warnings.warn(\ 394 'Port(%s) operation(%s) in Binding(%s) soapAction(%s) != WS-Action(%s)' \ 395 %(port.name, op.name, binding.name, soap_action, wsaction_in), 396 ) 397 break 398 raise WsdlGeneratorError,\ 399 'Port(%s) operation(%s) in Binding(%s) soapAction(%s) MUST match WS-Action(%s)' \ 400 %(port.name, op.name, binding.name, soap_action, wsaction_in) 401 402 method_name = self.getMethodName(op.name) 403 404 m = s.newMethod() 405 print >>m, '%sdef %s(self, ps, address):' %(self.getIndent(level=1), method_name) 406 407 msgin_name = msgout_name = None 408 msgin,msgout = op.getInputMessage(),op.getOutputMessage() 409 if msgin is not None: 410 msgin_name = TextProtect(msgin.name) 411 if msgout is not None: 412 msgout_name = TextProtect(msgout.name) 413 414 indent = self.getIndent(level=2) 415 for l in self.createMethodBody(msgin_name, msgout_name): 416 print >>m, indent + l 417 418 print >>m, '' 419 print >>m, '%ssoapAction[\'%s\'] = \'%s\'' %(self.getIndent(level=1), wsaction_in, method_name) 420 print >>m, '%swsAction[\'%s\'] = \'%s\'' %(self.getIndent(level=1), method_name, wsaction_out) 421 print >>m, '%sroot[(%s.typecode.nspname,%s.typecode.pname)] = \'%s\'' \ 422 %(self.getIndent(level=1), msgin_name, msgin_name, method_name) 423 424