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