1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 The I{wsdl} module provides an objectification of the WSDL.
19 The primary class is I{Definitions} as it represends the root element
20 found in the document.
21 """
22
23 from logging import getLogger
24 from suds import *
25 from suds.sax import splitPrefix
26 from suds.sax.element import Element
27 from suds.bindings.document import Document
28 from suds.bindings.rpc import RPC, Encoded
29 from suds.xsd import qualify, Namespace
30 from suds.xsd.schema import Schema, SchemaCollection
31 from suds.xsd.query import ElementQuery
32 from suds.sudsobject import Object, Facade, Metadata
33 from suds.reader import DocumentReader, DefinitionsReader
34 from urlparse import urljoin
35 import re, soaparray
36
37 log = getLogger(__name__)
38
39 wsdlns = (None, "http://schemas.xmlsoap.org/wsdl/")
40 soapns = (None, 'http://schemas.xmlsoap.org/wsdl/soap/')
41 soap12ns = (None, 'http://schemas.xmlsoap.org/wsdl/soap12/')
45 """
46 Base object for wsdl types.
47 @ivar root: The XML I{root} element.
48 @type root: L{Element}
49 """
50
51 - def __init__(self, root, definitions=None):
52 """
53 @param root: An XML root element.
54 @type root: L{Element}
55 @param definitions: A definitions object.
56 @type definitions: L{Definitions}
57 """
58 Object.__init__(self)
59 self.root = root
60 pmd = Metadata()
61 pmd.excludes = ['root']
62 pmd.wrappers = dict(qname=repr)
63 self.__metadata__.__print__ = pmd
64
66 """
67 Resolve named references to other WSDL objects.
68 @param definitions: A definitions object.
69 @type definitions: L{Definitions}
70 """
71 pass
72
75 """
76 A B{named} WSDL object.
77 @ivar name: The name of the object.
78 @type name: str
79 @ivar qname: The I{qualified} name of the object.
80 @type qname: (name, I{namespace-uri}).
81 """
82
84 """
85 @param root: An XML root element.
86 @type root: L{Element}
87 @param definitions: A definitions object.
88 @type definitions: L{Definitions}
89 """
90 WObject.__init__(self, root, definitions)
91 self.name = root.get('name')
92 self.qname = (self.name, definitions.tns[1])
93 pmd = self.__metadata__.__print__
94 pmd.wrappers['qname'] = repr
95
98 """
99 Represents the I{root} container of the WSDL objects as defined
100 by <wsdl:definitions/>
101 @ivar id: The object id.
102 @type id: str
103 @ivar options: An options dictionary.
104 @type options: L{options.Options}
105 @ivar url: The URL used to load the object.
106 @type url: str
107 @ivar tns: The target namespace for the WSDL.
108 @type tns: str
109 @ivar schema: The collective WSDL schema object.
110 @type schema: L{SchemaCollection}
111 @ivar children: The raw list of child objects.
112 @type children: [L{WObject},...]
113 @ivar imports: The list of L{Import} children.
114 @type imports: [L{Import},...]
115 @ivar messages: The dictionary of L{Message} children key'd by I{qname}
116 @type messages: [L{Message},...]
117 @ivar port_types: The dictionary of L{PortType} children key'd by I{qname}
118 @type port_types: [L{PortType},...]
119 @ivar bindings: The dictionary of L{Binding} children key'd by I{qname}
120 @type bindings: [L{Binding},...]
121 @ivar service: The service object.
122 @type service: L{Service}
123 """
124
125 Tag = 'definitions'
126
164
166 """ Get/create the target namespace """
167 tns = root.get('targetNamespace')
168 prefix = root.findPrefix(tns)
169 if prefix is None:
170 log.debug('warning: tns (%s), not mapped to prefix', tns)
171 prefix = 'tns'
172 return (prefix, tns)
173
175 """ Add child objects using the factory """
176 for c in root.getChildren(ns=wsdlns):
177 child = Factory.create(c, self)
178 if child is None: continue
179 self.children.append(child)
180 if isinstance(child, Import):
181 self.imports.append(child)
182 continue
183 if isinstance(child, Types):
184 self.types.append(child)
185 continue
186 if isinstance(child, Message):
187 self.messages[child.qname] = child
188 continue
189 if isinstance(child, PortType):
190 self.port_types[child.qname] = child
191 continue
192 if isinstance(child, Binding):
193 self.bindings[child.qname] = child
194 continue
195 if isinstance(child, Service):
196 self.services.append(child)
197 continue
198
200 """ Import the I{imported} WSDLs. """
201 for imp in self.imports:
202 imp.load(self)
203
205 """ Tell all children to resolve themselves """
206 for c in self.children:
207 c.resolve(self)
208
224
249
269
271 nopickle = ('options',)
272 state = self.__dict__.copy()
273 for k in nopickle:
274 if k in state:
275 del state[k]
276 return state
277
279 return 'Definitions (id=%s)' % self.id
280
283 """
284 Represents the <wsdl:import/>.
285 @ivar location: The value of the I{location} attribute.
286 @type location: str
287 @ivar ns: The value of the I{namespace} attribute.
288 @type ns: str
289 @ivar imported: The imported object.
290 @type imported: L{Definitions}
291 """
292
294 """
295 @param root: An XML root element.
296 @type root: L{Element}
297 @param definitions: A definitions object.
298 @type definitions: L{Definitions}
299 """
300 WObject.__init__(self, root, definitions)
301 self.location = root.get('location')
302 self.ns = root.get('namespace')
303 self.imported = None
304 pmd = self.__metadata__.__print__
305 pmd.wrappers['imported'] = repr
306
307 - def load(self, definitions):
322
331
341
344
345
346 -class Types(WObject):
347 """
348 Represents <types><schema/></types>.
349 """
350
351 @classmethod
352 - def create(cls, definitions):
356
358 """
359 @param root: An XML root element.
360 @type root: L{Element}
361 @param definitions: A definitions object.
362 @type definitions: L{Definitions}
363 """
364 WObject.__init__(self, root, definitions)
365 self.definitions = definitions
366
367 - def contents(self):
368 return self.root.getChildren('schema', Namespace.xsdns)
369
371 return self.definitions.schema
372
374 return ( self.definitions.schema is None )
375
377 return ( not self.local() )
378
380 return isinstance(other, Import)
381
382
383 -class Part(NamedObject):
384 """
385 Represents <message><part/></message>.
386 @ivar element: The value of the {element} attribute.
387 Stored as a I{qref} as converted by L{suds.xsd.qualify}.
388 @type element: str
389 @ivar type: The value of the {type} attribute.
390 Stored as a I{qref} as converted by L{suds.xsd.qualify}.
391 @type type: str
392 """
393
395 """
396 @param root: An XML root element.
397 @type root: L{Element}
398 @param definitions: A definitions object.
399 @type definitions: L{Definitions}
400 """
401 NamedObject.__init__(self, root, definitions)
402 pmd = Metadata()
403 pmd.wrappers = dict(element=repr, type=repr)
404 self.__metadata__.__print__ = pmd
405 tns = definitions.tns
406 self.element = self.__getref('element', tns)
407 self.type = self.__getref('type', tns)
408
410 """ Get the qualified value of attribute named 'a'."""
411 s = self.root.get(a)
412 if s is None:
413 return s
414 else:
415 return qualify(s, self.root, tns)
416
419 """
420 Represents <message/>.
421 @ivar parts: A list of message parts.
422 @type parts: [I{Part},...]
423 """
424
426 """
427 @param root: An XML root element.
428 @type root: L{Element}
429 @param definitions: A definitions object.
430 @type definitions: L{Definitions}
431 """
432 NamedObject.__init__(self, root, definitions)
433 self.parts = []
434 for p in root.getChildren('part'):
435 part = Part(p, definitions)
436 self.parts.append(part)
437
440
443 """
444 Represents <portType/>.
445 @ivar operations: A list of contained operations.
446 @type operations: list
447 """
448
450 """
451 @param root: An XML root element.
452 @type root: L{Element}
453 @param definitions: A definitions object.
454 @type definitions: L{Definitions}
455 """
456 NamedObject.__init__(self, root, definitions)
457 self.operations = {}
458 for c in root.getChildren('operation'):
459 op = Facade('Operation')
460 op.name = c.get('name')
461 op.tns = definitions.tns
462 input = c.getChild('input')
463 if input is None:
464 op.input = None
465 else:
466 op.input = input.get('message')
467 output = c.getChild('output')
468 if output is None:
469 op.output = None
470 else:
471 op.output = output.get('message')
472 faults = []
473 for fault in c.getChildren('fault'):
474 f = Facade('Fault')
475 f.name = fault.get('name')
476 f.message = fault.get('message')
477 faults.append(f)
478 op.faults = faults
479 self.operations[op.name] = op
480
482 """
483 Resolve named references to other WSDL objects.
484 @param definitions: A definitions object.
485 @type definitions: L{Definitions}
486 """
487 for op in self.operations.values():
488 if op.input is None:
489 op.input = Message(Element('no-input'), definitions)
490 else:
491 qref = qualify(op.input, self.root, definitions.tns)
492 msg = definitions.messages.get(qref)
493 if msg is None:
494 raise Exception("msg '%s', not-found" % op.input)
495 else:
496 op.input = msg
497 if op.output is None:
498 op.output = Message(Element('no-output'), definitions)
499 else:
500 qref = qualify(op.output, self.root, definitions.tns)
501 msg = definitions.messages.get(qref)
502 if msg is None:
503 raise Exception("msg '%s', not-found" % op.output)
504 else:
505 op.output = msg
506 for f in op.faults:
507 qref = qualify(f.message, self.root, definitions.tns)
508 msg = definitions.messages.get(qref)
509 if msg is None:
510 raise Exception, "msg '%s', not-found" % f.message
511 f.message = msg
512
514 """
515 Shortcut used to get a contained operation by name.
516 @param name: An operation name.
517 @type name: str
518 @return: The named operation.
519 @rtype: Operation
520 @raise L{MethodNotFound}: When not found.
521 """
522 try:
523 return self.operations[name]
524 except Exception, e:
525 raise MethodNotFound(name)
526
529
532 """
533 Represents <binding/>
534 @ivar operations: A list of contained operations.
535 @type operations: list
536 """
537
539 """
540 @param root: An XML root element.
541 @type root: L{Element}
542 @param definitions: A definitions object.
543 @type definitions: L{Definitions}
544 """
545 NamedObject.__init__(self, root, definitions)
546 self.operations = {}
547 self.type = root.get('type')
548 sr = self.soaproot()
549 if sr is None:
550 self.soap = None
551 log.debug('binding: "%s" not a soap binding', self.name)
552 return
553 soap = Facade('soap')
554 self.soap = soap
555 self.soap.style = sr.get('style', default='document')
556 self.add_operations(self.root, definitions)
557
559 """ get the soap:binding """
560 for ns in (soapns, soap12ns):
561 sr = self.root.getChild('binding', ns=ns)
562 if sr is not None:
563 return sr
564 return None
565
567 """ Add <operation/> children """
568 dsop = Element('operation', ns=soapns)
569 for c in root.getChildren('operation'):
570 op = Facade('Operation')
571 op.name = c.get('name')
572 sop = c.getChild('operation', default=dsop)
573 soap = Facade('soap')
574 soap.action = '"%s"' % sop.get('soapAction', default='')
575 soap.style = sop.get('style', default=self.soap.style)
576 soap.input = Facade('Input')
577 soap.input.body = Facade('Body')
578 soap.input.headers = []
579 soap.output = Facade('Output')
580 soap.output.body = Facade('Body')
581 soap.output.headers = []
582 op.soap = soap
583 input = c.getChild('input')
584 if input is None:
585 input = Element('input', ns=wsdlns)
586 body = input.getChild('body')
587 self.body(definitions, soap.input.body, body)
588 for header in input.getChildren('header'):
589 self.header(definitions, soap.input, header)
590 output = c.getChild('output')
591 if output is None:
592 output = Element('output', ns=wsdlns)
593 body = output.getChild('body')
594 self.body(definitions, soap.output.body, body)
595 for header in output.getChildren('header'):
596 self.header(definitions, soap.output, header)
597 faults = []
598 for fault in c.getChildren('fault'):
599 sf = fault.getChild('fault')
600 if sf is None:
601 continue
602 fn = fault.get('name')
603 f = Facade('Fault')
604 f.name = sf.get('name', default=fn)
605 f.use = sf.get('use', default='literal')
606 faults.append(f)
607 soap.faults = faults
608 self.operations[op.name] = op
609
610 - def body(self, definitions, body, root):
611 """ add the input/output body properties """
612 if root is None:
613 body.use = 'literal'
614 body.namespace = definitions.tns
615 body.parts = ()
616 return
617 parts = root.get('parts')
618 if parts is None:
619 body.parts = ()
620 else:
621 body.parts = re.split('[\s,]', parts)
622 body.use = root.get('use', default='literal')
623 ns = root.get('namespace')
624 if ns is None:
625 body.namespace = definitions.tns
626 else:
627 prefix = root.findPrefix(ns, 'b0')
628 body.namespace = (prefix, ns)
629
649
651 """
652 Resolve named references to other WSDL objects. This includes
653 cross-linking information (from) the portType (to) the I{soap}
654 protocol information on the binding for each operation.
655 @param definitions: A definitions object.
656 @type definitions: L{Definitions}
657 """
658 self.resolveport(definitions)
659 for op in self.operations.values():
660 self.resolvesoapbody(definitions, op)
661 self.resolveheaders(definitions, op)
662 self.resolvefaults(definitions, op)
663
665 """
666 Resolve port_type reference.
667 @param definitions: A definitions object.
668 @type definitions: L{Definitions}
669 """
670 ref = qualify(self.type, self.root, definitions.tns)
671 port_type = definitions.port_types.get(ref)
672 if port_type is None:
673 raise Exception("portType '%s', not-found" % self.type)
674 else:
675 self.type = port_type
676
677 - def resolvesoapbody(self, definitions, op):
678 """
679 Resolve soap body I{message} parts by
680 cross-referencing with operation defined in port type.
681 @param definitions: A definitions object.
682 @type definitions: L{Definitions}
683 @param op: An I{operation} object.
684 @type op: I{operation}
685 """
686 ptop = self.type.operation(op.name)
687 if ptop is None:
688 raise Exception, \
689 "operation '%s' not defined in portType" % op.name
690 soap = op.soap
691 parts = soap.input.body.parts
692 if len(parts):
693 pts = []
694 for p in ptop.input.parts:
695 if p.name in parts:
696 pts.append(p)
697 soap.input.body.parts = pts
698 else:
699 soap.input.body.parts = ptop.input.parts
700 parts = soap.output.body.parts
701 if len(parts):
702 pts = []
703 for p in ptop.output.parts:
704 if p.name in parts:
705 pts.append(p)
706 soap.output.body.parts = pts
707 else:
708 soap.output.body.parts = ptop.output.parts
709
711 """
712 Resolve soap header I{message} references.
713 @param definitions: A definitions object.
714 @type definitions: L{Definitions}
715 @param op: An I{operation} object.
716 @type op: I{operation}
717 """
718 soap = op.soap
719 headers = soap.input.headers + soap.output.headers
720 for header in headers:
721 mn = header.message
722 ref = qualify(mn, self.root, definitions.tns)
723 message = definitions.messages.get(ref)
724 if message is None:
725 raise Exception, "message'%s', not-found" % mn
726 pn = header.part
727 for p in message.parts:
728 if p.name == pn:
729 header.part = p
730 break
731 if pn == header.part:
732 raise Exception, \
733 "message '%s' has not part named '%s'" % (ref, pn)
734
736 """
737 Resolve soap fault I{message} references by
738 cross-referencing with operation defined in port type.
739 @param definitions: A definitions object.
740 @type definitions: L{Definitions}
741 @param op: An I{operation} object.
742 @type op: I{operation}
743 """
744 ptop = self.type.operation(op.name)
745 if ptop is None:
746 raise Exception, \
747 "operation '%s' not defined in portType" % op.name
748 soap = op.soap
749 for fault in soap.faults:
750 for f in ptop.faults:
751 if f.name == fault.name:
752 fault.parts = f.message.parts
753 continue
754 if hasattr(fault, 'parts'):
755 continue
756 raise Exception, \
757 "fault '%s' not defined in portType '%s'" % (fault.name, self.type.name)
758
760 """
761 Shortcut used to get a contained operation by name.
762 @param name: An operation name.
763 @type name: str
764 @return: The named operation.
765 @rtype: Operation
766 @raise L{MethodNotFound}: When not found.
767 """
768 try:
769 return self.operations[name]
770 except:
771 raise MethodNotFound(name)
772
774 return ( not isinstance(other, Service) )
775
776
777 -class Port(NamedObject):
778 """
779 Represents a service port.
780 @ivar service: A service.
781 @type service: L{Service}
782 @ivar binding: A binding name.
783 @type binding: str
784 @ivar location: The service location (url).
785 @type location: str
786 """
787
788 - def __init__(self, root, definitions, service):
789 """
790 @param root: An XML root element.
791 @type root: L{Element}
792 @param definitions: A definitions object.
793 @type definitions: L{Definitions}
794 @param service: A service object.
795 @type service: L{Service}
796 """
797 NamedObject.__init__(self, root, definitions)
798 self.__service = service
799 self.binding = root.get('binding')
800 address = root.getChild('address')
801 if address is None:
802 self.location = None
803 else:
804 self.location = address.get('location').encode('utf-8')
805 self.methods = {}
806
808 """
809 Get a method defined in this portType by name.
810 @param name: A method name.
811 @type name: str
812 @return: The requested method object.
813 @rtype: I{Method}
814 """
815 return self.methods.get(name)
816
819 """
820 Represents <service/>.
821 @ivar port: The contained ports.
822 @type port: [Port,..]
823 @ivar methods: The contained methods for all ports.
824 @type methods: [Method,..]
825 """
826
828 """
829 @param root: An XML root element.
830 @type root: L{Element}
831 @param definitions: A definitions object.
832 @type definitions: L{Definitions}
833 """
834 NamedObject.__init__(self, root, definitions)
835 self.ports = []
836 for p in root.getChildren('port'):
837 port = Port(p, definitions, self)
838 self.ports.append(port)
839
840 - def port(self, name):
841 """
842 Locate a port by name.
843 @param name: A port name.
844 @type name: str
845 @return: The port object.
846 @rtype: L{Port}
847 """
848 for p in self.ports:
849 if p.name == name:
850 return p
851 return None
852
854 """
855 Override the invocation location (url) for service method.
856 @param url: A url location.
857 @type url: A url.
858 @param names: A list of method names. None=ALL
859 @type names: [str,..]
860 """
861 for p in self.ports:
862 for m in p.methods.values():
863 if names is None or m.name in names:
864 m.location = url
865
867 """
868 Resolve named references to other WSDL objects.
869 Ports without soap bindings are discarded.
870 @param definitions: A definitions object.
871 @type definitions: L{Definitions}
872 """
873 filtered = []
874 for p in self.ports:
875 ref = qualify(p.binding, self.root, definitions.tns)
876 binding = definitions.bindings.get(ref)
877 if binding is None:
878 raise Exception("binding '%s', not-found" % p.binding)
879 if binding.soap is None:
880 log.debug('binding "%s" - not a soap, discarded', binding.name)
881 continue
882 p.binding = binding
883 filtered.append(p)
884 self.ports = filtered
885
888
891 """
892 Simple WSDL object factory.
893 @cvar tags: Dictionary of tag->constructor mappings.
894 @type tags: dict
895 """
896
897 tags =\
898 {
899 'import' : Import,
900 'types' : Types,
901 'message' : Message,
902 'portType' : PortType,
903 'binding' : Binding,
904 'service' : Service,
905 }
906
907 @classmethod
908 - def create(cls, root, definitions):
909 """
910 Create an object based on the root tag name.
911 @param root: An XML root element.
912 @type root: L{Element}
913 @param definitions: A definitions object.
914 @type definitions: L{Definitions}
915 @return: The created object.
916 @rtype: L{WObject}
917 """
918 fn = cls.tags.get(root.name)
919 if fn is not None:
920 return fn(root, definitions)
921 else:
922 return None
923