You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1172 lines
41 KiB

  1. # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
  2. #
  3. # This software is subject to the provisions of the Zope Public License,
  4. # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
  5. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  6. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  7. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  8. # FOR A PARTICULAR PURPOSE.
  9. ident = "$Id$"
  10. from Utility import DOM, Collection
  11. from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter
  12. from StringIO import StringIO
  13. import urllib
  14. class WSDLReader:
  15. """A WSDLReader creates WSDL instances from urls and xml data."""
  16. # Custom subclasses of WSDLReader may wish to implement a caching
  17. # strategy or other optimizations. Because application needs vary
  18. # so widely, we don't try to provide any caching by default.
  19. def loadFromStream(self, file):
  20. """Return a WSDL instance loaded from a file object."""
  21. document = DOM.loadDocument(file)
  22. wsdl = WSDL()
  23. wsdl.location = file.name
  24. wsdl.load(document)
  25. return wsdl
  26. def loadFromURL(self, url):
  27. """Return a WSDL instance loaded from the given url."""
  28. document = DOM.loadFromURL(url)
  29. wsdl = WSDL()
  30. wsdl.location = url
  31. wsdl.load(document)
  32. return wsdl
  33. def loadFromString(self, data):
  34. """Return a WSDL instance loaded from an xml string."""
  35. return self.loadFromStream(StringIO(data))
  36. def loadFromFile(self, filename):
  37. """Return a WSDL instance loaded from the given file."""
  38. file = open(filename, 'rb')
  39. try:
  40. wsdl = self.loadFromStream(file)
  41. finally:
  42. file.close()
  43. return wsdl
  44. class WSDL:
  45. """A WSDL object models a WSDL service description. WSDL objects
  46. may be created manually or loaded from an xml representation
  47. using a WSDLReader instance."""
  48. def __init__(self, targetNamespace=None):
  49. self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
  50. self.documentation = ''
  51. self.location = None
  52. self.document = None
  53. self.name = None
  54. self.services = Collection(self)
  55. self.messages = Collection(self)
  56. self.portTypes = Collection(self)
  57. self.bindings = Collection(self)
  58. self.imports = Collection(self)
  59. self.types = Types(self)
  60. self.extensions = []
  61. def __del__(self):
  62. if self.document is not None:
  63. self.document.unlink()
  64. self.document = None
  65. version = '1.1'
  66. def addService(self, name, documentation=''):
  67. if self.services.has_key(name):
  68. raise WSDLError(
  69. 'Duplicate service element: %s' % name
  70. )
  71. item = Service(name, documentation)
  72. self.services[name] = item
  73. return item
  74. def addMessage(self, name, documentation=''):
  75. if self.messages.has_key(name):
  76. raise WSDLError(
  77. 'Duplicate message element: %s.' % name
  78. )
  79. item = Message(name, documentation)
  80. self.messages[name] = item
  81. return item
  82. def addPortType(self, name, documentation=''):
  83. if self.portTypes.has_key(name):
  84. raise WSDLError(
  85. 'Duplicate portType element: name'
  86. )
  87. item = PortType(name, documentation)
  88. self.portTypes[name] = item
  89. return item
  90. def addBinding(self, name, type, documentation=''):
  91. if self.bindings.has_key(name):
  92. raise WSDLError(
  93. 'Duplicate binding element: %s' % name
  94. )
  95. item = Binding(name, type, documentation)
  96. self.bindings[name] = item
  97. return item
  98. def addImport(self, namespace, location):
  99. item = ImportElement(namespace, location)
  100. self.imports[namespace] = item
  101. return item
  102. def load(self, document):
  103. # We save a reference to the DOM document to ensure that elements
  104. # saved as "extensions" will continue to have a meaningful context
  105. # for things like namespace references. The lifetime of the DOM
  106. # document is bound to the lifetime of the WSDL instance.
  107. self.document = document
  108. definitions = DOM.getElement(document, 'definitions', None, None)
  109. if definitions is None:
  110. raise WSDLError(
  111. 'Missing <definitions> element.'
  112. )
  113. self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
  114. NS_WSDL = DOM.GetWSDLUri(self.version)
  115. self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
  116. None, None)
  117. self.name = DOM.getAttr(definitions, 'name', None, None)
  118. self.documentation = GetDocumentation(definitions)
  119. # Resolve (recursively) any import elements in the document.
  120. imported = {}
  121. while 1:
  122. imports = []
  123. for element in DOM.getElements(definitions, 'import', NS_WSDL):
  124. location = DOM.getAttr(element, 'location')
  125. if not imported.has_key(location):
  126. imports.append(element)
  127. if not imports:
  128. break
  129. for element in imports:
  130. self._import(document, element)
  131. location = DOM.getAttr(element, 'location')
  132. imported[location] = 1
  133. reader = SchemaReader(base_url=self.location)
  134. for element in DOM.getElements(definitions, None, None):
  135. localName = element.localName
  136. if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
  137. if localName == 'schema':
  138. schema = reader.loadFromNode(WSDLToolsAdapter(self), element)
  139. schema.setBaseUrl(self.location)
  140. self.types.addSchema(schema)
  141. else:
  142. self.extensions.append(element)
  143. continue
  144. elif localName == 'message':
  145. name = DOM.getAttr(element, 'name')
  146. docs = GetDocumentation(element)
  147. message = self.addMessage(name, docs)
  148. parts = DOM.getElements(element, 'part', NS_WSDL)
  149. message.load(parts)
  150. continue
  151. elif localName == 'portType':
  152. name = DOM.getAttr(element, 'name')
  153. docs = GetDocumentation(element)
  154. ptype = self.addPortType(name, docs)
  155. operations = DOM.getElements(element, 'operation', NS_WSDL)
  156. ptype.load(operations)
  157. continue
  158. elif localName == 'binding':
  159. name = DOM.getAttr(element, 'name')
  160. type = DOM.getAttr(element, 'type', default=None)
  161. if type is None:
  162. raise WSDLError(
  163. 'Missing type attribute for binding %s.' % name
  164. )
  165. type = type.split(':', 1)[-1]
  166. docs = GetDocumentation(element)
  167. binding = self.addBinding(name, type, docs)
  168. operations = DOM.getElements(element, 'operation', NS_WSDL)
  169. binding.load(operations)
  170. binding.load_ex(GetExtensions(element))
  171. continue
  172. elif localName == 'service':
  173. name = DOM.getAttr(element, 'name')
  174. docs = GetDocumentation(element)
  175. service = self.addService(name, docs)
  176. ports = DOM.getElements(element, 'port', NS_WSDL)
  177. service.load(ports)
  178. service.load_ex(GetExtensions(element))
  179. continue
  180. elif localName == 'types':
  181. self.types.documentation = GetDocumentation(element)
  182. for item in DOM.getElements(element, None, None):
  183. if item.localName == 'schema':
  184. schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
  185. schema.setBaseUrl(self.location)
  186. self.types.addSchema(schema)
  187. else:
  188. self.types.addExtension(item)
  189. continue
  190. def _import(self, document, element):
  191. namespace = DOM.getAttr(element, 'namespace', default=None)
  192. location = DOM.getAttr(element, 'location', default=None)
  193. if namespace is None or location is None:
  194. raise WSDLError(
  195. 'Invalid import element (missing namespace or location).'
  196. )
  197. # Sort-of support relative locations to simplify unit testing. The
  198. # WSDL specification actually doesn't allow relative URLs, so its
  199. # ok that this only works with urls relative to the initial document.
  200. location = urllib.basejoin(self.location, location)
  201. obimport = self.addImport(namespace, location)
  202. obimport._loaded = 1
  203. importdoc = DOM.loadFromURL(location)
  204. try:
  205. if location.find('#') > -1:
  206. idref = location.split('#')[-1]
  207. imported = DOM.getElementById(importdoc, idref)
  208. else:
  209. imported = importdoc.documentElement
  210. if imported is None:
  211. raise WSDLError(
  212. 'Import target element not found for: %s' % location
  213. )
  214. imported_tns = DOM.findTargetNS(imported)
  215. if imported_tns != namespace:
  216. return
  217. if imported.localName == 'definitions':
  218. imported_nodes = imported.childNodes
  219. else:
  220. imported_nodes = [imported]
  221. parent = element.parentNode
  222. for node in imported_nodes:
  223. if node.nodeType != node.ELEMENT_NODE:
  224. continue
  225. child = DOM.importNode(document, node, 1)
  226. parent.appendChild(child)
  227. child.setAttribute('targetNamespace', namespace)
  228. attrsNS = imported._attrsNS
  229. for attrkey in attrsNS.keys():
  230. if attrkey[0] == DOM.NS_XMLNS:
  231. attr = attrsNS[attrkey].cloneNode(1)
  232. child.setAttributeNode(attr)
  233. finally:
  234. importdoc.unlink()
  235. class Element:
  236. """A class that provides common functions for WSDL element classes."""
  237. def __init__(self, name=None, documentation=''):
  238. self.name = name
  239. self.documentation = documentation
  240. self.extensions = []
  241. def addExtension(self, item):
  242. self.extensions.append(item)
  243. class ImportElement(Element):
  244. def __init__(self, namespace, location):
  245. self.namespace = namespace
  246. self.location = location
  247. _loaded = None
  248. class Types(Collection):
  249. default = lambda self,k: k.targetNamespace
  250. def __init__(self, parent):
  251. Collection.__init__(self, parent)
  252. self.documentation = ''
  253. self.extensions = []
  254. def addSchema(self, schema):
  255. name = schema.targetNamespace
  256. self[name] = schema
  257. return schema
  258. def addExtension(self, item):
  259. self.extensions.append(item)
  260. class Message(Element):
  261. def __init__(self, name, documentation=''):
  262. Element.__init__(self, name, documentation)
  263. self.parts = Collection(self)
  264. def addPart(self, name, type=None, element=None):
  265. if self.parts.has_key(name):
  266. raise WSDLError(
  267. 'Duplicate message part element: %s' % name
  268. )
  269. if type is None and element is None:
  270. raise WSDLError(
  271. 'Missing type or element attribute for part: %s' % name
  272. )
  273. item = MessagePart(name)
  274. item.element = element
  275. item.type = type
  276. self.parts[name] = item
  277. return item
  278. def load(self, elements):
  279. for element in elements:
  280. name = DOM.getAttr(element, 'name')
  281. part = MessagePart(name)
  282. self.parts[name] = part
  283. elemref = DOM.getAttr(element, 'element', default=None)
  284. typeref = DOM.getAttr(element, 'type', default=None)
  285. if typeref is None and elemref is None:
  286. raise WSDLError(
  287. 'No type or element attribute for part: %s' % name
  288. )
  289. if typeref is not None:
  290. part.type = ParseTypeRef(typeref, element)
  291. if elemref is not None:
  292. part.element = ParseTypeRef(elemref, element)
  293. class MessagePart(Element):
  294. def __init__(self, name):
  295. Element.__init__(self, name, '')
  296. self.element = None
  297. self.type = None
  298. class PortType(Element):
  299. def __init__(self, name, documentation=''):
  300. Element.__init__(self, name, documentation)
  301. self.operations = Collection(self)
  302. def getWSDL(self):
  303. return self.parent().parent()
  304. def addOperation(self, name, documentation='', parameterOrder=None):
  305. item = Operation(name, documentation, parameterOrder)
  306. self.operations[name] = item
  307. return item
  308. def load(self, elements):
  309. for element in elements:
  310. name = DOM.getAttr(element, 'name')
  311. docs = GetDocumentation(element)
  312. param_order = DOM.getAttr(element, 'parameterOrder', default=None)
  313. if param_order is not None:
  314. param_order = param_order.split(' ')
  315. operation = self.addOperation(name, docs, param_order)
  316. item = DOM.getElement(element, 'input', None, None)
  317. if item is not None:
  318. name = DOM.getAttr(item, 'name')
  319. docs = GetDocumentation(item)
  320. msgref = DOM.getAttr(item, 'message')
  321. message = msgref.split(':', 1)[-1]
  322. operation.setInput(message, name, docs)
  323. item = DOM.getElement(element, 'output', None, None)
  324. if item is not None:
  325. name = DOM.getAttr(item, 'name')
  326. docs = GetDocumentation(item)
  327. msgref = DOM.getAttr(item, 'message')
  328. message = msgref.split(':', 1)[-1]
  329. operation.setOutput(message, name, docs)
  330. for item in DOM.getElements(element, 'fault', None):
  331. name = DOM.getAttr(item, 'name')
  332. docs = GetDocumentation(item)
  333. msgref = DOM.getAttr(item, 'message')
  334. message = msgref.split(':', 1)[-1]
  335. operation.addFault(message, name, docs)
  336. class Operation(Element):
  337. def __init__(self, name, documentation='', parameterOrder=None):
  338. Element.__init__(self, name, documentation)
  339. self.parameterOrder = parameterOrder
  340. self.faults = Collection(self)
  341. self.input = None
  342. self.output = None
  343. def getPortType(self):
  344. return self.parent().parent()
  345. def getInputMessage(self):
  346. if self.input is None:
  347. return None
  348. wsdl = self.getPortType().getWSDL()
  349. return wsdl.messages[self.input.message]
  350. def getOutputMessage(self):
  351. if self.output is None:
  352. return None
  353. wsdl = self.getPortType().getWSDL()
  354. return wsdl.messages[self.output.message]
  355. def getFaultMessage(self, name):
  356. wsdl = self.getPortType().getWSDL()
  357. return wsdl.messages[self.faults[name].message]
  358. def addFault(self, name, message, documentation=''):
  359. if self.faults.has_key(name):
  360. raise WSDLError(
  361. 'Duplicate fault element: %s' % name
  362. )
  363. item = MessageRole('fault', message, name, documentation)
  364. self.faults[name] = item
  365. return item
  366. def setInput(self, message, name='', documentation=''):
  367. self.input = MessageRole('input', message, name, documentation)
  368. return self.input
  369. def setOutput(self, message, name='', documentation=''):
  370. self.output = MessageRole('output', message, name, documentation)
  371. return self.output
  372. class MessageRole(Element):
  373. def __init__(self, type, message, name='', documentation=''):
  374. Element.__init__(self, name, documentation)
  375. self.message = message
  376. self.type = type
  377. class Binding(Element):
  378. def __init__(self, name, type, documentation=''):
  379. Element.__init__(self, name, documentation)
  380. self.operations = Collection(self)
  381. self.type = type
  382. def getWSDL(self):
  383. """Return the WSDL object that contains this binding."""
  384. return self.parent().parent()
  385. def getPortType(self):
  386. """Return the PortType object associated with this binding."""
  387. return self.getWSDL().portTypes[self.type]
  388. def findBinding(self, kind):
  389. for item in self.extensions:
  390. if isinstance(item, kind):
  391. return item
  392. return None
  393. def findBindings(self, kind):
  394. return [ item for item in self.extensions if isinstance(item, kind) ]
  395. def addOperationBinding(self, name, documentation=''):
  396. item = OperationBinding(name, documentation)
  397. self.operations[name] = item
  398. return item
  399. def load(self, elements):
  400. for element in elements:
  401. name = DOM.getAttr(element, 'name')
  402. docs = GetDocumentation(element)
  403. opbinding = self.addOperationBinding(name, docs)
  404. opbinding.load_ex(GetExtensions(element))
  405. item = DOM.getElement(element, 'input', None, None)
  406. if item is not None:
  407. mbinding = MessageRoleBinding('input')
  408. mbinding.documentation = GetDocumentation(item)
  409. opbinding.input = mbinding
  410. mbinding.load_ex(GetExtensions(item))
  411. item = DOM.getElement(element, 'output', None, None)
  412. if item is not None:
  413. mbinding = MessageRoleBinding('output')
  414. mbinding.documentation = GetDocumentation(item)
  415. opbinding.output = mbinding
  416. mbinding.load_ex(GetExtensions(item))
  417. for item in DOM.getElements(element, 'fault', None):
  418. name = DOM.getAttr(item, 'name')
  419. mbinding = MessageRoleBinding('fault', name)
  420. mbinding.documentation = GetDocumentation(item)
  421. opbinding.faults[name] = mbinding
  422. mbinding.load_ex(GetExtensions(item))
  423. def load_ex(self, elements):
  424. for e in elements:
  425. ns, name = e.namespaceURI, e.localName
  426. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
  427. transport = DOM.getAttr(e, 'transport', default=None)
  428. style = DOM.getAttr(e, 'style', default='document')
  429. ob = SoapBinding(transport, style)
  430. self.addExtension(ob)
  431. continue
  432. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
  433. verb = DOM.getAttr(e, 'verb')
  434. ob = HttpBinding(verb)
  435. self.addExtension(ob)
  436. continue
  437. else:
  438. self.addExtension(e)
  439. class OperationBinding(Element):
  440. def __init__(self, name, documentation=''):
  441. Element.__init__(self, name, documentation)
  442. self.input = None
  443. self.output = None
  444. self.faults = Collection(self)
  445. def getBinding(self):
  446. """Return the parent Binding object of the operation binding."""
  447. return self.parent().parent()
  448. def getOperation(self):
  449. """Return the abstract Operation associated with this binding."""
  450. return self.getBinding().getPortType().operations[self.name]
  451. def findBinding(self, kind):
  452. for item in self.extensions:
  453. if isinstance(item, kind):
  454. return item
  455. return None
  456. def findBindings(self, kind):
  457. return [ item for item in self.extensions if isinstance(item, kind) ]
  458. def addInputBinding(self, binding):
  459. if self.input is None:
  460. self.input = MessageRoleBinding('input')
  461. self.input.addExtension(binding)
  462. return binding
  463. def addOutputBinding(self, binding):
  464. if self.output is None:
  465. self.output = MessageRoleBinding('output')
  466. self.output.addExtension(binding)
  467. return binding
  468. def addFaultBinding(self, name, binding):
  469. fault = self.get(name, None)
  470. if fault is None:
  471. fault = MessageRoleBinding('fault', name)
  472. fault.addExtension(binding)
  473. return binding
  474. def load_ex(self, elements):
  475. for e in elements:
  476. ns, name = e.namespaceURI, e.localName
  477. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
  478. soapaction = DOM.getAttr(e, 'soapAction', default=None)
  479. style = DOM.getAttr(e, 'style', default=None)
  480. ob = SoapOperationBinding(soapaction, style)
  481. self.addExtension(ob)
  482. continue
  483. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
  484. location = DOM.getAttr(e, 'location')
  485. ob = HttpOperationBinding(location)
  486. self.addExtension(ob)
  487. continue
  488. else:
  489. self.addExtension(e)
  490. class MessageRoleBinding(Element):
  491. def __init__(self, type, name='', documentation=''):
  492. Element.__init__(self, name, documentation)
  493. self.type = type
  494. def findBinding(self, kind):
  495. for item in self.extensions:
  496. if isinstance(item, kind):
  497. return item
  498. return None
  499. def findBindings(self, kind):
  500. return [ item for item in self.extensions if isinstance(item, kind) ]
  501. def load_ex(self, elements):
  502. for e in elements:
  503. ns, name = e.namespaceURI, e.localName
  504. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  505. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  506. namespace = DOM.getAttr(e, 'namespace', default=None)
  507. parts = DOM.getAttr(e, 'parts', default=None)
  508. use = DOM.getAttr(e, 'use', default=None)
  509. if use is None:
  510. raise WSDLError(
  511. 'Invalid soap:body binding element.'
  512. )
  513. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  514. self.addExtension(ob)
  515. continue
  516. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
  517. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  518. namespace = DOM.getAttr(e, 'namespace', default=None)
  519. name = DOM.getAttr(e, 'name', default=None)
  520. use = DOM.getAttr(e, 'use', default=None)
  521. if use is None or name is None:
  522. raise WSDLError(
  523. 'Invalid soap:fault binding element.'
  524. )
  525. ob = SoapFaultBinding(name, use, namespace, encstyle)
  526. self.addExtension(ob)
  527. continue
  528. elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
  529. 'header', 'headerfault'
  530. ):
  531. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  532. namespace = DOM.getAttr(e, 'namespace', default=None)
  533. message = DOM.getAttr(e, 'message')
  534. part = DOM.getAttr(e, 'part')
  535. use = DOM.getAttr(e, 'use')
  536. if name == 'header':
  537. _class = SoapHeaderBinding
  538. else:
  539. _class = SoapHeaderFaultBinding
  540. ob = _class(message, part, use, namespace, encstyle)
  541. self.addExtension(ob)
  542. continue
  543. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
  544. ob = HttpUrlReplacementBinding()
  545. self.addExtension(ob)
  546. continue
  547. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
  548. ob = HttpUrlEncodedBinding()
  549. self.addExtension(ob)
  550. continue
  551. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
  552. ob = MimeMultipartRelatedBinding()
  553. self.addExtension(ob)
  554. ob.load_ex(GetExtensions(e))
  555. continue
  556. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  557. part = DOM.getAttr(e, 'part', default=None)
  558. type = DOM.getAttr(e, 'type', default=None)
  559. ob = MimeContentBinding(part, type)
  560. self.addExtension(ob)
  561. continue
  562. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  563. part = DOM.getAttr(e, 'part', default=None)
  564. ob = MimeXmlBinding(part)
  565. self.addExtension(ob)
  566. continue
  567. else:
  568. self.addExtension(e)
  569. class Service(Element):
  570. def __init__(self, name, documentation=''):
  571. Element.__init__(self, name, documentation)
  572. self.ports = Collection(self)
  573. def getWSDL(self):
  574. return self.parent().parent()
  575. def addPort(self, name, binding, documentation=''):
  576. item = Port(name, binding, documentation)
  577. self.ports[name] = item
  578. return item
  579. def load(self, elements):
  580. for element in elements:
  581. name = DOM.getAttr(element, 'name', default=None)
  582. docs = GetDocumentation(element)
  583. binding = DOM.getAttr(element, 'binding', default=None)
  584. if name is None or binding is None:
  585. raise WSDLError(
  586. 'Invalid port element.'
  587. )
  588. binding = binding.split(':', 1)[-1]
  589. port = self.addPort(name, binding, docs)
  590. port.load_ex(GetExtensions(element))
  591. def load_ex(self, elements):
  592. for e in elements:
  593. self.addExtension(e)
  594. class Port(Element):
  595. def __init__(self, name, binding, documentation=''):
  596. Element.__init__(self, name, documentation)
  597. self.binding = binding
  598. def getService(self):
  599. """Return the Service object associated with this port."""
  600. return self.parent().parent()
  601. def getBinding(self):
  602. """Return the Binding object that is referenced by this port."""
  603. wsdl = self.getService().getWSDL()
  604. return wsdl.bindings[self.binding]
  605. def getPortType(self):
  606. """Return the PortType object that is referenced by this port."""
  607. wsdl = self.getService().getWSDL()
  608. binding = wsdl.bindings[self.binding]
  609. return wsdl.portTypes[binding.type]
  610. def getAddressBinding(self):
  611. """A convenience method to obtain the extension element used
  612. as the address binding for the port, or None if undefined."""
  613. for item in self.extensions:
  614. if isinstance(item, SoapAddressBinding) or \
  615. isinstance(item, HttpAddressBinding):
  616. return item
  617. raise WSDLError(
  618. 'No address binding found in port.'
  619. )
  620. def load_ex(self, elements):
  621. for e in elements:
  622. ns, name = e.namespaceURI, e.localName
  623. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
  624. location = DOM.getAttr(e, 'location', default=None)
  625. ob = SoapAddressBinding(location)
  626. self.addExtension(ob)
  627. continue
  628. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
  629. location = DOM.getAttr(e, 'location', default=None)
  630. ob = HttpAddressBinding(location)
  631. self.addExtension(ob)
  632. continue
  633. else:
  634. self.addExtension(e)
  635. class SoapBinding:
  636. def __init__(self, transport, style='rpc'):
  637. self.transport = transport
  638. self.style = style
  639. class SoapAddressBinding:
  640. def __init__(self, location):
  641. self.location = location
  642. class SoapOperationBinding:
  643. def __init__(self, soapAction=None, style=None):
  644. self.soapAction = soapAction
  645. self.style = style
  646. class SoapBodyBinding:
  647. def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
  648. if not use in ('literal', 'encoded'):
  649. raise WSDLError(
  650. 'Invalid use attribute value: %s' % use
  651. )
  652. self.encodingStyle = encodingStyle
  653. self.namespace = namespace
  654. if type(parts) in (type(''), type(u'')):
  655. raise WSDLError(
  656. 'The parts argument must be a sequence.'
  657. )
  658. self.parts = parts
  659. self.use = use
  660. class SoapFaultBinding:
  661. def __init__(self, name, use, namespace=None, encodingStyle=None):
  662. if not use in ('literal', 'encoded'):
  663. raise WSDLError(
  664. 'Invalid use attribute value: %s' % use
  665. )
  666. self.encodingStyle = encodingStyle
  667. self.namespace = namespace
  668. self.name = name
  669. self.use = use
  670. class SoapHeaderBinding:
  671. def __init__(self, message, part, use, namespace=None, encodingStyle=None):
  672. if not use in ('literal', 'encoded'):
  673. raise WSDLError(
  674. 'Invalid use attribute value: %s' % use
  675. )
  676. self.encodingStyle = encodingStyle
  677. self.namespace = namespace
  678. self.message = message
  679. self.part = part
  680. self.use = use
  681. tagname = 'header'
  682. class SoapHeaderFaultBinding(SoapHeaderBinding):
  683. tagname = 'headerfault'
  684. class HttpBinding:
  685. def __init__(self, verb):
  686. self.verb = verb
  687. class HttpAddressBinding:
  688. def __init__(self, location):
  689. self.location = location
  690. class HttpOperationBinding:
  691. def __init__(self, location):
  692. self.location = location
  693. class HttpUrlReplacementBinding:
  694. pass
  695. class HttpUrlEncodedBinding:
  696. pass
  697. class MimeContentBinding:
  698. def __init__(self, part=None, type=None):
  699. self.part = part
  700. self.type = type
  701. class MimeXmlBinding:
  702. def __init__(self, part=None):
  703. self.part = part
  704. class MimeMultipartRelatedBinding:
  705. def __init__(self):
  706. self.parts = []
  707. def load_ex(self, elements):
  708. for e in elements:
  709. ns, name = e.namespaceURI, e.localName
  710. if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
  711. self.parts.append(MimePartBinding())
  712. continue
  713. class MimePartBinding:
  714. def __init__(self):
  715. self.items = []
  716. def load_ex(self, elements):
  717. for e in elements:
  718. ns, name = e.namespaceURI, e.localName
  719. if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  720. part = DOM.getAttr(e, 'part', default=None)
  721. type = DOM.getAttr(e, 'type', default=None)
  722. ob = MimeContentBinding(part, type)
  723. self.items.append(ob)
  724. continue
  725. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  726. part = DOM.getAttr(e, 'part', default=None)
  727. ob = MimeXmlBinding(part)
  728. self.items.append(ob)
  729. continue
  730. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  731. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  732. namespace = DOM.getAttr(e, 'namespace', default=None)
  733. parts = DOM.getAttr(e, 'parts', default=None)
  734. use = DOM.getAttr(e, 'use', default=None)
  735. if use is None:
  736. raise WSDLError(
  737. 'Invalid soap:body binding element.'
  738. )
  739. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  740. self.items.append(ob)
  741. continue
  742. class WSDLError(Exception):
  743. pass
  744. def DeclareNSPrefix(writer, prefix, nsuri):
  745. if writer.hasNSPrefix(nsuri):
  746. return
  747. writer.declareNSPrefix(prefix, nsuri)
  748. def ParseTypeRef(value, element):
  749. parts = value.split(':', 1)
  750. if len(parts) == 1:
  751. return (DOM.findTargetNS(element), value)
  752. nsuri = DOM.findNamespaceURI(parts[0], element)
  753. return (nsuri, parts[1])
  754. def ParseQName(value, element):
  755. nameref = value.split(':', 1)
  756. if len(nameref) == 2:
  757. nsuri = DOM.findNamespaceURI(nameref[0], element)
  758. name = nameref[-1]
  759. else:
  760. nsuri = DOM.findTargetNS(element)
  761. name = nameref[-1]
  762. return nsuri, name
  763. def GetDocumentation(element):
  764. docnode = DOM.getElement(element, 'documentation', None, None)
  765. if docnode is not None:
  766. return DOM.getElementText(docnode)
  767. return ''
  768. def GetExtensions(element):
  769. return [ item for item in DOM.getElements(element, None, None)
  770. if item.namespaceURI != DOM.NS_WSDL ]
  771. def FindExtensions(object, kind, t_type=type(())):
  772. if isinstance(kind, t_type):
  773. result = []
  774. namespaceURI, name = kind
  775. return [ item for item in object.extensions
  776. if hasattr(item, 'nodeType') \
  777. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  778. and item.name == name ]
  779. return [ item for item in object.extensions if isinstance(item, kind) ]
  780. def FindExtension(object, kind, t_type=type(())):
  781. if isinstance(kind, t_type):
  782. namespaceURI, name = kind
  783. for item in object.extensions:
  784. if hasattr(item, 'nodeType') \
  785. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  786. and item.name == name:
  787. return item
  788. else:
  789. for item in object.extensions:
  790. if isinstance(item, kind):
  791. return item
  792. return None
  793. class SOAPCallInfo:
  794. """SOAPCallInfo captures the important binding information about a
  795. SOAP operation, in a structure that is easier to work with than
  796. raw WSDL structures."""
  797. def __init__(self, methodName):
  798. self.methodName = methodName
  799. self.inheaders = []
  800. self.outheaders = []
  801. self.inparams = []
  802. self.outparams = []
  803. self.retval = None
  804. encodingStyle = DOM.NS_SOAP_ENC
  805. documentation = ''
  806. soapAction = None
  807. transport = None
  808. namespace = None
  809. location = None
  810. use = 'encoded'
  811. style = 'rpc'
  812. def addInParameter(self, name, type, namespace=None, element_type=0):
  813. """Add an input parameter description to the call info."""
  814. parameter = ParameterInfo(name, type, namespace, element_type)
  815. self.inparams.append(parameter)
  816. return parameter
  817. def addOutParameter(self, name, type, namespace=None, element_type=0):
  818. """Add an output parameter description to the call info."""
  819. parameter = ParameterInfo(name, type, namespace, element_type)
  820. self.outparams.append(parameter)
  821. return parameter
  822. def setReturnParameter(self, name, type, namespace=None, element_type=0):
  823. """Set the return parameter description for the call info."""
  824. parameter = ParameterInfo(name, type, namespace, element_type)
  825. self.retval = parameter
  826. return parameter
  827. def addInHeaderInfo(self, name, type, namespace, element_type=0,
  828. mustUnderstand=0):
  829. """Add an input SOAP header description to the call info."""
  830. headerinfo = HeaderInfo(name, type, namespace, element_type)
  831. if mustUnderstand:
  832. headerinfo.mustUnderstand = 1
  833. self.inheaders.append(headerinfo)
  834. return headerinfo
  835. def addOutHeaderInfo(self, name, type, namespace, element_type=0,
  836. mustUnderstand=0):
  837. """Add an output SOAP header description to the call info."""
  838. headerinfo = HeaderInfo(name, type, namespace, element_type)
  839. if mustUnderstand:
  840. headerinfo.mustUnderstand = 1
  841. self.outheaders.append(headerinfo)
  842. return headerinfo
  843. def getInParameters(self):
  844. """Return a sequence of the in parameters of the method."""
  845. return self.inparams
  846. def getOutParameters(self):
  847. """Return a sequence of the out parameters of the method."""
  848. return self.outparams
  849. def getReturnParameter(self):
  850. """Return param info about the return value of the method."""
  851. return self.retval
  852. def getInHeaders(self):
  853. """Return a sequence of the in headers of the method."""
  854. return self.inheaders
  855. def getOutHeaders(self):
  856. """Return a sequence of the out headers of the method."""
  857. return self.outheaders
  858. class ParameterInfo:
  859. """A ParameterInfo object captures parameter binding information."""
  860. def __init__(self, name, type, namespace=None, element_type=0):
  861. if element_type:
  862. self.element_type = 1
  863. if namespace is not None:
  864. self.namespace = namespace
  865. self.name = name
  866. self.type = type
  867. element_type = 0
  868. namespace = None
  869. default = None
  870. class HeaderInfo(ParameterInfo):
  871. """A HeaderInfo object captures SOAP header binding information."""
  872. def __init__(self, name, type, namespace, element_type=None):
  873. ParameterInfo.__init__(self, name, type, namespace, element_type)
  874. mustUnderstand = 0
  875. actor = None
  876. def callInfoFromWSDL(port, name):
  877. """Return a SOAPCallInfo given a WSDL port and operation name."""
  878. wsdl = port.getService().getWSDL()
  879. binding = port.getBinding()
  880. portType = binding.getPortType()
  881. operation = portType.operations[name]
  882. opbinding = binding.operations[name]
  883. messages = wsdl.messages
  884. callinfo = SOAPCallInfo(name)
  885. addrbinding = port.getAddressBinding()
  886. if not isinstance(addrbinding, SoapAddressBinding):
  887. raise ValueError, 'Unsupported binding type.'
  888. callinfo.location = addrbinding.location
  889. soapbinding = binding.findBinding(SoapBinding)
  890. if soapbinding is None:
  891. raise ValueError, 'Missing soap:binding element.'
  892. callinfo.transport = soapbinding.transport
  893. callinfo.style = soapbinding.style or 'document'
  894. soap_op_binding = opbinding.findBinding(SoapOperationBinding)
  895. if soap_op_binding is not None:
  896. callinfo.soapAction = soap_op_binding.soapAction
  897. callinfo.style = soap_op_binding.style or callinfo.style
  898. parameterOrder = operation.parameterOrder
  899. if operation.input is not None:
  900. message = messages[operation.input.message]
  901. msgrole = opbinding.input
  902. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  903. if mime is not None:
  904. raise ValueError, 'Mime bindings are not supported.'
  905. else:
  906. for item in msgrole.findBindings(SoapHeaderBinding):
  907. part = messages[item.message].parts[item.part]
  908. header = callinfo.addInHeaderInfo(
  909. part.name,
  910. part.element or part.type,
  911. item.namespace,
  912. element_type = part.element and 1 or 0
  913. )
  914. header.encodingStyle = item.encodingStyle
  915. body = msgrole.findBinding(SoapBodyBinding)
  916. if body is None:
  917. raise ValueError, 'Missing soap:body binding.'
  918. callinfo.encodingStyle = body.encodingStyle
  919. callinfo.namespace = body.namespace
  920. callinfo.use = body.use
  921. if body.parts is not None:
  922. parts = []
  923. for name in body.parts:
  924. parts.append(message.parts[name])
  925. else:
  926. parts = message.parts.values()
  927. for part in parts:
  928. callinfo.addInParameter(
  929. part.name,
  930. part.element or part.type,
  931. element_type = part.element and 1 or 0
  932. )
  933. if operation.output is not None:
  934. message = messages[operation.output.message]
  935. msgrole = opbinding.output
  936. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  937. if mime is not None:
  938. raise ValueError, 'Mime bindings are not supported.'
  939. else:
  940. for item in msgrole.findBindings(SoapHeaderBinding):
  941. part = messages[item.message].parts[item.part]
  942. header = callinfo.addOutHeaderInfo(
  943. part.name,
  944. part.element or part.type,
  945. item.namespace,
  946. element_type = part.element and 1 or 0
  947. )
  948. header.encodingStyle = item.encodingStyle
  949. body = msgrole.findBinding(SoapBodyBinding)
  950. if body is None:
  951. raise ValueError, 'Missing soap:body binding.'
  952. callinfo.encodingStyle = body.encodingStyle
  953. callinfo.namespace = body.namespace
  954. callinfo.use = body.use
  955. if body.parts is not None:
  956. parts = []
  957. for name in body.parts:
  958. parts.append(message.parts[name])
  959. else:
  960. parts = message.parts.values()
  961. if parts:
  962. callinfo.setReturnParameter(
  963. parts[0].name,
  964. parts[0].element or parts[0].type,
  965. element_type = parts[0].element and 1 or 0
  966. )
  967. for part in parts[1:]:
  968. callinfo.addOutParameter(
  969. part.name,
  970. part.element or part.type,
  971. element_type = part.element and 1 or 0
  972. )
  973. return callinfo