def envelope(self, headers, body, bodytag=None): ''' Build a SOAP envelope given headers and body. ''' env = ET.Element(ns.expand('soapenv:Envelope', self.nsmap), nsmap=self.nsmap) envheader = ET.Element(ns.expand('soapenv:Header', self.nsmap)) envbody = ET.Element(ns.expand('soapenv:Body', self.nsmap)) env.append(envheader) env.append(envbody) for header in headers: if not ET.iselement(header): if hasattr(header, '__xml__'): header = header.__xml__() else: raise Exception('Cannott create SOAP:header') envheader.append(header) if body is not None: if not ET.iselement(body): if hasattr(body, '__xml__'): body = body.__xml__(tag=bodytag) else: raise Exception('Cannot create SOAP:body') envbody.append(body) return env
def __init__(self, client, btype, op, tns=None): self.name = op.get('name') portop = client.wsdl.find('wsdl:portType[@name="%s"]/wsdl:operation[@name="%s"]' % (btype, self.name)) self.imsg = None self.ihdr = None self.omsg = None self.client = client self.faults = [] self.action = '""' soapop = op.find(ns.expand('soap:operation', client.nsmap)) if soapop is not None: self.action = '"%s"' % soapop.get('soapAction', '') hdr = op.find(ns.expand('soap:header', client.nsmap)) if hdr is not None: msg = client.wsdl.messages[ns.expand(hdr.get('message'), el.nsmap)] self.ihdr = msg for el in portop: (namespace, tag) = ns.split(el.tag) if tag not in ('input', 'output', 'fault'): continue msg = client.wsdl.messages[ns.expand(el.get('message'), el.nsmap)] if tag == 'input': self.imsg = msg if tag == 'output': self.omsg = msg if tag == 'fault': self.faults.append(msg)
def _messages(self): messages = {} for doc in self.documents: r = doc.getroot() tns = r.get('targetNamespace') for m in doc.findall('wsdl:message', namespaces=self.nsmap): messages[ns.expand(m.get('name'), r.nsmap, targetNamespace=tns)] = ns.expand(m[0].get('element'), r.nsmap, targetNamespace=tns) return messages
def _factory(self, typename, **kwargs): if 'targetNamespace' not in kwargs and self.namespace: kwargs['targetNamespace'] = self.namespace typename = ns.expand(typename, self.loader.allns, **kwargs) cls = None try: cls = self.cache[typename] except KeyError: oldroot = self.root self.root = self.loader.schema(typename) node = self.loader.type(typename) if node is None: node = self.loader.element(typename) if node is not None and 'type' in node.attrib: tns, _ = self.nssplit(typename) cls = self._factory(node.get('type'), targetNamespace=tns) if node is not None and cls is None: self.process(node) cls = self.cache.get(typename) self.root = oldroot if cls is None: raise TypeError("Unknown Type", typename) return cls
def _make_type(self, value, type): ''' Parse value and convert it to type ''' iselem = False if ET.iselement(value): iselem = True xtype = value.get(xsi_type) if xtype: xtype = ns.expand(xtype, value.nsmap) (prefix, t) = ns.split(xtype) if prefix == ns.XS: type = 'xs:'+t else: type = xtype if value.get(xsi_nil) == 'true': return None if type.startswith('xs:'): if iselem: value = value.text c = converter(type) if value is not None and not c.check(value): value = c.fromstr(value) else: if value == "": value = None value = self.__builder__.factory(type)(value, __relax__=self.__relax__) return value
def _make_type(self, value, type): ''' Parse value and convert it to type ''' iselem = False if ET.iselement(value): iselem = True xtype = value.get(xsi_type) if xtype: xtype = ns.expand(xtype, value.nsmap) (prefix, t) = ns.split(xtype) if prefix == ns.XS: type = 'xs:' + t else: type = xtype if value.get(xsi_nil) == 'true': return None if type.startswith('xs:'): if iselem: value = value.text c = converter(type) if value is not None and not c.check(value): value = c.fromstr(value) else: if value == "": value = None value = self.__builder__.factory(type)(value, __relax__=self.__relax__) return value
def _mk_service(self): ''' Build a Service object for each wsdl:service ''' for s in self.wsdl.findall('wsdl:service'): name = s.get('name') service = Service(name) setattr(self, name, service) port = s.find(ns.expand('wsdl:port', self.nsmap)) (_, binding) = ns.split(port.get('binding'), port.nsmap) self._mk_binding(binding, service)
def _mk_binding(self, bname, service): ''' Build operation objects bound to the service ''' #bname = ns.expand(bname, self.nsmap) #print "making binding for",bname binding = self.wsdl.find('wsdl:binding[@name="%s"]' % bname) (_, btype) = ns.split(binding.get('type'), binding.nsmap) for op in binding.findall(ns.expand('wsdl:operation', self.nsmap)): operation = Operation(self, btype, op) setattr(service, op.get('name'), operation)
def _load(self, url): doc = ET.parse(urllib2.urlopen(url)) extrans = doc.getroot().nsmap targetns = doc.getroot().get('targetNamespace') schemas = doc.findall(ns.expand('*/xs:schema')) for s in schemas: for k,v in extrans.items(): if k not in s.nsmap: s.nsmap[k] = v self.schemaloader.load(s, pathinfo=self.url) self.documents.append(doc) wsdls = doc.findall(ns.expand('/wsdl:import')) for w in wsdls: location = w.get('location') location = urljoin(url, location) tns = self._load(location) if tns: targetns = tns return targetns
def load(self, schema, force=False, fragment=False, pathinfo='', basecls=None): ''' Load and pre-process an XML schema. @type schema: L{ElementTree.Element} or URL @param schema: A schema to load. @type force: bool @param force: Optional. Reload an already loaded schema. @type fragment: bool @param fragment: Optional. The schema is actually a fragment of a an already-loaded schema and should be integrated with it. @type pathinfo: str @param pathinfo: Optional. A URL to help with loading the schema. Usually used by SchemaLoader to process xs:import directives. @rtype: str @return: The targetNamespace of the loaded schema ''' # Is schema already parsed if ET.iselement(schema): root = schema else: schema = urljoin(pathinfo, schema) root = ET.parse(u2.urlopen(schema)).getroot() # Get the target namespace. Exit early if we already know this schema. targetNamespace = root.get('targetNamespace') if targetNamespace in self.schemas and not (force or fragment): return targetNamespace # Add a new entry to our dictionary if not fragment: self.schemas[targetNamespace] = { 'root': root, 'types': {}, 'elements': {}, 'groups': {}, 'validator': None, 'basecls': basecls } # Update our "all namespaces" dictionary and get references to the # various subdictionaries we'll need self.allns.update(root.nsmap) self.revns.update((v,k) for k,v in root.nsmap.items() if k not in (None, 'tns')) types = self.schemas[targetNamespace]['types'] elements = self.schemas[targetNamespace]['elements'] groups = self.schemas[targetNamespace]['groups'] # Process includes includes = [] while True: # Get the list of includes inclist = root.findall(ns.expand('xs:include')) if not inclist: break for el in inclist: # remove it from the document root.remove(el) # Get the schemaLocation and compute the URL location = el.get('schemaLocation') if location in includes: # skip if we've processed this schema continue includes.append(location) url = urljoin(pathinfo, location) # Parse the XML and append it to the root document # We probably *should* include it into the place where the # xs:include node was, but for now, punt and append it # to the end of the document inc = ET.parse(u2.urlopen(url)).getroot() root.extend(inc) # Process imports for el in root.findall(ns.expand('xs:import')): location = el.get('schemaLocation') if location: self.load(location, pathinfo=pathinfo) # Find all first-level tags we care about and reference them # in the types/elements/groups dictionaries for el in root.findall(ns.expand('xs:complexType')): types[el.get('name')] = el for el in root.findall(ns.expand('xs:simpleType')): types[el.get('name')] = el for el in root.findall(ns.expand('xs:element')): elements[el.get('name')] = el for el in root.findall(ns.expand('xs:group')): groups[el.get('name')] = el # If this is a schema fragment, integrate it into the # original schema element tree in memory if fragment: realroot = self.schemas[targetNamespace]['root'] nsmap = dict(realroot.nsmap) nsmap.update(root.nsmap) attrib = dict(realroot.attrib) attrib.update(root.attrib) newroot = ET.Element(realroot.tag, attrib=attrib, nsmap=nsmap) newroot.text = realroot.text newroot.extend(realroot.getchildren()) newroot.extend(root.getchildren()) self.schemas[targetNamespace]['root'] = newroot return targetNamespace
def nsexpand(self, s, target=True): tns = {} if target: tns['targetNamespace'] = self.root.get('targetNamespace') return ns.expand(s, self.root.nsmap, **tns)
def invoke(self, operation, *args, **kwargs): ''' Invoke a SOAP operation. ''' self._reqno += 1 retxml = kwargs.pop('__retxml__', self.retxml) timeout = kwargs.pop('__timeout__', self.timeout) transport_options = kwargs.pop('__transport__', {}) # Create an instance of the request message and initialize # the object from the arguments param = self.factory(operation.imsg) tmpl = param.__template__ for k,v in zip((t[0] for t in tmpl), args): param[k] = v for k,v in kwargs.items(): param[k] = v # Build the soap envelope and set the http headers payload = self.envelope(self.headers, param, operation.imsg) httphdr = { 'Content-Type': 'text/xml', 'SOAPAction': operation.action } httphdr.update(self.httphdr) # Construct and issue the request, read the response payload = ET.tostring(payload, pretty_print=True) log.debug('=== SOAP REQUEST ===\n%s', re.sub(r'password>.*?<', r'password>*****<', payload )) req = urllib2.Request(self.url, payload, httphdr) try: if self._inject: xml = ET.fromstring(self._inject.next()) else: if hasattr(self.transport, 'open'): rsp = self.transport.open(req, timeout=timeout, **transport_options) else: rsp = self.transport.urlopen(req, timeout=timeout, **transport_options) xml = ET.parse(rsp) except urllib2.HTTPError as ex: xml = ET.parse(ex) log.debug('=== SOAP RESPONSE ===\n%s', xmlstr(xml)) # Get the soap body retval = xml.find(ns.expand('soapenv:Body', self.nsmap)) if not retxml: # Does the body contain any nodes? if len(retval): # Get the first child and examine it retval = retval[0] namespace, tag = ns.split(retval.tag) # If it's a fault, convert it to an exception if tag == 'Fault': raise SoapFault(retval, self) # Otherwise, deserialize obj = self.factory(operation.omsg, retval) # If the deserialized # object has only one item, return that item, otherwise the # whole object # # This is so if the return value is a single primitive type # (like a string), you don't have to dig into an object just # to get at the single primitive return value if len(obj) == 1: obj = obj[0] retval = obj else: retval = None return retval
def invoke(self, operation, *args, **kwargs): ''' Invoke a SOAP operation. ''' self._reqno += 1 retxml = kwargs.pop('__retxml__', self.retxml) timeout = kwargs.pop('__timeout__', self.timeout) transport_options = kwargs.pop('__transport__', {}) # Create an instance of the request message and initialize # the object from the arguments param = self.factory(operation.imsg) tmpl = param.__template__ for k,v in zip((t[0] for t in tmpl), args): param[k] = v for k,v in kwargs.items(): param[k] = v # Build the soap envelope and set the http headers payload = self.envelope(self.headers, param, operation.imsg) httphdr = { 'Content-Type': 'text/xml', 'SOAPAction': operation.action } httphdr.update(self.httphdr) # Construct and issue the request, read the response payload = ET.tostring(payload, pretty_print=True) log.debug('=== SOAP REQUEST ===\n%s', payload) req = urllib2.Request(self.url, payload, httphdr) try: if self._inject: xml = ET.fromstring(self._inject.next()) else: if hasattr(self.transport, 'open'): rsp = self.transport.open(req, timeout=timeout, **transport_options) else: rsp = self.transport.urlopen(req, timeout=timeout, **transport_options) xml = ET.parse(rsp) except urllib2.HTTPError as ex: xml = ET.parse(ex) log.debug('=== SOAP RESPONSE ===\n%s', xmlstr(xml)) # Get the soap body retval = xml.find(ns.expand('soapenv:Body', self.nsmap)) if not retxml: # Does the body contain any nodes? if len(retval): # Get the first child and examine it retval = retval[0] namespace, tag = ns.split(retval.tag) # If it's a fault, convert it to an exception if tag == 'Fault': raise SoapFault(retval, self) # Otherwise, deserialize obj = self.factory(operation.omsg, retval) # If the deserialized # object has only one item, return that item, otherwise the # whole object # # This is so if the return value is a single primitive type # (like a string), you don't have to dig into an object just # to get at the single primitive return value if len(obj) == 1: obj = obj[0] retval = obj else: retval = None return retval
def load(self, schema, force=False, fragment=False, pathinfo='', basecls=None): ''' Load and pre-process an XML schema. @type schema: L{ElementTree.Element} or URL @param schema: A schema to load. @type force: bool @param force: Optional. Reload an already loaded schema. @type fragment: bool @param fragment: Optional. The schema is actually a fragment of a an already-loaded schema and should be integrated with it. @type pathinfo: str @param pathinfo: Optional. A URL to help with loading the schema. Usually used by SchemaLoader to process xs:import directives. @rtype: str @return: The targetNamespace of the loaded schema ''' # Is schema already parsed if ET.iselement(schema): root = schema else: schema = urljoin(pathinfo, schema) root = ET.parse(u2.urlopen(schema)).getroot() # Get the target namespace. Exit early if we already know this schema. targetNamespace = root.get('targetNamespace') if targetNamespace in self.schemas and not (force or fragment): return targetNamespace # Add a new entry to our dictionary if not fragment: self.schemas[targetNamespace] = { 'root': root, 'types': {}, 'elements': {}, 'groups': {}, 'validator': None, 'basecls': basecls } # Update our "all namespaces" dictionary and get references to the # various subdictionaries we'll need self.allns.update(root.nsmap) self.revns.update( (v, k) for k, v in root.nsmap.items() if k not in (None, 'tns')) types = self.schemas[targetNamespace]['types'] elements = self.schemas[targetNamespace]['elements'] groups = self.schemas[targetNamespace]['groups'] # Process includes includes = [] while True: # Get the list of includes inclist = root.findall(ns.expand('xs:include')) if not inclist: break for el in inclist: # remove it from the document root.remove(el) # Get the schemaLocation and compute the URL location = el.get('schemaLocation') if location in includes: # skip if we've processed this schema continue includes.append(location) url = urljoin(pathinfo, location) # Parse the XML and append it to the root document # We probably *should* include it into the place where the # xs:include node was, but for now, punt and append it # to the end of the document inc = ET.parse(u2.urlopen(url)).getroot() root.extend(inc) # Process imports for el in root.findall(ns.expand('xs:import')): location = el.get('schemaLocation') if location: self.load(location, pathinfo=pathinfo) # Find all first-level tags we care about and reference them # in the types/elements/groups dictionaries for el in root.findall(ns.expand('xs:complexType')): types[el.get('name')] = el for el in root.findall(ns.expand('xs:simpleType')): types[el.get('name')] = el for el in root.findall(ns.expand('xs:element')): elements[el.get('name')] = el for el in root.findall(ns.expand('xs:group')): groups[el.get('name')] = el # If this is a schema fragment, integrate it into the # original schema element tree in memory if fragment: realroot = self.schemas[targetNamespace]['root'] nsmap = dict(realroot.nsmap) nsmap.update(root.nsmap) attrib = dict(realroot.attrib) attrib.update(root.attrib) newroot = ET.Element(realroot.tag, attrib=attrib, nsmap=nsmap) newroot.text = realroot.text newroot.extend(realroot.getchildren()) newroot.extend(root.getchildren()) self.schemas[targetNamespace]['root'] = newroot return targetNamespace