1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  """ 
 18  The I{2nd generation} service proxy provides access to web services. 
 19  See I{README.txt} 
 20  """ 
 21   
 22  import suds 
 23  import suds.metrics as metrics 
 24  from cookielib import CookieJar 
 25  from suds import * 
 26  from suds.reader import DefinitionsReader 
 27  from suds.transport import TransportError, Request 
 28  from suds.transport.https import HttpAuthenticated 
 29  from suds.servicedefinition import ServiceDefinition 
 30  from suds import sudsobject 
 31  from sudsobject import Factory as InstFactory 
 32  from sudsobject import Object 
 33  from suds.resolver import PathResolver 
 34  from suds.builder import Builder 
 35  from suds.wsdl import Definitions 
 36  from suds.cache import ObjectCache 
 37  from suds.sax.document import Document 
 38  from suds.sax.parser import Parser 
 39  from suds.options import Options 
 40  from suds.properties import Unskin 
 41  from urlparse import urlparse 
 42  from copy import deepcopy 
 43  from suds.plugin import PluginContainer 
 44  from logging import getLogger 
 45   
 46  log = getLogger(__name__) 
 50      """  
 51      A lightweight web services client. 
 52      I{(2nd generation)} API. 
 53      @ivar wsdl: The WSDL object. 
 54      @type wsdl:L{Definitions} 
 55      @ivar service: The service proxy used to invoke operations. 
 56      @type service: L{Service} 
 57      @ivar factory: The factory used to create objects. 
 58      @type factory: L{Factory} 
 59      @ivar sd: The service definition 
 60      @type sd: L{ServiceDefinition} 
 61      @ivar messages: The last sent/received messages. 
 62      @type messages: str[2] 
 63      """ 
 64      @classmethod 
 66          """ 
 67          Extract the I{items} from a suds object much like the 
 68          items() method works on I{dict}. 
 69          @param sobject: A suds object 
 70          @type sobject: L{Object} 
 71          @return: A list of items contained in I{sobject}. 
 72          @rtype: [(key, value),...] 
 73          """ 
 74          return sudsobject.items(sobject) 
  75       
 76      @classmethod 
 77 -    def dict(cls, sobject): 
  78          """ 
 79          Convert a sudsobject into a dictionary. 
 80          @param sobject: A suds object 
 81          @type sobject: L{Object} 
 82          @return: A python dictionary containing the 
 83              items contained in I{sobject}. 
 84          @rtype: dict 
 85          """ 
 86          return sudsobject.asdict(sobject) 
  87       
 88      @classmethod 
 98   
123           
125          """ 
126          Set options. 
127          @param kwargs: keyword arguments. 
128          @see: L{Options} 
129          """ 
130          p = Unskin(self.options) 
131          p.update(kwargs) 
 132           
134          """ 
135          Add I{static} mapping of an XML namespace prefix to a namespace. 
136          This is useful for cases when a wsdl and referenced schemas make heavy 
137          use of namespaces and those namespaces are subject to changed. 
138          @param prefix: An XML namespace prefix. 
139          @type prefix: str 
140          @param uri: An XML namespace URI. 
141          @type uri: str 
142          @raise Exception: when prefix is already mapped. 
143          """ 
144          root = self.wsdl.root 
145          mapped = root.resolvePrefix(prefix, None) 
146          if mapped is None: 
147              root.addPrefix(prefix, uri) 
148              return 
149          if mapped[1] != uri: 
150              raise Exception('"%s" already mapped as "%s"' % (prefix, mapped)) 
 151           
153          """ 
154          Get last sent I{soap} message. 
155          @return: The last sent I{soap} message. 
156          @rtype: L{Document} 
157          """ 
158          return self.messages.get('tx') 
 159       
161          """ 
162          Get last received I{soap} message. 
163          @return: The last received I{soap} message. 
164          @rtype: L{Document} 
165          """ 
166          return self.messages.get('rx') 
 167       
169          """ 
170          Get a shallow clone of this object. 
171          The clone only shares the WSDL.  All other attributes are 
172          unique to the cloned object including options. 
173          @return: A shallow clone. 
174          @rtype: L{Client} 
175          """ 
176          class Uninitialized(Client): 
177              def __init__(self): 
178                  pass 
  179          clone = Uninitialized() 
180          clone.options = Options() 
181          cp = Unskin(clone.options) 
182          mp = Unskin(self.options) 
183          cp.update(deepcopy(mp)) 
184          clone.wsdl = self.wsdl 
185          clone.factory = self.factory 
186          clone.service = ServiceSelector(clone, self.wsdl.services) 
187          clone.sd = self.sd 
188          clone.messages = dict(tx=None, rx=None) 
189          return clone 
190    
193           
203   
206      """ 
207      A factory for instantiating types defined in the wsdl 
208      @ivar resolver: A schema type resolver. 
209      @type resolver: L{PathResolver} 
210      @ivar builder: A schema object builder. 
211      @type builder: L{Builder} 
212      """ 
213       
222       
249       
251          """ 
252          Set the path separator. 
253          @param ps: The new path separator. 
254          @type ps: char 
255          """ 
256          self.resolver = PathResolver(self.wsdl, ps) 
  257   
260      """ 
261      The B{service} selector is used to select a web service. 
262      In most cases, the wsdl only defines (1) service in which access 
263      by subscript is passed through to a L{PortSelector}.  This is also the 
264      behavior when a I{default} service has been specified.  In cases 
265      where multiple services have been defined and no default has been 
266      specified, the service is found by name (or index) and a L{PortSelector} 
267      for the service is returned.  In all cases, attribute access is 
268      forwarded to the L{PortSelector} for either the I{first} service or the 
269      I{default} service (when specified). 
270      @ivar __client: A suds client. 
271      @type __client: L{Client} 
272      @ivar __services: A list of I{wsdl} services. 
273      @type __services: list 
274      """ 
276          """ 
277          @param client: A suds client. 
278          @type client: L{Client} 
279          @param services: A list of I{wsdl} services. 
280          @type services: list 
281          """ 
282          self.__client = client 
283          self.__services = services 
 284       
286          """ 
287          Request to access an attribute is forwarded to the 
288          L{PortSelector} for either the I{first} service or the 
289          I{default} service (when specified). 
290          @param name: The name of a method. 
291          @type name: str 
292          @return: A L{PortSelector}. 
293          @rtype: L{PortSelector}.  
294          """ 
295          default = self.__ds() 
296          if default is None: 
297              port = self.__find(0) 
298          else: 
299              port = default 
300          return getattr(port, name) 
 301       
303          """ 
304          Provides selection of the I{service} by name (string) or  
305          index (integer).  In cases where only (1) service is defined 
306          or a I{default} has been specified, the request is forwarded 
307          to the L{PortSelector}. 
308          @param name: The name (or index) of a service. 
309          @type name: (int|str) 
310          @return: A L{PortSelector} for the specified service. 
311          @rtype: L{PortSelector}.  
312          """ 
313          if len(self.__services) == 1: 
314              port = self.__find(0) 
315              return port[name] 
316          default = self.__ds() 
317          if default is not None: 
318              port = default 
319              return port[name] 
320          return self.__find(name) 
 321       
323          """ 
324          Find a I{service} by name (string) or index (integer). 
325          @param name: The name (or index) of a service. 
326          @type name: (int|str) 
327          @return: A L{PortSelector} for the found service. 
328          @rtype: L{PortSelector}.  
329          """ 
330          service = None 
331          if not len(self.__services): 
332              raise Exception, 'No services defined' 
333          if isinstance(name, int): 
334              try: 
335                  service = self.__services[name] 
336                  name = service.name 
337              except IndexError: 
338                  raise ServiceNotFound, 'at [%d]' % name 
339          else: 
340              for s in self.__services: 
341                  if name == s.name: 
342                      service = s 
343                      break 
344          if service is None: 
345              raise ServiceNotFound, name 
346          return PortSelector(self.__client, service.ports, name) 
 347       
349          """ 
350          Get the I{default} service if defined in the I{options}. 
351          @return: A L{PortSelector} for the I{default} service. 
352          @rtype: L{PortSelector}.  
353          """ 
354          ds = self.__client.options.service 
355          if ds is None: 
356              return None 
357          else: 
358              return self.__find(ds) 
  359   
362      """ 
363      The B{port} selector is used to select a I{web service} B{port}. 
364      In cases where multiple ports have been defined and no default has been 
365      specified, the port is found by name (or index) and a L{MethodSelector} 
366      for the port is returned.  In all cases, attribute access is 
367      forwarded to the L{MethodSelector} for either the I{first} port or the 
368      I{default} port (when specified). 
369      @ivar __client: A suds client. 
370      @type __client: L{Client} 
371      @ivar __ports: A list of I{service} ports. 
372      @type __ports: list 
373      @ivar __qn: The I{qualified} name of the port (used for logging). 
374      @type __qn: str 
375      """ 
377          """ 
378          @param client: A suds client. 
379          @type client: L{Client} 
380          @param ports: A list of I{service} ports. 
381          @type ports: list 
382          @param qn: The name of the service. 
383          @type qn: str 
384          """ 
385          self.__client = client 
386          self.__ports = ports 
387          self.__qn = qn 
 388       
390          """ 
391          Request to access an attribute is forwarded to the 
392          L{MethodSelector} for either the I{first} port or the 
393          I{default} port (when specified). 
394          @param name: The name of a method. 
395          @type name: str 
396          @return: A L{MethodSelector}. 
397          @rtype: L{MethodSelector}.  
398          """ 
399          default = self.__dp() 
400          if default is None: 
401              m = self.__find(0) 
402          else: 
403              m = default 
404          return getattr(m, name) 
 405       
407          """ 
408          Provides selection of the I{port} by name (string) or  
409          index (integer).  In cases where only (1) port is defined 
410          or a I{default} has been specified, the request is forwarded 
411          to the L{MethodSelector}. 
412          @param name: The name (or index) of a port. 
413          @type name: (int|str) 
414          @return: A L{MethodSelector} for the specified port. 
415          @rtype: L{MethodSelector}.  
416          """ 
417          default = self.__dp() 
418          if default is None: 
419              return self.__find(name) 
420          else: 
421              return default 
 422       
424          """ 
425          Find a I{port} by name (string) or index (integer). 
426          @param name: The name (or index) of a port. 
427          @type name: (int|str) 
428          @return: A L{MethodSelector} for the found port. 
429          @rtype: L{MethodSelector}.  
430          """ 
431          port = None 
432          if not len(self.__ports): 
433              raise Exception, 'No ports defined: %s' % self.__qn 
434          if isinstance(name, int): 
435              qn = '%s[%d]' % (self.__qn, name) 
436              try: 
437                  port = self.__ports[name] 
438              except IndexError: 
439                  raise PortNotFound, qn 
440          else: 
441              qn = '.'.join((self.__qn, name)) 
442              for p in self.__ports: 
443                  if name == p.name: 
444                      port = p 
445                      break 
446          if port is None: 
447              raise PortNotFound, qn 
448          qn = '.'.join((self.__qn, port.name)) 
449          return MethodSelector(self.__client, port.methods, qn) 
 450       
452          """ 
453          Get the I{default} port if defined in the I{options}. 
454          @return: A L{MethodSelector} for the I{default} port. 
455          @rtype: L{MethodSelector}.  
456          """ 
457          dp = self.__client.options.port 
458          if dp is None: 
459              return None 
460          else: 
461              return self.__find(dp) 
  462       
465      """ 
466      The B{method} selector is used to select a B{method} by name. 
467      @ivar __client: A suds client. 
468      @type __client: L{Client} 
469      @ivar __methods: A dictionary of methods. 
470      @type __methods: dict 
471      @ivar __qn: The I{qualified} name of the method (used for logging). 
472      @type __qn: str 
473      """ 
474 -    def __init__(self, client, methods, qn): 
 475          """ 
476          @param client: A suds client. 
477          @type client: L{Client} 
478          @param methods: A dictionary of methods. 
479          @type methods: dict 
480          @param qn: The I{qualified} name of the port. 
481          @type qn: str 
482          """ 
483          self.__client = client 
484          self.__methods = methods 
485          self.__qn = qn 
 486       
488          """ 
489          Get a method by name and return it in an I{execution wrapper}. 
490          @param name: The name of a method. 
491          @type name: str 
492          @return: An I{execution wrapper} for the specified method name. 
493          @rtype: L{Method} 
494          """ 
495          return self[name] 
 496       
498          """ 
499          Get a method by name and return it in an I{execution wrapper}. 
500          @param name: The name of a method. 
501          @type name: str 
502          @return: An I{execution wrapper} for the specified method name. 
503          @rtype: L{Method} 
504          """ 
505          m = self.__methods.get(name) 
506          if m is None: 
507              qn = '.'.join((self.__qn, name)) 
508              raise MethodNotFound, qn 
509          return Method(self.__client, m) 
  510   
513      """ 
514      The I{method} (namespace) object. 
515      @ivar client: A client object. 
516      @type client: L{Client} 
517      @ivar method: A I{wsdl} method. 
518      @type I{wsdl} Method. 
519      """ 
520   
522          """ 
523          @param client: A client object. 
524          @type client: L{Client} 
525          @param method: A I{raw} method. 
526          @type I{raw} Method. 
527          """ 
528          self.client = client 
529          self.method = method 
 530       
544           
548           
 555   
558      """ 
559      A lightweight soap based web client B{**not intended for external use} 
560      @ivar service: The target method. 
561      @type service: L{Service} 
562      @ivar method: A target method. 
563      @type method: L{Method} 
564      @ivar options: A dictonary of options. 
565      @type options: dict 
566      @ivar cookiejar: A cookie jar. 
567      @type cookiejar: libcookie.CookieJar 
568      """ 
569   
571          """ 
572          @param client: A suds client. 
573          @type client: L{Client} 
574          @param method: A target method. 
575          @type method: L{Method} 
576          """ 
577          self.client = client 
578          self.method = method 
579          self.options = client.options 
580          self.cookiejar = CookieJar() 
 581           
582 -    def invoke(self, args, kwargs): 
 610       
611 -    def send(self, soapenv): 
 652       
654          """ 
655          Get http headers or the http/https request. 
656          @return: A dictionary of header/values. 
657          @rtype: dict 
658          """ 
659          action = self.method.soap.action 
660          stock = { 'Content-Type' : 'text/xml; charset=utf-8', 'SOAPAction': action } 
661          result = dict(stock, **self.options.headers) 
662          log.debug('headers = %s', result) 
663          return result 
 664       
689           
690 -    def failed(self, binding, error): 
 691          """ 
692          Request failed, process reply based on reason 
693          @param binding: The binding to be used to process the reply. 
694          @type binding: L{suds.bindings.binding.Binding} 
695          @param error: The http error message 
696          @type error: L{transport.TransportError} 
697          """ 
698          status, reason = (error.httpcode, tostr(error)) 
699          reply = error.fp.read() 
700          log.debug('http failed:\n%s', reply) 
701          if status == 500: 
702              if len(reply) > 0: 
703                  r, p = binding.get_fault(reply) 
704                  self.last_received(r) 
705                  return (status, p) 
706              else: 
707                  return (status, None) 
708          if self.options.faults: 
709              raise Exception((status, reason)) 
710          else: 
711              return (status, None) 
 712   
716       
718          key = 'tx' 
719          messages = self.client.messages 
720          if d is None: 
721              return messages.get(key) 
722          else: 
723              messages[key] = d 
 724           
726          key = 'rx' 
727          messages = self.client.messages 
728          if d is None: 
729              return messages.get(key) 
730          else: 
731              messages[key] = d 
  732   
735      """ 
736      Loopback client used for message/reply simulation. 
737      """ 
738       
739      injkey = '__inject' 
740       
741      @classmethod 
743          """ get whether loopback has been specified in the I{kwargs}. """ 
744          return kwargs.has_key(SimClient.injkey) 
 745           
746 -    def invoke(self, args, kwargs): 
 747          """ 
748          Send the required soap message to invoke the specified method 
749          @param args: A list of args for the method invoked. 
750          @type args: list 
751          @param kwargs: Named (keyword) args for the method invoked. 
752          @type kwargs: dict 
753          @return: The result of the method invocation. 
754          @rtype: I{builtin} or I{subclass of} L{Object} 
755          """ 
756          simulation = kwargs[self.injkey] 
757          msg = simulation.get('msg') 
758          reply = simulation.get('reply') 
759          fault = simulation.get('fault') 
760          if msg is None: 
761              if reply is not None: 
762                  return self.__reply(reply, args, kwargs) 
763              if fault is not None: 
764                  return self.__fault(fault) 
765              raise Exception('(reply|fault) expected when msg=None') 
766          sax = Parser() 
767          msg = sax.parse(string=msg) 
768          return self.send(msg) 
 769       
770 -    def __reply(self, reply, args, kwargs): 
 777       
 787