1############################################################################ 2# Monte M. Goode, LBNL 3# See LBNLCopyright for copyright notice! 4########################################################################### 5 6# main generator engine for new generation generator 7 8# $Id: wsdl2python.py 1402 2007-07-06 22:51:32Z boverhof $ 9 10import os, sys, warnings 11from ZSI import _get_idstr 12from ZSI.wstools.logging import getLogger as _GetLogger 13from ZSI.wstools import WSDLTools 14from ZSI.wstools.WSDLTools import SoapAddressBinding,\ 15 SoapBodyBinding, SoapBinding,MimeContentBinding,\ 16 HttpUrlEncodedBinding 17from ZSI.wstools.XMLSchema import SchemaReader, ElementDeclaration, SchemaError 18from ZSI.typeinterpreter import BaseTypeInterpreter 19from ZSI.generate import WsdlGeneratorError, Wsdl2PythonError 20from containers import * 21from ZSI.generate import utility 22from ZSI.generate.utility import NamespaceAliasDict as NAD 23from ZSI.generate.utility import GetModuleBaseNameFromWSDL 24 25""" 26classes: 27 WriteServiceModule 28 -- composes/writes out client stubs and types module. 29 30 ServiceDescription 31 -- represents a single WSDL service. 32 33 MessageWriter 34 -- represents a single WSDL Message and associated bindings 35 of the port/binding. 36 37 SchemaDescription 38 -- generates classes for defs and decs in the schema instance. 39 40 TypeWriter 41 -- represents a type definition. 42 43 ElementWriter 44 -- represents a element declaration. 45 46""" 47 48class WriteServiceModule: 49 """top level driver class invoked by wsd2py 50 class variables: 51 client_module_suffix -- suffix of client module. 52 types_module_suffix -- suffix of types module. 53 """ 54 client_module_suffix = '_client' 55 messages_module_suffix = '_messages' 56 types_module_suffix = '_types' 57 logger = _GetLogger("WriteServiceModule") 58 59 def __init__(self, wsdl, addressing=False, notification=False, 60 do_extended=False, extPyClasses=None, configParser = None): 61 self._wsdl = wsdl 62 self._addressing = addressing 63 self._notification = notification 64 self._configParser = configParser 65 self.usedNamespaces = None 66 self.services = [] 67 self.client_module_path = None 68 self.types_module_name = None 69 self.types_module_path = None 70 self.messages_module_path = None # used in extended generation 71 self.do_extended = do_extended 72 self.extPyClasses = extPyClasses 73 74 def getClientModuleName(self): 75 """client module name. 76 """ 77 name = GetModuleBaseNameFromWSDL(self._wsdl) 78 if not name: 79 raise WsdlGeneratorError, 'could not determine a service name' 80 81 if self.client_module_suffix is None: 82 return name 83 84 return '%s%s' %(name, self.client_module_suffix) 85 86# def getMessagesModuleName(self): 87# name = GetModuleBaseNameFromWSDL(self._wsdl) 88# if not name: 89# raise WsdlGeneratorError, 'could not determine a service name' 90# 91# if self.messages_module_suffix is None: 92# return name 93# 94# if len(self.messages_module_suffix) == 0: 95# return self.getClientModuleName() 96# 97# return '%s%s' %(name, self.messages_module_suffix) 98 99 def setTypesModuleName(self, name): 100 self.types_module_name = name 101 102 def getTypesModuleName(self): 103 """types module name. 104 """ 105 if self.types_module_name is not None: 106 return self.types_module_name 107 108 name = GetModuleBaseNameFromWSDL(self._wsdl) 109 if not name: 110 raise WsdlGeneratorError, 'could not determine a service name' 111 112 if self.types_module_suffix is None: 113 return name 114 115 return '%s%s' %(name, self.types_module_suffix) 116 117 def setClientModulePath(self, path): 118 """setup module path to where client module before calling fromWsdl. 119 module path to types module eg. MyApp.client 120 """ 121 self.client_module_path = path 122 123 def getTypesModulePath(self): 124 """module path to types module eg. MyApp.types 125 """ 126 return self.types_module_path 127 128# def getMessagesModulePath(self): 129# '''module path to messages module 130# same as types path 131# ''' 132# return self.messages_module_path 133 134 def setTypesModulePath(self, path): 135 """setup module path to where service module before calling fromWsdl. 136 module path to types module eg. MyApp.types 137 """ 138 self.types_module_path = path 139 140# def setMessagesModulePath(self, path): 141# """setup module path to where message module before calling fromWsdl. 142# module path to types module eg. MyApp.types 143# """ 144# self.messages_module_path = path 145 146 def gatherNamespaces(self): 147 '''This method must execute once.. Grab all schemas 148 representing each targetNamespace. 149 ''' 150 if self.usedNamespaces is not None: 151 return 152 153 self.logger.debug('gatherNamespaces') 154 self.usedNamespaces = {} 155 156 # Add all schemas defined in wsdl 157 # to used namespace and to the Alias dict 158 for schema in self._wsdl.types.values(): 159 tns = schema.getTargetNamespace() 160 self.logger.debug('Register schema(%s) -- TNS(%s)'\ 161 %(_get_idstr(schema), tns),) 162 if self.usedNamespaces.has_key(tns) is False: 163 self.usedNamespaces[tns] = [] 164 self.usedNamespaces[tns].append(schema) 165 NAD.add(tns) 166 167 # Add all xsd:import schema instances 168 # to used namespace and to the Alias dict 169 for k,v in SchemaReader.namespaceToSchema.items(): 170 self.logger.debug('Register schema(%s) -- TNS(%s)'\ 171 %(_get_idstr(v), k),) 172 if self.usedNamespaces.has_key(k) is False: 173 self.usedNamespaces[k] = [] 174 self.usedNamespaces[k].append(v) 175 NAD.add(k) 176 177 def writeClient(self, fd, sdClass=None, **kw): 178 """write out client module to file descriptor. 179 Parameters and Keywords arguments: 180 fd -- file descriptor 181 sdClass -- service description class name 182 imports -- list of imports 183 readerclass -- class name of ParsedSoap reader 184 writerclass -- class name of SoapWriter writer 185 """ 186 sdClass = sdClass or ServiceDescription 187 assert issubclass(sdClass, ServiceDescription), \ 188 'parameter sdClass must subclass ServiceDescription' 189 190# header = '%s \n# %s.py \n# generated by %s\n%s\n'\ 191# %('#'*50, self.getClientModuleName(), self.__module__, '#'*50) 192 print >>fd, '#'*50 193 print >>fd, '# file: %s.py' %self.getClientModuleName() 194 print >>fd, '# ' 195 print >>fd, '# client stubs generated by "%s"' %self.__class__ 196 print >>fd, '# %s' %' '.join(sys.argv) 197 print >>fd, '# ' 198 print >>fd, '#'*50 199 200 self.services = [] 201 for service in self._wsdl.services: 202 sd = sdClass(self._addressing, do_extended=self.do_extended, 203 wsdl=self._wsdl) 204 if len(self._wsdl.types) > 0: 205 sd.setTypesModuleName(self.getTypesModuleName(), 206 self.getTypesModulePath()) 207# sd.setMessagesModuleName(self.getMessagesModuleName(), 208# self.getMessagesModulePath()) 209 210 self.gatherNamespaces() 211 sd.fromWsdl(service, **kw) 212 sd.write(fd) 213 self.services.append(sd) 214 215 def writeTypes(self, fd): 216 """write out types module to file descriptor. 217 """ 218 print >>fd, '#'*50 219 print >>fd, '# file: %s.py' %self.getTypesModuleName() 220 print >>fd, '#' 221 print >>fd, '# schema types generated by "%s"' %self.__class__ 222 print >>fd, '# %s' %' '.join(sys.argv) 223 print >>fd, '#' 224 print >>fd, '#'*50 225 226 print >>fd, TypesHeaderContainer() 227 self.gatherNamespaces() 228 for l in self.usedNamespaces.values(): 229 sd = SchemaDescription(do_extended=self.do_extended, 230 extPyClasses=self.extPyClasses) 231 for schema in l: 232 sd.fromSchema(schema) 233 sd.write(fd) 234 235 236class ServiceDescription: 237 """client interface - locator, port, etc classes""" 238 separate_messages = False 239 logger = _GetLogger("ServiceDescription") 240 241 def __init__(self, addressing=False, do_extended=False, wsdl=None): 242 self.typesModuleName = None 243 self.messagesModuleName = None 244 self.wsAddressing = addressing 245 self.imports = ServiceHeaderContainer() 246 self.messagesImports = ServiceHeaderContainer() 247 self.locator = ServiceLocatorContainer() 248 self.bindings = [] 249 self.messages = [] 250 self.do_extended=do_extended 251 self._wsdl = wsdl # None unless do_extended == True 252 253 def setTypesModuleName(self, name, modulePath=None): 254 """The types module to be imported. 255 Parameters 256 name -- name of types module 257 modulePath -- optional path where module is located. 258 """ 259 self.typesModuleName = '%s' %name 260 if modulePath is not None: 261 self.typesModuleName = '%s.%s' %(modulePath,name) 262 263# def setMessagesModuleName(self, name, modulePath=None): 264# '''The types module to be imported. 265# Parameters 266# name -- name of types module 267# modulePath -- optional path where module is located. 268# ''' 269# self.messagesModuleName = '%s' %name 270# if modulePath is not None: 271# self.messagesModuleName = '%s.%s' %(modulePath,name) 272 273 def fromWsdl(self, service, **kw): 274 self.imports.setTypesModuleName(self.typesModuleName) 275# if self.separate_messages: 276# self.messagesImports.setMessagesModuleName(self.messagesModuleName) 277 self.imports.appendImport(kw.get('imports', [])) 278 279 self.locator.setUp(service) 280 281 try: 282 bindings = map(lambda p: p.binding, service.ports) 283 except: 284 warnings.warn('not all ports have binding declared,') 285 bindings = () 286 287 for port in service.ports: 288 if port.binding not in bindings: 289 continue 290 while port.binding in bindings: 291 bindings.remove(port.binding) 292 293 desc = BindingDescription(useWSA=self.wsAddressing, 294 do_extended=self.do_extended, 295 wsdl=self._wsdl) 296 try: 297 desc.setUp(port.getBinding()) 298 except Wsdl2PythonError, ex: 299 self.logger.warning('Skipping port(%s)' %port.name) 300 if len(ex.args): 301 self.logger.warning(ex.args[0]) 302 continue 303 304 desc.setReaderClass(kw.get('readerclass')) 305 desc.setWriterClass(kw.get('writerclass')) 306 for soc in desc.operations: 307 if soc.hasInput() is True: 308 mw = MessageWriter(do_extended=self.do_extended) 309 mw.setUp(soc, port, input=True) 310 self.messages.append(mw) 311 312 if soc.hasOutput() is True: 313 mw = MessageWriter(do_extended=self.do_extended) 314 mw.setUp(soc, port, input=False) 315 self.messages.append(mw) 316 317 self.bindings.append(desc) 318 319 320 def write(self, fd, msg_fd=None): 321 """write out module to file descriptor. 322 fd -- file descriptor to write out service description. 323 msg_fd -- optional file descriptor for messages module. 324 """ 325# if msg_fd != None: 326# print >>fd, self.messagesImports 327# print >>msg_fd, self.imports 328# else: 329 print >>fd, self.imports 330 331 print >>fd, self.locator 332 for m in self.bindings: 333 print >>fd, m 334 335# if msg_fd != None: 336# for m in self.messages: 337# print >>msg_fd, m 338# else: 339 for m in self.messages: 340 print >>fd, m 341 342 343class MessageWriter: 344 logger = _GetLogger("MessageWriter") 345 346 def __init__(self, do_extended=False): 347 """Representation of a WSDL Message and associated WSDL Binding. 348 operation -- 349 boperation -- 350 input -- 351 rpc -- 352 literal -- 353 simple -- 354 """ 355 self.content = None 356 self.do_extended = do_extended 357 358 def __str__(self): 359 if not self.content: 360 raise Wsdl2PythonError, 'Must call setUp.' 361 return self.content.getvalue() 362 363 def setUp(self, soc, port, input=False): 364 assert isinstance(soc, ServiceOperationContainer),\ 365 'expecting a ServiceOperationContainer instance' 366 assert isinstance(port, WSDLTools.Port),\ 367 'expecting a WSDL.Port instance' 368 369 rpc,literal = soc.isRPC(), soc.isLiteral(input) 370 kw,klass = {}, None 371 372 if rpc and literal: 373 klass = ServiceRPCLiteralMessageContainer 374 elif not rpc and literal: 375 kw['do_extended'] = self.do_extended 376 klass = ServiceDocumentLiteralMessageContainer 377 elif rpc and not literal: 378 klass = ServiceRPCEncodedMessageContainer 379 else: 380 raise WsdlGeneratorError, 'doc/enc not supported.' 381 382 self.content = klass(**kw) 383 self.content.setUp(port, soc, input) 384 385 386class SchemaDescription: 387 """generates classes for defs and decs in the schema instance. 388 """ 389 logger = _GetLogger("SchemaDescription") 390 391 def __init__(self, do_extended=False, extPyClasses=None): 392 self.classHead = NamespaceClassHeaderContainer() 393 self.classFoot = NamespaceClassFooterContainer() 394 self.items = [] 395 self.__types = [] 396 self.__elements = [] 397 self.targetNamespace = None 398 self.do_extended=do_extended 399 self.extPyClasses = extPyClasses 400 401 def fromSchema(self, schema): 402 ''' Can be called multiple times, but will not redefine a 403 previously defined type definition or element declaration. 404 ''' 405 ns = schema.getTargetNamespace() 406 assert self.targetNamespace is None or self.targetNamespace == ns,\ 407 'SchemaDescription instance represents %s, not %s'\ 408 %(self.targetNamespace, ns) 409 410 if self.targetNamespace is None: 411 self.targetNamespace = ns 412 413 self.classHead.ns = self.classFoot.ns = ns 414 for item in [t for t in schema.types if t.getAttributeName() not in self.__types]: 415 self.__types.append(item.getAttributeName()) 416 self.items.append(TypeWriter(do_extended=self.do_extended, extPyClasses=self.extPyClasses)) 417 self.items[-1].fromSchemaItem(item) 418 419 for item in [e for e in schema.elements if e.getAttributeName() not in self.__elements]: 420 self.__elements.append(item.getAttributeName()) 421 self.items.append(ElementWriter(do_extended=self.do_extended)) 422 self.items[-1].fromSchemaItem(item) 423 424 def getTypes(self): 425 return self.__types 426 427 def getElements(self): 428 return self.__elements 429 430 def write(self, fd): 431 """write out to file descriptor. 432 """ 433 print >>fd, self.classHead 434 for t in self.items: 435 print >>fd, t 436 print >>fd, self.classFoot 437 438class SchemaItemWriter: 439 """contains/generates a single declaration""" 440 logger = _GetLogger("SchemaItemWriter") 441 442 def __init__(self, do_extended=False, extPyClasses=None): 443 self.content = None 444 self.do_extended=do_extended 445 self.extPyClasses=extPyClasses 446 447 def __str__(self): 448 '''this appears to set up whatever is in self.content.localElements, 449 local elements simpleType|complexType. 450 ''' 451 assert self.content is not None, 'Must call fromSchemaItem to setup.' 452 return str(self.content) 453 454 def fromSchemaItem(self, item): 455 raise NotImplementedError, '' 456 457 458class ElementWriter(SchemaItemWriter): 459 """contains/generates a single declaration""" 460 logger = _GetLogger("ElementWriter") 461 462 def fromSchemaItem(self, item): 463 """set up global elements. 464 """ 465 if item.isElement() is False or item.isLocal() is True: 466 raise TypeError, 'expecting global element declaration: %s' %item.getItemTrace() 467 468 local = False 469 qName = item.getAttribute('type') 470 if not qName: 471 etp = item.content 472 local = True 473 else: 474 etp = item.getTypeDefinition('type') 475 476 if etp is None: 477 if local is True: 478 self.content = ElementLocalComplexTypeContainer(do_extended=self.do_extended) 479 else: 480 self.content = ElementSimpleTypeContainer() 481 elif etp.isLocal() is False: 482 self.content = ElementGlobalDefContainer() 483 elif etp.isSimple() is True: 484 self.content = ElementLocalSimpleTypeContainer() 485 elif etp.isComplex(): 486 self.content = ElementLocalComplexTypeContainer(do_extended=self.do_extended) 487 else: 488 raise Wsdl2PythonError, "Unknown element declaration: %s" %item.getItemTrace() 489 490 self.logger.debug('ElementWriter setUp container "%r", Schema Item "%s"' %( 491 self.content, item.getItemTrace())) 492 493 self.content.setUp(item) 494 495 496class TypeWriter(SchemaItemWriter): 497 """contains/generates a single definition""" 498 logger = _GetLogger("TypeWriter") 499 500 def fromSchemaItem(self, item): 501 if item.isDefinition() is False or item.isLocal() is True: 502 raise TypeError, \ 503 'expecting global type definition not: %s' %item.getItemTrace() 504 505 self.content = None 506 if item.isSimple(): 507 if item.content.isRestriction(): 508 self.content = RestrictionContainer() 509 elif item.content.isUnion(): 510 self.content = UnionContainer() 511 elif item.content.isList(): 512 self.content = ListContainer() 513 else: 514 raise Wsdl2PythonError,\ 515 'unknown simple type definition: %s' %item.getItemTrace() 516 517 self.content.setUp(item) 518 return 519 520 if item.isComplex(): 521 kw = {} 522 if item.content is None or item.content.isModelGroup(): 523 self.content = \ 524 ComplexTypeContainer(\ 525 do_extended=self.do_extended, 526 extPyClasses=self.extPyClasses 527 ) 528 kw['empty'] = item.content is None 529 elif item.content.isSimple(): 530 self.content = ComplexTypeSimpleContentContainer() 531 elif item.content.isComplex(): 532 self.content = \ 533 ComplexTypeComplexContentContainer(\ 534 do_extended=self.do_extended 535 ) 536 else: 537 raise Wsdl2PythonError,\ 538 'unknown complex type definition: %s' %item.getItemTrace() 539 540 self.logger.debug('TypeWriter setUp container "%r", Schema Item "%s"' %( 541 self.content, item.getItemTrace())) 542 543 try: 544 self.content.setUp(item, **kw) 545 except Exception, ex: 546 args = ['Failure in setUp: %s' %item.getItemTrace()] 547 args += ex.args 548 ex.args = tuple(args) 549 raise 550 551 return 552 553 raise TypeError,\ 554 'expecting SimpleType or ComplexType: %s' %item.getItemTrace() 555 556 557 558