1 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
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.
13 from cStringIO import StringIO
14 from Namespaces import OASIS, XMLNS, WSA, WSA_LIST, WSAW_LIST, WSRF_V1_2, WSRF
15 from Utility import Collection, CollectionNS, DOM, ElementProxy, basejoin
16 from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter
20 """A WSDLReader creates WSDL instances from urls and xml data."""
22 # Custom subclasses of WSDLReader may wish to implement a caching
23 # strategy or other optimizations. Because application needs vary
24 # so widely, we don't try to provide any caching by default.
26 def loadFromStream(self, stream, name=None):
27 """Return a WSDL instance loaded from a stream object."""
28 document = DOM.loadDocument(stream)
32 elif hasattr(stream, 'name'):
33 wsdl.location = stream.name
37 def loadFromURL(self, url):
38 """Return a WSDL instance loaded from the given url."""
39 document = DOM.loadFromURL(url)
45 def loadFromString(self, data):
46 """Return a WSDL instance loaded from an xml string."""
47 return self.loadFromStream(StringIO(data))
49 def loadFromFile(self, filename):
50 """Return a WSDL instance loaded from the given file."""
51 file = open(filename, 'rb')
53 wsdl = self.loadFromStream(file)
59 """A WSDL object models a WSDL service description. WSDL objects
60 may be created manually or loaded from an xml representation
61 using a WSDLReader instance."""
63 def __init__(self, targetNamespace=None, strict=1):
64 self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
65 self.documentation = ''
69 self.services = CollectionNS(self)
70 self.messages = CollectionNS(self)
71 self.portTypes = CollectionNS(self)
72 self.bindings = CollectionNS(self)
73 self.imports = Collection(self)
74 self.types = Types(self)
79 if self.document is not None:
80 self.document.unlink()
84 def addService(self, name, documentation='', targetNamespace=None):
85 if self.services.has_key(name):
87 'Duplicate service element: %s' % name
89 item = Service(name, documentation)
91 item.targetNamespace = targetNamespace
92 self.services[name] = item
95 def addMessage(self, name, documentation='', targetNamespace=None):
96 if self.messages.has_key(name):
98 'Duplicate message element: %s.' % name
100 item = Message(name, documentation)
102 item.targetNamespace = targetNamespace
103 self.messages[name] = item
106 def addPortType(self, name, documentation='', targetNamespace=None):
107 if self.portTypes.has_key(name):
109 'Duplicate portType element: name'
111 item = PortType(name, documentation)
113 item.targetNamespace = targetNamespace
114 self.portTypes[name] = item
117 def addBinding(self, name, type, documentation='', targetNamespace=None):
118 if self.bindings.has_key(name):
120 'Duplicate binding element: %s' % name
122 item = Binding(name, type, documentation)
124 item.targetNamespace = targetNamespace
125 self.bindings[name] = item
128 def addImport(self, namespace, location):
129 item = ImportElement(namespace, location)
130 self.imports[namespace] = item
134 """ Generate a DOM representation of the WSDL instance.
135 Not dealing with generating XML Schema, thus the targetNamespace
136 of all XML Schema elements or types used by WSDL message parts
137 needs to be specified via import information items.
139 namespaceURI = DOM.GetWSDLUri(self.version)
140 self.document = DOM.createDocument(namespaceURI ,'wsdl:definitions')
142 # Set up a couple prefixes for easy reading.
143 child = DOM.getElement(self.document, None)
144 child.setAttributeNS(None, 'targetNamespace', self.targetNamespace)
145 child.setAttributeNS(XMLNS.BASE, 'xmlns:wsdl', namespaceURI)
146 child.setAttributeNS(XMLNS.BASE, 'xmlns:xsd', 'http://www.w3.org/1999/XMLSchema')
147 child.setAttributeNS(XMLNS.BASE, 'xmlns:soap', 'http://schemas.xmlsoap.org/wsdl/soap/')
148 child.setAttributeNS(XMLNS.BASE, 'xmlns:tns', self.targetNamespace)
151 child.setAttributeNS(None, 'name', self.name)
154 for item in self.imports:
157 for item in self.messages:
160 for item in self.portTypes:
163 for item in self.bindings:
166 for item in self.services:
169 def load(self, document):
170 # We save a reference to the DOM document to ensure that elements
171 # saved as "extensions" will continue to have a meaningful context
172 # for things like namespace references. The lifetime of the DOM
173 # document is bound to the lifetime of the WSDL instance.
174 self.document = document
176 definitions = DOM.getElement(document, 'definitions', None, None)
177 if definitions is None:
179 'Missing <definitions> element.'
181 self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
182 NS_WSDL = DOM.GetWSDLUri(self.version)
184 self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
186 self.name = DOM.getAttr(definitions, 'name', None, None)
187 self.documentation = GetDocumentation(definitions)
190 # Retrieve all <wsdl:import>'s, append all children of imported
191 # document to main document. First iteration grab all original
192 # <wsdl:import>'s from document, second iteration grab all
193 # "imported" <wsdl:imports> from document, etc break out when
194 # no more <wsdl:import>'s.
197 base_location = self.location
201 for element in DOM.getElements(definitions, 'import', NS_WSDL):
202 location = DOM.getAttr(element, 'location')
204 if base_location is not None:
205 location = basejoin(base_location, location)
207 if location not in imported:
209 self._import(document, element, base_location)
210 imported.append(location)
212 definitions.removeChild(element)
217 # No more <wsdl:import>'s, now load up all other
218 # WSDL information items.
220 for element in DOM.getElements(definitions, None, None):
221 targetNamespace = DOM.getAttr(element, 'targetNamespace')
222 localName = element.localName
224 if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
225 if localName == 'schema':
226 tns = DOM.getAttr(element, 'targetNamespace')
227 reader = SchemaReader(base_url=self.imports[tns].location)
228 schema = reader.loadFromNode(WSDLToolsAdapter(self),
230 # schema.setBaseUrl(self.location)
231 self.types.addSchema(schema)
233 self.extensions.append(element)
236 elif localName == 'message':
237 name = DOM.getAttr(element, 'name')
238 docs = GetDocumentation(element)
239 message = self.addMessage(name, docs, targetNamespace)
240 parts = DOM.getElements(element, 'part', NS_WSDL)
244 elif localName == 'portType':
245 name = DOM.getAttr(element, 'name')
246 docs = GetDocumentation(element)
247 ptype = self.addPortType(name, docs, targetNamespace)
248 #operations = DOM.getElements(element, 'operation', NS_WSDL)
249 #ptype.load(operations)
253 elif localName == 'binding':
254 name = DOM.getAttr(element, 'name')
255 type = DOM.getAttr(element, 'type', default=None)
258 'Missing type attribute for binding %s.' % name
260 type = ParseQName(type, element)
261 docs = GetDocumentation(element)
262 binding = self.addBinding(name, type, docs, targetNamespace)
263 operations = DOM.getElements(element, 'operation', NS_WSDL)
264 binding.load(operations)
265 binding.load_ex(GetExtensions(element))
268 elif localName == 'service':
269 name = DOM.getAttr(element, 'name')
270 docs = GetDocumentation(element)
271 service = self.addService(name, docs, targetNamespace)
272 ports = DOM.getElements(element, 'port', NS_WSDL)
274 service.load_ex(GetExtensions(element))
277 elif localName == 'types':
278 self.types.documentation = GetDocumentation(element)
279 base_location = DOM.getAttr(element, 'base-location')
281 element.removeAttribute('base-location')
282 base_location = base_location or self.location
283 reader = SchemaReader(base_url=base_location)
284 for item in DOM.getElements(element, None, None):
285 if item.localName == 'schema':
286 schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
287 # XXX <types> could have been imported
288 #schema.setBaseUrl(self.location)
289 schema.setBaseUrl(base_location)
290 self.types.addSchema(schema)
292 self.types.addExtension(item)
293 # XXX remove the attribute
294 # element.removeAttribute('base-location')
297 def _import(self, document, element, base_location=None):
298 '''Algo take <import> element's children, clone them,
299 and add them to the main document. Support for relative
300 locations is a bit complicated. The orig document context
301 is lost, so we need to store base location in DOM elements
302 representing <types>, by creating a special temporary
303 "base-location" attribute, and <import>, by resolving
304 the relative "location" and storing it as "location".
306 document -- document we are loading
307 element -- DOM Element representing <import>
308 base_location -- location of document from which this
309 <import> was gleaned.
311 namespace = DOM.getAttr(element, 'namespace', default=None)
312 location = DOM.getAttr(element, 'location', default=None)
313 if namespace is None or location is None:
315 'Invalid import element (missing namespace or location).'
318 location = basejoin(base_location, location)
319 element.setAttributeNS(None, 'location', location)
321 obimport = self.addImport(namespace, location)
324 importdoc = DOM.loadFromURL(location)
326 if location.find('#') > -1:
327 idref = location.split('#')[-1]
328 imported = DOM.getElementById(importdoc, idref)
330 imported = importdoc.documentElement
333 'Import target element not found for: %s' % location
336 imported_tns = DOM.findTargetNS(imported)
337 if imported_tns != namespace:
340 if imported.localName == 'definitions':
341 imported_nodes = imported.childNodes
343 imported_nodes = [imported]
344 parent = element.parentNode
346 parent.removeChild(element)
348 for node in imported_nodes:
349 if node.nodeType != node.ELEMENT_NODE:
351 child = DOM.importNode(document, node, 1)
352 parent.appendChild(child)
353 child.setAttribute('targetNamespace', namespace)
354 attrsNS = imported._attrsNS
355 for attrkey in attrsNS.keys():
356 if attrkey[0] == DOM.NS_XMLNS:
357 attr = attrsNS[attrkey].cloneNode(1)
358 child.setAttributeNode(attr)
360 #XXX Quick Hack, should be in WSDL Namespace.
361 if child.localName == 'import':
362 rlocation = child.getAttributeNS(None, 'location')
363 alocation = basejoin(location, rlocation)
364 child.setAttribute('location', alocation)
365 elif child.localName == 'types':
366 child.setAttribute('base-location', location)
373 """A class that provides common functions for WSDL element classes."""
374 def __init__(self, name=None, documentation=''):
376 self.documentation = documentation
379 def addExtension(self, item):
380 item.parent = weakref.ref(self)
381 self.extensions.append(item)
384 """Return the WSDL object that contains this information item."""
387 # skip any collections
388 if isinstance(parent, WSDL):
390 try: parent = parent.parent()
396 class ImportElement(Element):
397 def __init__(self, namespace, location):
398 self.namespace = namespace
399 self.location = location
402 # """Return the WSDL object that contains this Message Part."""
403 # return self.parent().parent()
406 wsdl = self.getWSDL()
407 ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
408 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'import')
409 epc.setAttributeNS(None, 'namespace', self.namespace)
410 epc.setAttributeNS(None, 'location', self.location)
415 class Types(Collection):
416 default = lambda self,k: k.targetNamespace
417 def __init__(self, parent):
418 Collection.__init__(self, parent)
419 self.documentation = ''
422 def addSchema(self, schema):
423 name = schema.targetNamespace
427 def addExtension(self, item):
428 self.extensions.append(item)
431 class Message(Element):
432 def __init__(self, name, documentation=''):
433 Element.__init__(self, name, documentation)
434 self.parts = Collection(self)
436 def addPart(self, name, type=None, element=None):
437 if self.parts.has_key(name):
439 'Duplicate message part element: %s' % name
441 if type is None and element is None:
443 'Missing type or element attribute for part: %s' % name
445 item = MessagePart(name)
446 item.element = element
448 self.parts[name] = item
451 def load(self, elements):
452 for element in elements:
453 name = DOM.getAttr(element, 'name')
454 part = MessagePart(name)
455 self.parts[name] = part
456 elemref = DOM.getAttr(element, 'element', default=None)
457 typeref = DOM.getAttr(element, 'type', default=None)
458 if typeref is None and elemref is None:
460 'No type or element attribute for part: %s' % name
462 if typeref is not None:
463 part.type = ParseTypeRef(typeref, element)
464 if elemref is not None:
465 part.element = ParseTypeRef(elemref, element)
467 # def getElementDeclaration(self):
468 # """Return the XMLSchema.ElementDeclaration instance or None"""
471 # nsuri,name = self.element
472 # wsdl = self.getWSDL()
473 # if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].elements.has_key(name):
474 # element = wsdl.types[nsuri].elements[name]
477 # def getTypeDefinition(self):
478 # """Return the XMLSchema.TypeDefinition instance or None"""
481 # nsuri,name = self.type
482 # wsdl = self.getWSDL()
483 # if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].types.has_key(name):
484 # type = wsdl.types[nsuri].types[name]
488 # """Return the WSDL object that contains this Message Part."""
489 # return self.parent().parent()
492 wsdl = self.getWSDL()
493 ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
494 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'message')
495 epc.setAttributeNS(None, 'name', self.name)
497 for part in self.parts:
498 part.toDom(epc._getNode())
501 class MessagePart(Element):
502 def __init__(self, name):
503 Element.__init__(self, name, '')
508 # """Return the WSDL object that contains this Message Part."""
509 # return self.parent().parent().parent().parent()
511 def getTypeDefinition(self):
512 wsdl = self.getWSDL()
513 nsuri,name = self.type
514 schema = wsdl.types.get(nsuri, {})
515 return schema.get(name)
517 def getElementDeclaration(self):
518 wsdl = self.getWSDL()
519 nsuri,name = self.element
520 schema = wsdl.types.get(nsuri, {})
521 return schema.get(name)
523 def toDom(self, node):
524 """node -- node representing message"""
525 wsdl = self.getWSDL()
526 ep = ElementProxy(None, node)
527 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'part')
528 epc.setAttributeNS(None, 'name', self.name)
530 if self.element is not None:
531 ns,name = self.element
532 prefix = epc.getPrefix(ns)
533 epc.setAttributeNS(None, 'element', '%s:%s'%(prefix,name))
534 elif self.type is not None:
536 prefix = epc.getPrefix(ns)
537 epc.setAttributeNS(None, 'type', '%s:%s'%(prefix,name))
540 class PortType(Element):
541 '''PortType has a anyAttribute, thus must provide for an extensible
542 mechanism for supporting such attributes. ResourceProperties is
543 specified in WS-ResourceProperties. wsa:Action is specified in
547 name -- name attribute
548 resourceProperties -- optional. wsr:ResourceProperties attribute,
549 value is a QName this is Parsed into a (namespaceURI, name)
550 that represents a Global Element Declaration.
554 def __init__(self, name, documentation=''):
555 Element.__init__(self, name, documentation)
556 self.operations = Collection(self)
557 self.resourceProperties = None
560 # return self.parent().parent()
562 def getTargetNamespace(self):
563 return self.targetNamespace or self.getWSDL().targetNamespace
565 def getResourceProperties(self):
566 return self.resourceProperties
568 def addOperation(self, name, documentation='', parameterOrder=None):
569 item = Operation(name, documentation, parameterOrder)
570 self.operations[name] = item
573 def load(self, element):
574 self.name = DOM.getAttr(element, 'name')
575 self.documentation = GetDocumentation(element)
576 self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
578 for nsuri in WSRF_V1_2.PROPERTIES.XSD_LIST:
579 if DOM.hasAttr(element, 'ResourceProperties', nsuri):
580 rpref = DOM.getAttr(element, 'ResourceProperties', nsuri)
581 self.resourceProperties = ParseQName(rpref, element)
583 NS_WSDL = DOM.GetWSDLUri(self.getWSDL().version)
584 elements = DOM.getElements(element, 'operation', NS_WSDL)
585 for element in elements:
586 name = DOM.getAttr(element, 'name')
587 docs = GetDocumentation(element)
588 param_order = DOM.getAttr(element, 'parameterOrder', default=None)
589 if param_order is not None:
590 param_order = param_order.split(' ')
591 operation = self.addOperation(name, docs, param_order)
593 item = DOM.getElement(element, 'input', None, None)
595 name = DOM.getAttr(item, 'name')
596 docs = GetDocumentation(item)
597 msgref = DOM.getAttr(item, 'message')
598 message = ParseQName(msgref, item)
599 for WSA in WSA_LIST + WSAW_LIST:
600 action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
602 operation.setInput(message, name, docs, action)
604 item = DOM.getElement(element, 'output', None, None)
606 name = DOM.getAttr(item, 'name')
607 docs = GetDocumentation(item)
608 msgref = DOM.getAttr(item, 'message')
609 message = ParseQName(msgref, item)
610 for WSA in WSA_LIST + WSAW_LIST:
611 action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
613 operation.setOutput(message, name, docs, action)
615 for item in DOM.getElements(element, 'fault', None):
616 name = DOM.getAttr(item, 'name')
617 docs = GetDocumentation(item)
618 msgref = DOM.getAttr(item, 'message')
619 message = ParseQName(msgref, item)
620 for WSA in WSA_LIST + WSAW_LIST:
621 action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
623 operation.addFault(message, name, docs, action)
626 wsdl = self.getWSDL()
628 ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
629 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'portType')
630 epc.setAttributeNS(None, 'name', self.name)
631 if self.resourceProperties:
632 ns,name = self.resourceProperties
633 prefix = epc.getPrefix(ns)
634 epc.setAttributeNS(WSRF.PROPERTIES.LATEST, 'ResourceProperties',
635 '%s:%s'%(prefix,name))
637 for op in self.operations:
638 op.toDom(epc._getNode())
642 class Operation(Element):
643 def __init__(self, name, documentation='', parameterOrder=None):
644 Element.__init__(self, name, documentation)
645 self.parameterOrder = parameterOrder
646 self.faults = Collection(self)
651 """Return the WSDL object that contains this Operation."""
652 return self.parent().parent().parent().parent()
654 def getPortType(self):
655 return self.parent().parent()
657 def getInputAction(self):
658 """wsa:Action attribute"""
659 return GetWSAActionInput(self)
661 def getInputMessage(self):
662 if self.input is None:
664 wsdl = self.getPortType().getWSDL()
665 return wsdl.messages[self.input.message]
667 def getOutputAction(self):
668 """wsa:Action attribute"""
669 return GetWSAActionOutput(self)
671 def getOutputMessage(self):
672 if self.output is None:
674 wsdl = self.getPortType().getWSDL()
675 return wsdl.messages[self.output.message]
677 def getFaultAction(self, name):
678 """wsa:Action attribute"""
679 return GetWSAActionFault(self, name)
681 def getFaultMessage(self, name):
682 wsdl = self.getPortType().getWSDL()
683 return wsdl.messages[self.faults[name].message]
685 def addFault(self, message, name, documentation='', action=None):
686 if self.faults.has_key(name):
688 'Duplicate fault element: %s' % name
690 item = MessageRole('fault', message, name, documentation, action)
691 self.faults[name] = item
694 def setInput(self, message, name='', documentation='', action=None):
695 self.input = MessageRole('input', message, name, documentation, action)
696 self.input.parent = weakref.ref(self)
699 def setOutput(self, message, name='', documentation='', action=None):
700 self.output = MessageRole('output', message, name, documentation, action)
701 self.output.parent = weakref.ref(self)
704 def toDom(self, node):
705 wsdl = self.getWSDL()
707 ep = ElementProxy(None, node)
708 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
709 epc.setAttributeNS(None, 'name', self.name)
710 node = epc._getNode()
712 self.input.toDom(node)
714 self.output.toDom(node)
715 for fault in self.faults:
719 class MessageRole(Element):
720 def __init__(self, type, message, name='', documentation='', action=None):
721 Element.__init__(self, name, documentation)
722 self.message = message
727 """Return the WSDL object that contains this information item."""
730 # skip any collections
731 if isinstance(parent, WSDL):
733 try: parent = parent.parent()
738 def getMessage(self):
739 """Return the WSDL object that represents the attribute message
740 (namespaceURI, name) tuple
742 wsdl = self.getWSDL()
743 return wsdl.messages[self.message]
745 def toDom(self, node):
746 wsdl = self.getWSDL()
748 ep = ElementProxy(None, node)
749 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
750 if not isinstance(self.message, basestring) and len(self.message) == 2:
751 ns,name = self.message
752 prefix = epc.getPrefix(ns)
753 epc.setAttributeNS(None, 'message', '%s:%s' %(prefix,name))
755 epc.setAttributeNS(None, 'message', self.message)
758 epc.setAttributeNS(WSA.ADDRESS, 'Action', self.action)
761 epc.setAttributeNS(None, 'name', self.name)
764 class Binding(Element):
765 def __init__(self, name, type, documentation=''):
766 Element.__init__(self, name, documentation)
767 self.operations = Collection(self)
771 # """Return the WSDL object that contains this binding."""
772 # return self.parent().parent()
774 def getPortType(self):
775 """Return the PortType object associated with this binding."""
776 return self.getWSDL().portTypes[self.type]
778 def findBinding(self, kind):
779 for item in self.extensions:
780 if isinstance(item, kind):
784 def findBindings(self, kind):
785 return [ item for item in self.extensions if isinstance(item, kind) ]
787 def addOperationBinding(self, name, documentation=''):
788 item = OperationBinding(name, documentation)
789 self.operations[name] = item
792 def load(self, elements):
793 for element in elements:
794 name = DOM.getAttr(element, 'name')
795 docs = GetDocumentation(element)
796 opbinding = self.addOperationBinding(name, docs)
797 opbinding.load_ex(GetExtensions(element))
799 item = DOM.getElement(element, 'input', None, None)
801 #TODO: addInputBinding?
802 mbinding = MessageRoleBinding('input')
803 mbinding.documentation = GetDocumentation(item)
804 opbinding.input = mbinding
805 mbinding.load_ex(GetExtensions(item))
806 mbinding.parent = weakref.ref(opbinding)
808 item = DOM.getElement(element, 'output', None, None)
810 mbinding = MessageRoleBinding('output')
811 mbinding.documentation = GetDocumentation(item)
812 opbinding.output = mbinding
813 mbinding.load_ex(GetExtensions(item))
814 mbinding.parent = weakref.ref(opbinding)
816 for item in DOM.getElements(element, 'fault', None):
817 name = DOM.getAttr(item, 'name')
818 mbinding = MessageRoleBinding('fault', name)
819 mbinding.documentation = GetDocumentation(item)
820 opbinding.faults[name] = mbinding
821 mbinding.load_ex(GetExtensions(item))
822 mbinding.parent = weakref.ref(opbinding)
824 def load_ex(self, elements):
826 ns, name = e.namespaceURI, e.localName
827 if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
828 transport = DOM.getAttr(e, 'transport', default=None)
829 style = DOM.getAttr(e, 'style', default='document')
830 ob = SoapBinding(transport, style)
831 self.addExtension(ob)
833 elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
834 verb = DOM.getAttr(e, 'verb')
835 ob = HttpBinding(verb)
836 self.addExtension(ob)
842 wsdl = self.getWSDL()
843 ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
844 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'binding')
845 epc.setAttributeNS(None, 'name', self.name)
848 prefix = epc.getPrefix(ns)
849 epc.setAttributeNS(None, 'type', '%s:%s' %(prefix,name))
851 node = epc._getNode()
852 for ext in self.extensions:
854 for op_binding in self.operations:
855 op_binding.toDom(node)
858 class OperationBinding(Element):
859 def __init__(self, name, documentation=''):
860 Element.__init__(self, name, documentation)
863 self.faults = Collection(self)
866 # """Return the WSDL object that contains this binding."""
867 # return self.parent().parent().parent().parent()
870 def getBinding(self):
871 """Return the parent Binding object of the operation binding."""
872 return self.parent().parent()
874 def getOperation(self):
875 """Return the abstract Operation associated with this binding."""
876 return self.getBinding().getPortType().operations[self.name]
878 def findBinding(self, kind):
879 for item in self.extensions:
880 if isinstance(item, kind):
884 def findBindings(self, kind):
885 return [ item for item in self.extensions if isinstance(item, kind) ]
887 def addInputBinding(self, binding):
888 if self.input is None:
889 self.input = MessageRoleBinding('input')
890 self.input.parent = weakref.ref(self)
891 self.input.addExtension(binding)
894 def addOutputBinding(self, binding):
895 if self.output is None:
896 self.output = MessageRoleBinding('output')
897 self.output.parent = weakref.ref(self)
898 self.output.addExtension(binding)
901 def addFaultBinding(self, name, binding):
902 fault = self.get(name, None)
904 fault = MessageRoleBinding('fault', name)
905 fault.addExtension(binding)
908 def load_ex(self, elements):
910 ns, name = e.namespaceURI, e.localName
911 if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
912 soapaction = DOM.getAttr(e, 'soapAction', default=None)
913 style = DOM.getAttr(e, 'style', default=None)
914 ob = SoapOperationBinding(soapaction, style)
915 self.addExtension(ob)
917 elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
918 location = DOM.getAttr(e, 'location')
919 ob = HttpOperationBinding(location)
920 self.addExtension(ob)
925 def toDom(self, node):
926 wsdl = self.getWSDL()
927 ep = ElementProxy(None, node)
928 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
929 epc.setAttributeNS(None, 'name', self.name)
931 node = epc._getNode()
932 for ext in self.extensions:
935 self.input.toDom(node)
937 self.output.toDom(node)
938 for fault in self.faults:
942 class MessageRoleBinding(Element):
943 def __init__(self, type, name='', documentation=''):
944 Element.__init__(self, name, documentation)
947 def findBinding(self, kind):
948 for item in self.extensions:
949 if isinstance(item, kind):
953 def findBindings(self, kind):
954 return [ item for item in self.extensions if isinstance(item, kind) ]
956 def load_ex(self, elements):
958 ns, name = e.namespaceURI, e.localName
959 if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
960 encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
961 namespace = DOM.getAttr(e, 'namespace', default=None)
962 parts = DOM.getAttr(e, 'parts', default=None)
963 use = DOM.getAttr(e, 'use', default=None)
966 'Invalid soap:body binding element.'
968 ob = SoapBodyBinding(use, namespace, encstyle, parts)
969 self.addExtension(ob)
972 elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
973 encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
974 namespace = DOM.getAttr(e, 'namespace', default=None)
975 name = DOM.getAttr(e, 'name', default=None)
976 use = DOM.getAttr(e, 'use', default=None)
977 if use is None or name is None:
979 'Invalid soap:fault binding element.'
981 ob = SoapFaultBinding(name, use, namespace, encstyle)
982 self.addExtension(ob)
985 elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
986 'header', 'headerfault'
988 encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
989 namespace = DOM.getAttr(e, 'namespace', default=None)
990 message = DOM.getAttr(e, 'message')
991 part = DOM.getAttr(e, 'part')
992 use = DOM.getAttr(e, 'use')
994 _class = SoapHeaderBinding
996 _class = SoapHeaderFaultBinding
997 message = ParseQName(message, e)
998 ob = _class(message, part, use, namespace, encstyle)
999 self.addExtension(ob)
1002 elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
1003 ob = HttpUrlReplacementBinding()
1004 self.addExtension(ob)
1007 elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
1008 ob = HttpUrlEncodedBinding()
1009 self.addExtension(ob)
1012 elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
1013 ob = MimeMultipartRelatedBinding()
1014 self.addExtension(ob)
1015 ob.load_ex(GetExtensions(e))
1018 elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
1019 part = DOM.getAttr(e, 'part', default=None)
1020 type = DOM.getAttr(e, 'type', default=None)
1021 ob = MimeContentBinding(part, type)
1022 self.addExtension(ob)
1025 elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
1026 part = DOM.getAttr(e, 'part', default=None)
1027 ob = MimeXmlBinding(part)
1028 self.addExtension(ob)
1032 self.addExtension(e)
1034 def toDom(self, node):
1035 wsdl = self.getWSDL()
1036 ep = ElementProxy(None, node)
1037 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
1039 node = epc._getNode()
1040 for item in self.extensions:
1041 if item: item.toDom(node)
1044 class Service(Element):
1045 def __init__(self, name, documentation=''):
1046 Element.__init__(self, name, documentation)
1047 self.ports = Collection(self)
1050 return self.parent().parent()
1052 def addPort(self, name, binding, documentation=''):
1053 item = Port(name, binding, documentation)
1054 self.ports[name] = item
1057 def load(self, elements):
1058 for element in elements:
1059 name = DOM.getAttr(element, 'name', default=None)
1060 docs = GetDocumentation(element)
1061 binding = DOM.getAttr(element, 'binding', default=None)
1062 if name is None or binding is None:
1064 'Invalid port element.'
1066 binding = ParseQName(binding, element)
1067 port = self.addPort(name, binding, docs)
1068 port.load_ex(GetExtensions(element))
1070 def load_ex(self, elements):
1072 self.addExtension(e)
1075 wsdl = self.getWSDL()
1076 ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
1077 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "service")
1078 epc.setAttributeNS(None, "name", self.name)
1080 node = epc._getNode()
1081 for port in self.ports:
1085 class Port(Element):
1086 def __init__(self, name, binding, documentation=''):
1087 Element.__init__(self, name, documentation)
1088 self.binding = binding
1090 # def getWSDL(self):
1091 # return self.parent().parent().getWSDL()
1093 def getService(self):
1094 """Return the Service object associated with this port."""
1095 return self.parent().parent()
1097 def getBinding(self):
1098 """Return the Binding object that is referenced by this port."""
1099 wsdl = self.getService().getWSDL()
1100 return wsdl.bindings[self.binding]
1102 def getPortType(self):
1103 """Return the PortType object that is referenced by this port."""
1104 wsdl = self.getService().getWSDL()
1105 binding = wsdl.bindings[self.binding]
1106 return wsdl.portTypes[binding.type]
1108 def getAddressBinding(self):
1109 """A convenience method to obtain the extension element used
1110 as the address binding for the port."""
1111 for item in self.extensions:
1112 if isinstance(item, SoapAddressBinding) or \
1113 isinstance(item, HttpAddressBinding):
1116 'No address binding found in port.'
1119 def load_ex(self, elements):
1121 ns, name = e.namespaceURI, e.localName
1122 if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
1123 location = DOM.getAttr(e, 'location', default=None)
1124 ob = SoapAddressBinding(location)
1125 self.addExtension(ob)
1127 elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
1128 location = DOM.getAttr(e, 'location', default=None)
1129 ob = HttpAddressBinding(location)
1130 self.addExtension(ob)
1133 self.addExtension(e)
1135 def toDom(self, node):
1136 wsdl = self.getWSDL()
1137 ep = ElementProxy(None, node)
1138 epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "port")
1139 epc.setAttributeNS(None, "name", self.name)
1141 ns,name = self.binding
1142 prefix = epc.getPrefix(ns)
1143 epc.setAttributeNS(None, "binding", "%s:%s" %(prefix,name))
1145 node = epc._getNode()
1146 for ext in self.extensions:
1151 def __init__(self, transport, style='rpc'):
1152 self.transport = transport
1156 return self.parent().getWSDL()
1158 def toDom(self, node):
1159 wsdl = self.getWSDL()
1160 ep = ElementProxy(None, node)
1161 epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'binding')
1163 epc.setAttributeNS(None, "transport", self.transport)
1165 epc.setAttributeNS(None, "style", self.style)
1167 class SoapAddressBinding:
1168 def __init__(self, location):
1169 self.location = location
1172 return self.parent().getWSDL()
1174 def toDom(self, node):
1175 wsdl = self.getWSDL()
1176 ep = ElementProxy(None, node)
1177 epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'address')
1178 epc.setAttributeNS(None, "location", self.location)
1181 class SoapOperationBinding:
1182 def __init__(self, soapAction=None, style=None):
1183 self.soapAction = soapAction
1187 return self.parent().getWSDL()
1189 def toDom(self, node):
1190 wsdl = self.getWSDL()
1191 ep = ElementProxy(None, node)
1192 epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'operation')
1194 epc.setAttributeNS(None, 'soapAction', self.soapAction)
1196 epc.setAttributeNS(None, 'style', self.style)
1199 class SoapBodyBinding:
1200 def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
1201 if not use in ('literal', 'encoded'):
1203 'Invalid use attribute value: %s' % use
1205 self.encodingStyle = encodingStyle
1206 self.namespace = namespace
1207 if type(parts) in (type(''), type(u'')):
1208 parts = parts.split()
1213 return self.parent().getWSDL()
1215 def toDom(self, node):
1216 wsdl = self.getWSDL()
1217 ep = ElementProxy(None, node)
1218 epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
1219 epc.setAttributeNS(None, "use", self.use)
1220 epc.setAttributeNS(None, "namespace", self.namespace)
1223 class SoapFaultBinding:
1224 def __init__(self, name, use, namespace=None, encodingStyle=None):
1225 if not use in ('literal', 'encoded'):
1227 'Invalid use attribute value: %s' % use
1229 self.encodingStyle = encodingStyle
1230 self.namespace = namespace
1235 return self.parent().getWSDL()
1237 def toDom(self, node):
1238 wsdl = self.getWSDL()
1239 ep = ElementProxy(None, node)
1240 epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
1241 epc.setAttributeNS(None, "use", self.use)
1242 epc.setAttributeNS(None, "name", self.name)
1243 if self.namespace is not None:
1244 epc.setAttributeNS(None, "namespace", self.namespace)
1245 if self.encodingStyle is not None:
1246 epc.setAttributeNS(None, "encodingStyle", self.encodingStyle)
1249 class SoapHeaderBinding:
1250 def __init__(self, message, part, use, namespace=None, encodingStyle=None):
1251 if not use in ('literal', 'encoded'):
1253 'Invalid use attribute value: %s' % use
1255 self.encodingStyle = encodingStyle
1256 self.namespace = namespace
1257 self.message = message
1263 class SoapHeaderFaultBinding(SoapHeaderBinding):
1264 tagname = 'headerfault'
1268 def __init__(self, verb):
1271 class HttpAddressBinding:
1272 def __init__(self, location):
1273 self.location = location
1276 class HttpOperationBinding:
1277 def __init__(self, location):
1278 self.location = location
1280 class HttpUrlReplacementBinding:
1284 class HttpUrlEncodedBinding:
1288 class MimeContentBinding:
1289 def __init__(self, part=None, type=None):
1294 class MimeXmlBinding:
1295 def __init__(self, part=None):
1299 class MimeMultipartRelatedBinding:
1303 def load_ex(self, elements):
1305 ns, name = e.namespaceURI, e.localName
1306 if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
1307 self.parts.append(MimePartBinding())
1311 class MimePartBinding:
1315 def load_ex(self, elements):
1317 ns, name = e.namespaceURI, e.localName
1318 if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
1319 part = DOM.getAttr(e, 'part', default=None)
1320 type = DOM.getAttr(e, 'type', default=None)
1321 ob = MimeContentBinding(part, type)
1322 self.items.append(ob)
1325 elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
1326 part = DOM.getAttr(e, 'part', default=None)
1327 ob = MimeXmlBinding(part)
1328 self.items.append(ob)
1331 elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
1332 encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
1333 namespace = DOM.getAttr(e, 'namespace', default=None)
1334 parts = DOM.getAttr(e, 'parts', default=None)
1335 use = DOM.getAttr(e, 'use', default=None)
1338 'Invalid soap:body binding element.'
1340 ob = SoapBodyBinding(use, namespace, encstyle, parts)
1341 self.items.append(ob)
1345 class WSDLError(Exception):
1350 def DeclareNSPrefix(writer, prefix, nsuri):
1351 if writer.hasNSPrefix(nsuri):
1353 writer.declareNSPrefix(prefix, nsuri)
1355 def ParseTypeRef(value, element):
1356 parts = value.split(':', 1)
1358 return (DOM.findTargetNS(element), value)
1359 nsuri = DOM.findNamespaceURI(parts[0], element)
1360 return (nsuri, parts[1])
1362 def ParseQName(value, element):
1363 nameref = value.split(':', 1)
1364 if len(nameref) == 2:
1365 nsuri = DOM.findNamespaceURI(nameref[0], element)
1368 nsuri = DOM.findTargetNS(element)
1372 def GetDocumentation(element):
1373 docnode = DOM.getElement(element, 'documentation', None, None)
1374 if docnode is not None:
1375 return DOM.getElementText(docnode)
1378 def GetExtensions(element):
1379 return [ item for item in DOM.getElements(element, None, None)
1380 if item.namespaceURI != DOM.NS_WSDL ]
1382 def GetWSAActionFault(operation, name):
1383 """Find wsa:Action attribute, and return value or WSA.FAULT
1386 attr = operation.faults[name].action
1387 if attr is not None:
1391 def GetWSAActionInput(operation):
1392 """Find wsa:Action attribute, and return value or the default."""
1393 attr = operation.input.action
1394 if attr is not None:
1396 portType = operation.getPortType()
1397 targetNamespace = portType.getTargetNamespace()
1398 ptName = portType.name
1399 msgName = operation.input.name
1401 msgName = operation.name + 'Request'
1402 if targetNamespace.endswith('/'):
1403 return '%s%s/%s' %(targetNamespace, ptName, msgName)
1404 return '%s/%s/%s' %(targetNamespace, ptName, msgName)
1406 def GetWSAActionOutput(operation):
1407 """Find wsa:Action attribute, and return value or the default."""
1408 attr = operation.output.action
1409 if attr is not None:
1411 targetNamespace = operation.getPortType().getTargetNamespace()
1412 ptName = operation.getPortType().name
1413 msgName = operation.output.name
1415 msgName = operation.name + 'Response'
1416 if targetNamespace.endswith('/'):
1417 return '%s%s/%s' %(targetNamespace, ptName, msgName)
1418 return '%s/%s/%s' %(targetNamespace, ptName, msgName)
1420 def FindExtensions(object, kind, t_type=type(())):
1421 if isinstance(kind, t_type):
1423 namespaceURI, name = kind
1424 return [ item for item in object.extensions
1425 if hasattr(item, 'nodeType') \
1426 and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
1427 and item.name == name ]
1428 return [ item for item in object.extensions if isinstance(item, kind) ]
1430 def FindExtension(object, kind, t_type=type(())):
1431 if isinstance(kind, t_type):
1432 namespaceURI, name = kind
1433 for item in object.extensions:
1434 if hasattr(item, 'nodeType') \
1435 and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
1436 and item.name == name:
1439 for item in object.extensions:
1440 if isinstance(item, kind):
1446 """SOAPCallInfo captures the important binding information about a
1447 SOAP operation, in a structure that is easier to work with than
1448 raw WSDL structures."""
1450 def __init__(self, methodName):
1451 self.methodName = methodName
1453 self.outheaders = []
1458 encodingStyle = DOM.NS_SOAP_ENC
1467 def addInParameter(self, name, type, namespace=None, element_type=0):
1468 """Add an input parameter description to the call info."""
1469 parameter = ParameterInfo(name, type, namespace, element_type)
1470 self.inparams.append(parameter)
1473 def addOutParameter(self, name, type, namespace=None, element_type=0):
1474 """Add an output parameter description to the call info."""
1475 parameter = ParameterInfo(name, type, namespace, element_type)
1476 self.outparams.append(parameter)
1479 def setReturnParameter(self, name, type, namespace=None, element_type=0):
1480 """Set the return parameter description for the call info."""
1481 parameter = ParameterInfo(name, type, namespace, element_type)
1482 self.retval = parameter
1485 def addInHeaderInfo(self, name, type, namespace, element_type=0,
1487 """Add an input SOAP header description to the call info."""
1488 headerinfo = HeaderInfo(name, type, namespace, element_type)
1490 headerinfo.mustUnderstand = 1
1491 self.inheaders.append(headerinfo)
1494 def addOutHeaderInfo(self, name, type, namespace, element_type=0,
1496 """Add an output SOAP header description to the call info."""
1497 headerinfo = HeaderInfo(name, type, namespace, element_type)
1499 headerinfo.mustUnderstand = 1
1500 self.outheaders.append(headerinfo)
1503 def getInParameters(self):
1504 """Return a sequence of the in parameters of the method."""
1505 return self.inparams
1507 def getOutParameters(self):
1508 """Return a sequence of the out parameters of the method."""
1509 return self.outparams
1511 def getReturnParameter(self):
1512 """Return param info about the return value of the method."""
1515 def getInHeaders(self):
1516 """Return a sequence of the in headers of the method."""
1517 return self.inheaders
1519 def getOutHeaders(self):
1520 """Return a sequence of the out headers of the method."""
1521 return self.outheaders
1524 class ParameterInfo:
1525 """A ParameterInfo object captures parameter binding information."""
1526 def __init__(self, name, type, namespace=None, element_type=0):
1528 self.element_type = 1
1529 if namespace is not None:
1530 self.namespace = namespace
1539 class HeaderInfo(ParameterInfo):
1540 """A HeaderInfo object captures SOAP header binding information."""
1541 def __init__(self, name, type, namespace, element_type=None):
1542 ParameterInfo.__init__(self, name, type, namespace, element_type)
1548 def callInfoFromWSDL(port, name):
1549 """Return a SOAPCallInfo given a WSDL port and operation name."""
1550 wsdl = port.getService().getWSDL()
1551 binding = port.getBinding()
1552 portType = binding.getPortType()
1553 operation = portType.operations[name]
1554 opbinding = binding.operations[name]
1555 messages = wsdl.messages
1556 callinfo = SOAPCallInfo(name)
1558 addrbinding = port.getAddressBinding()
1559 if not isinstance(addrbinding, SoapAddressBinding):
1560 raise ValueError, 'Unsupported binding type.'
1561 callinfo.location = addrbinding.location
1563 soapbinding = binding.findBinding(SoapBinding)
1564 if soapbinding is None:
1565 raise ValueError, 'Missing soap:binding element.'
1566 callinfo.transport = soapbinding.transport
1567 callinfo.style = soapbinding.style or 'document'
1569 soap_op_binding = opbinding.findBinding(SoapOperationBinding)
1570 if soap_op_binding is not None:
1571 callinfo.soapAction = soap_op_binding.soapAction
1572 callinfo.style = soap_op_binding.style or callinfo.style
1574 parameterOrder = operation.parameterOrder
1576 if operation.input is not None:
1577 message = messages[operation.input.message]
1578 msgrole = opbinding.input
1580 mime = msgrole.findBinding(MimeMultipartRelatedBinding)
1581 if mime is not None:
1582 raise ValueError, 'Mime bindings are not supported.'
1584 for item in msgrole.findBindings(SoapHeaderBinding):
1585 part = messages[item.message].parts[item.part]
1586 header = callinfo.addInHeaderInfo(
1588 part.element or part.type,
1590 element_type = part.element and 1 or 0
1592 header.encodingStyle = item.encodingStyle
1594 body = msgrole.findBinding(SoapBodyBinding)
1596 raise ValueError, 'Missing soap:body binding.'
1597 callinfo.encodingStyle = body.encodingStyle
1598 callinfo.namespace = body.namespace
1599 callinfo.use = body.use
1601 if body.parts is not None:
1603 for name in body.parts:
1604 parts.append(message.parts[name])
1606 parts = message.parts.values()
1609 callinfo.addInParameter(
1611 part.element or part.type,
1612 element_type = part.element and 1 or 0
1615 if operation.output is not None:
1617 message = messages[operation.output.message]
1621 "Recieved message not defined in the WSDL schema: %s" %
1622 operation.output.message)
1624 message = wsdl.addMessage(operation.output.message)
1626 "Recieved message not defined in the WSDL schema.", \
1628 print "Message:", operation.output.message
1630 msgrole = opbinding.output
1632 mime = msgrole.findBinding(MimeMultipartRelatedBinding)
1633 if mime is not None:
1634 raise ValueError, 'Mime bindings are not supported.'
1636 for item in msgrole.findBindings(SoapHeaderBinding):
1637 part = messages[item.message].parts[item.part]
1638 header = callinfo.addOutHeaderInfo(
1640 part.element or part.type,
1642 element_type = part.element and 1 or 0
1644 header.encodingStyle = item.encodingStyle
1646 body = msgrole.findBinding(SoapBodyBinding)
1648 raise ValueError, 'Missing soap:body binding.'
1649 callinfo.encodingStyle = body.encodingStyle
1650 callinfo.namespace = body.namespace
1651 callinfo.use = body.use
1653 if body.parts is not None:
1655 for name in body.parts:
1656 parts.append(message.parts[name])
1658 parts = message.parts.values()
1662 callinfo.addOutParameter(
1664 part.element or part.type,
1665 element_type = part.element and 1 or 0