@@ -26,11 +26,11 @@ class Http(): # wrapper to use when httplib2 not available def request(self, url, method, body, headers): f = urllib2.urlopen(urllib2.Request(url, body, headers)) return f.info(), f.read() - + from simplexml import SimpleXMLElement, TYPE_MAP, OrderedDict class SoapFault(RuntimeError): def __init__(self,faultcode,faultstring): self.faultcode = faultcode @@ -45,29 +45,29 @@ ) class SoapClient(object): "Simple SOAP Client (s�mil PHP)" def __init__(self, location = None, action = None, namespace = None, - cert = None, trace = False, exceptions = True, proxy = None, ns=False, + cert = None, trace = False, exceptions = True, proxy = None, ns=False, soap_ns=None, wsdl = None, cache = False): - self.certssl = cert - self.keyssl = None + self.certssl = cert + self.keyssl = None self.location = location # server location (url) self.action = action # SOAP base action - self.namespace = namespace # message + self.namespace = namespace # message self.trace = trace # show debug messages self.exceptions = exceptions # lanzar execpiones? (Soap Faults) self.xml_request = self.xml_response = '' if not soap_ns and not ns: self.__soap_ns = 'soap' # 1.1 elif not soap_ns and ns: self.__soap_ns = 'soapenv' # 1.2 else: self.__soap_ns = soap_ns - + # parse wsdl url - self.services = wsdl and self.wsdl(wsdl, debug=trace, cache=cache) + self.services = wsdl and self.wsdl(wsdl, debug=trace, cache=cache) self.service_port = None # service port for late binding if not proxy: self.http = Http() else: @@ -77,13 +77,13 @@ proxy_type=socks.PROXY_TYPE_HTTP, **proxy)) #if self.certssl: # esto funciona para validar al server? # self.http.add_certificate(self.keyssl, self.keyssl, self.certssl) self.__ns = ns # namespace prefix or False to not use it if not ns: - self.__xml = """ -<%(soap_ns)s:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" + self.__xml = """ +<%(soap_ns)s:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:%(soap_ns)s="%(soap_uri)s"> <%(soap_ns)s:Body> <%(method)s xmlns="%(namespace)s"> @@ -102,13 +102,13 @@ "Return a pseudo-method that can be called" if not self.services: # not using WSDL? return lambda self=self, *args, **kwargs: self.call(attr,*args,**kwargs) else: # using WSDL: return lambda self=self, *args, **kwargs: self.wsdl_call(attr,*args,**kwargs) - + def call(self, method, *args, **kwargs): - "Prepare xml request and make SOAP call, returning a SimpleXMLElement" + "Prepare xml request and make SOAP call, returning a SimpleXMLElement" #TODO: method != input_message # Basic SOAP request: xml = self.__xml % dict(method=method, namespace=self.namespace, ns=self.__ns, soap_ns=self.__soap_ns, soap_uri=soap_namespaces[self.__soap_ns]) request = SimpleXMLElement(xml,namespace=self.__ns and self.namespace, prefix=self.__ns) @@ -129,17 +129,17 @@ self.xml_response = self.send(method, self.xml_request) response = SimpleXMLElement(self.xml_response, namespace=self.namespace) if self.exceptions and response("Fault", ns=soap_namespaces.values(), error=False): raise SoapFault(unicode(response.faultcode), unicode(response.faultstring)) return response - + def send(self, method, xml): "Send SOAP request using HTTP" if self.location == 'test': return location = "%s" % self.location #?op=%s" % (self.location, method) if self.services: - soap_action = self.action + soap_action = self.action else: soap_action = self.action+method headers={ 'Content-type': 'text/xml; charset="UTF-8"', 'Content-length': str(len(xml)), @@ -152,12 +152,12 @@ print u"\n%s" % xml.decode("utf8","ignore") response, content = self.http.request( location,"POST", body=xml, headers=headers ) self.response = response self.content = content - if self.trace: - print + if self.trace: + print print '\n'.join(["%s: %s" % (k,v) for k,v in response.items()]) print content#.decode("utf8","ignore") print "="*80 return content @@ -180,11 +180,11 @@ if not operation: raise RuntimeError("Operation %s not found in WSDL: " "Service/Port Type: %s" % (method, self.service_port)) return operation - + def wsdl_call(self, method, *args, **kwargs): "Pre and post process SOAP call, input and output parameters using WSDL" soap_uri = soap_namespaces[self.__soap_ns] operation = self.get_operation(method) # get i/o type declarations: @@ -200,21 +200,21 @@ v = d.get(k) if v: if isinstance(v, dict): v = sort_dict(od[k], v) elif isinstance(v, list): - v = [sort_dict(od[k][0], v1) + v = [sort_dict(od[k][0], v1) for v1 in v] - ret[str(k)] = v + ret[str(k)] = v return ret else: return d if input and kwargs: params = sort_dict(input.values()[0], kwargs).items() method = input.keys()[0] #elif not input: - #TODO: no message! (see wsmtxca.dummy) + #TODO: no message! (see wsmtxca.dummy) else: params = kwargs and kwargs.items() # call remote procedure response = self.call(method, *params) # parse results: @@ -224,14 +224,14 @@ def help(self, method): "Return operation documentation and invocation/returned value example" operation = self.get_operation(method) input = operation['input'].values() input = input and input[0] - output = operation['output'].values()[0] + output = operation['output'].values()[0] return u"%s(%s)\n -> %s:\n\n%s" % ( - method, - input and ", ".join("%s=%s" % (k,repr(v)) for k,v + method, + input and ", ".join("%s=%s" % (k,repr(v)) for k,v in input.items()) or "", output and output or "", operation.get("documentation",""), ) @@ -242,22 +242,22 @@ "http://schemas.xmlsoap.org/wsdl/soap12/": 'soap12', } wsdl_uri="http://schemas.xmlsoap.org/wsdl/" xsd_uri="http://www.w3.org/2001/XMLSchema" xsi_uri="http://www.w3.org/2001/XMLSchema-instance" - + get_local_name = lambda s: str((':' in s) and s.split(':')[1] or s) - + REVERSE_TYPE_MAP = dict([(v,k) for k,v in TYPE_MAP.items()]) def fetch(url): "Fetch a document from a URL, save it locally if cache enabled" import os, hashlib - # make md5 hash of the url for caching... + # make md5 hash of the url for caching... filename = "%s.xml" % hashlib.md5(url).hexdigest() if isinstance(cache, basestring): - filename = os.path.join(cache, filename) + filename = os.path.join(cache, filename) if cache and os.path.exists(filename): if debug: print "Reading file %s" % (filename, ) f = open(filename, "r") xml = f.read() f.close() @@ -269,11 +269,11 @@ if debug: print "Writing file %s" % (filename, ) f = open(filename, "w") f.write(xml) f.close() return xml - + # Open uri and read xml: xml = fetch(url) # Parse WSDL XML: wsdl = SimpleXMLElement(xml, namespace=wsdl_uri) @@ -287,18 +287,18 @@ xsd_ns = get_local_name(k) # Extract useful data: self.namespace = wsdl['targetNamespace'] self.documentation = unicode(wsdl('documentation', error=False) or '') - + services = {} bindings = {} # binding_name: binding operations = {} # operation_name: operation port_type_bindings = {} # port_type_name: binding messages = {} # message: element elements = {} # element: type def - + for service in wsdl.service: service_name=service['name'] if not service_name: continue # empty service? if debug: print "Processing service", service_name @@ -313,11 +313,11 @@ bindings[binding_name] = {'service_name': service_name, 'location': location, 'soap_uri': soap_uri, 'soap_ver': soap_ver, } serv['ports'][port['name']] = bindings[binding_name] - + for binding in wsdl.binding: binding_name = binding['name'] if debug: print "Processing binding", service_name soap_binding = binding('binding', ns=soap_uris.values(), error=False) transport = soap_binding and soap_binding['transport'] or None @@ -335,11 +335,11 @@ bindings[binding_name]['operations'][op_name] = d d.update({'name': op_name}) #if action: #TODO: separe operation_binding from operation if action: d["action"] = action - + #TODO: cleanup element/schema/types parsing: def process_element(element_name, node): "Parse and define simple element types" if debug: print "Processing element", element_name for tag in node: @@ -353,11 +353,11 @@ children = tag.children() alias = False else: if debug: print element_name,"has not children!",tag continue #TODO: abstract? - d = OrderedDict() + d = OrderedDict() for e in children: t = e['type'] if not t: t = e['base'] # complexContent (extension)! if not t: @@ -389,11 +389,11 @@ # extend base element: process_element(element_name, e.children()) elements.setdefault(element_name, OrderedDict()).update(d) # check axis2 namespace at schema types attributes - self.namespace = dict(wsdl.types("schema", ns=xsd_uri)[:]).get('targetNamespace', self.namespace) + self.namespace = dict(wsdl.types("schema", ns=xsd_uri)[:]).get('targetNamespace', self.namespace) imported_schemas = {} def preprocess_schema(schema): "Find schema elements and complex types" @@ -453,14 +453,14 @@ #break if isinstance(v, list): for n in v: # recurse list postprocess_element(n) - + # process current wsdl schema: - for schema in wsdl.types("schema", ns=xsd_uri): - preprocess_schema(schema) + for schema in wsdl.types("schema", ns=xsd_uri): + preprocess_schema(schema) postprocess_element(elements) for message in wsdl.message: if debug: print "Processing message", message['name'] @@ -471,38 +471,38 @@ if not element_name: element_name = part['type'] # some uses type instead element_name = get_local_name(element_name) element = {element_name: elements.get(element_name)} messages[message['name']] = element - + for port_type in wsdl.portType: port_type_name = port_type['name'] if debug: print "Processing port type", port_type_name binding = port_type_bindings[port_type_name] for operation in port_type.operation: op_name = operation['name'] - op = operations[op_name] + op = operations[op_name] op['documentation'] = unicode(operation('documentation', error=False) or '') - if binding['soap_ver']: + if binding['soap_ver']: #TODO: separe operation_binding from operation (non SOAP?) input = get_local_name(operation.input['message']) output = get_local_name(operation.output['message']) op['input'] = messages[input] op['output'] = messages[output] if debug: import pprint pprint.pprint(services) - + return services def parse_proxy(proxy_str): "Parses proxy address user:pass@host:port into a dict suitable for httplib2" proxy_dict = {} if proxy_str is None: - return + return if "@" in proxy_str: user_pass, host_port = proxy_str.split("@") else: user_pass, host_port = "", proxy_str if ":" in host_port: @@ -509,23 +509,23 @@ host, port = host_port.split(":") proxy_dict['proxy_host'], proxy_dict['proxy_port'] = host, int(port) if ":" in user_pass: proxy_dict['proxy_user'], proxy_dict['proxy_pass'] = user_pass.split(":") return proxy_dict - - + + if __name__=="__main__": import sys - + if '--web2py' in sys.argv: # test local sample webservice exposed by web2py from client import SoapClient if not '--wsdl' in sys.argv: client = SoapClient( location = "http://127.0.0.1:8000/webservices/sample/call/soap", action = 'http://127.0.0.1:8000/webservices/sample/call/soap', # SOAPAction - namespace = "http://127.0.0.1:8000/webservices/sample/call/soap", + namespace = "http://127.0.0.1:8000/webservices/sample/call/soap", soap_ns='soap', trace = True, ns = False, exceptions=True) else: client = SoapClient(wsdl="http://127.0.0.1:8000/webservices/sample/call/soap?WSDL",trace=True) response = client.Dummy() print 'dummy', response @@ -543,17 +543,17 @@ # raw (unmarshalled parameter) local sample webservice exposed by web2py from client import SoapClient client = SoapClient( location = "http://127.0.0.1:8000/webservices/sample/call/soap", action = 'http://127.0.0.1:8000/webservices/sample/call/soap', # SOAPAction - namespace = "http://127.0.0.1:8000/webservices/sample/call/soap", + namespace = "http://127.0.0.1:8000/webservices/sample/call/soap", soap_ns='soap', trace = True, ns = False) params = SimpleXMLElement("""32""") # manully convert returned type response = client.call('AddIntegers',params) - result = response.AddResult + result = response.AddResult print int(result) # manully convert returned type - + if '--ctg' in sys.argv: # test AFIP Agriculture webservice client = SoapClient( location = "https://fwshomo.afip.gov.ar/wsctg/services/CTGService", action = 'http://impl.service.wsctg.afip.gov.ar/CTGService/', # SOAPAction @@ -563,11 +563,11 @@ response = client.dummy() result = response.dummyResponse print str(result.appserver) print str(result.dbserver) print str(result.authserver) - + if '--wsfe' in sys.argv: # Demo & Test (AFIP Electronic Invoice): ta_file = open("TA.xml") try: ta_string = ta_file.read() # read access ticket (wsaa.py) @@ -590,11 +590,11 @@ if int(results.FERecuperaQTYRequestResult.RError.percode) != 0: print "Percode: %s" % results.FERecuperaQTYRequestResult.RError.percode print "MSGerror: %s" % results.FERecuperaQTYRequestResult.RError.perrmsg else: print int(results.FERecuperaQTYRequestResult.qty.value) - + if '--feriados' in sys.argv: # Demo & Test: Argentina Holidays (Ministerio del Interior): # this webservice seems disabled from datetime import datetime, timedelta client = SoapClient( @@ -635,15 +635,15 @@ ta = SimpleXMLElement(ta_string) token = str(ta.credentials.token) sign = str(ta.credentials.sign) response = client.FEXGetCMP( Auth={"Token": token, "Sign": sign, "Cuit": 20267565393}, - Cmp={"Tipo_cbte": 19, "Punto_vta": 1, "Cbte_nro": 1}) + Cmp={"Tipo_cbte": 19, "Punto_vta": 1, "Cbte_nro": 1}) result = response['FEXGetCMPResult'] if False: print result if 'FEXErr' in result: - print "FEXError:", result['FEXErr']['ErrCode'], result['FEXErr']['ErrCode'] + print "FEXError:", result['FEXErr']['ErrCode'], result['FEXErr']['ErrCode'] cbt = result['FEXResultGet'] print cbt['Cae'] FEX_event = result['FEXEvents'] print FEX_event['EventCode'], FEX_event['EventMsg'] @@ -667,22 +667,23 @@ response = client.obtenerProvincias(auth={"token":token, "sign":sign, "cuitRepresentado":20267565393}) print "response=",response for ret in response: print ret['return']['codigoProvincia'], ret['return']['descripcionProvincia'].encode("latin1") prueba = dict(numeroCartaDePorte=512345678, codigoEspecie=23, - cuitRemitenteComercial=20267565393, cuitDestino=20267565393, cuitDestinatario=20267565393, - codigoLocalidadOrigen=3058, codigoLocalidadDestino=3059, - codigoCosecha='0910', pesoNetoCarga=1000, cantHoras=1, + cuitRemitenteComercial=20267565393, cuitDestino=20267565393, cuitDestinatario=20267565393, + codigoLocalidadOrigen=3058, codigoLocalidadDestino=3059, + codigoCosecha='0910', pesoNetoCarga=1000, cantHoras=1, patenteVehiculo='CZO985', cuitTransportista=20267565393, numeroCTG="43816783", transaccion='10000001681', observaciones='', ) - response = client.solicitarCTG( + response = client.solicitarCTG( auth={"token": token, "sign": sign, "cuitRepresentado": 20267565393}, solicitarCTGRequest= prueba) print response['return']['numeroCTG'] ##print parse_proxy(None) ##print parse_proxy("host:1234") ##print parse_proxy("user:pass@host:1234") - ##sys.exit(0) + ##sys.exit(0) +