class TwistedSUDSClient: def __init__(self, wsdl, timeout=DEFAULT_TIMEOUT, ctx_factory=None): self.options = Options() self.options.transport = FileTransport() reader = DefinitionsReader(self.options, Definitions) self.wsdl = reader.open(wsdl) self.type_factory = Factory(self.wsdl) self.timeout = timeout self.ctx_factory = ctx_factory def createType(self, type_name): """ @args typename: type to create. QNames are specified with {namespace}element syntax. """ return self.type_factory.create(type_name) def invoke(self, url, method_name, *args): """ Invoke a SOAP/WSDL action. No getattr/getitem magic, sorry. @args url: URL/Endpoint to POST SOAP at. @args method_name: SOAP Method to invoke. @args *args Argument for SOAP method. """ def invokeError(err, url, soap_action): if isinstance(err.value, ConnectionDone): pass # these are pretty common when the remote shuts down else: # response body is in err.value.response action = soap_action[1:-1].split('/')[-1] log.msg('SOAP method invocation failed: %s. Message: %s. URL: %s. Action: %s' % \ (err.getErrorMessage(), err.value.response, url, action), system=LOG_SYSTEM) return err method = self._getMethod(method_name) # build envelope and get action soap_envelope = method.binding.input.get_message(method, args, {}) soap_envelope = soap_envelope.str().encode('utf-8') soap_action = str(method.soap.action) short_action = soap_action[1:-1].split('/')[-1] log.msg('SOAP Dispatch: URL: %s. Action: %s. Length %s' % (url, short_action, len(soap_envelope)), system=LOG_SYSTEM, debug=True) # dispatch d, factory = _httpRequest(url, soap_action, soap_envelope, timeout=self.timeout, ctx_factory=self.ctx_factory) d.addCallbacks(self._parseResponse, invokeError, callbackArgs=(factory, method, short_action), errbackArgs=(url, soap_action)) return d def _getMethod(self, method_name): # one service and port should be enough for everybody assert len(self.wsdl.services) == 1, 'TwistedSUDSClient can only handle one service' service = self.wsdl.services[0] assert len(service.ports) == 1, 'TwistedSUDSClient can only handle port' port = service.ports[0] # print port.methods.keys() method = port.methods[method_name] return method def _parseResponse(self, response, factory, method, short_action): log.msg('Received SOAP response for %s' % short_action, debug=True, system=LOG_SYSTEM) if factory.status == '200': # Note: This can raise suds.WebFault, but it is the responsibility of the caller to handle that _, result = method.binding.input.get_reply(method, response) return result else: raise RequestError('Got a non-200 response from the service. Message:\n----\n' + response + '\n----\n')