X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=SOAPpy%2FSOAPBuilder.py;fp=SOAPpy%2FSOAPBuilder.py;h=f2eaee2d6b7f9ea6319500b6b349ff6bdaafe6e6;hb=978eaafd657554e3922ac9d95b76174b58a22e3d;hp=0000000000000000000000000000000000000000;hpb=06d38d07d01523410dbeda7a4f226a3c4e57c3fe;p=p2pool.git diff --git a/SOAPpy/SOAPBuilder.py b/SOAPpy/SOAPBuilder.py new file mode 100755 index 0000000..f2eaee2 --- /dev/null +++ b/SOAPpy/SOAPBuilder.py @@ -0,0 +1,648 @@ +""" +################################################################################ +# Copyright (c) 2003, Pfizer +# Copyright (c) 2001, Cayce Ullman. +# Copyright (c) 2001, Brian Matthews. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither the name of actzero, inc. nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################ +""" + +ident = '$Id: SOAPBuilder.py 1498 2010-03-12 02:13:19Z pooryorick $' +from version import __version__ + +import cgi +from wstools.XMLname import toXMLname, fromXMLname +import fpconst + +# SOAPpy modules +from Config import Config +from NS import NS +from Types import * + +# Test whether this Python version has Types.BooleanType +# If it doesn't have it, then False and True are serialized as integers +try: + BooleanType + pythonHasBooleanType = 1 +except NameError: + pythonHasBooleanType = 0 + +################################################################################ +# SOAP Builder +################################################################################ +class SOAPBuilder: + _xml_top = '\n' + _xml_enc_top = '\n' + _env_top = ( '%(ENV_T)s:Envelope\n' + \ + ' %(ENV_T)s:encodingStyle="%(ENC)s"\n' ) % \ + NS.__dict__ + _env_bot = '\n' % NS.__dict__ + + # Namespaces potentially defined in the Envelope tag. + + _env_ns = {NS.ENC: NS.ENC_T, NS.ENV: NS.ENV_T, + NS.XSD: NS.XSD_T, NS.XSD2: NS.XSD2_T, NS.XSD3: NS.XSD3_T, + NS.XSI: NS.XSI_T, NS.XSI2: NS.XSI2_T, NS.XSI3: NS.XSI3_T} + + def __init__(self, args = (), kw = {}, method = None, namespace = None, + header = None, methodattrs = None, envelope = 1, encoding = 'UTF-8', + use_refs = 0, config = Config, noroot = 0): + + # Test the encoding, raising an exception if it's not known + if encoding != None: + ''.encode(encoding) + + self.args = args + self.kw = kw + self.envelope = envelope + self.encoding = encoding + self.method = method + self.namespace = namespace + self.header = header + self.methodattrs= methodattrs + self.use_refs = use_refs + self.config = config + self.out = [] + self.tcounter = 0 + self.ncounter = 1 + self.icounter = 1 + self.envns = {} + self.ids = {} + self.depth = 0 + self.multirefs = [] + self.multis = 0 + self.body = not isinstance(args, bodyType) + self.noroot = noroot + + def build(self): + if Config.debug: print "In build." + ns_map = {} + + # Cache whether typing is on or not + typed = self.config.typed + + if self.header: + # Create a header. + self.dump(self.header, "Header", typed = typed) + #self.header = None # Wipe it out so no one is using it. + + if self.body: + # Call genns to record that we've used SOAP-ENV. + self.depth += 1 + body_ns = self.genns(ns_map, NS.ENV)[0] + self.out.append("<%sBody>\n" % body_ns) + + if self.method: + # Save the NS map so that it can be restored when we + # fall out of the scope of the method definition + save_ns_map = ns_map.copy() + self.depth += 1 + a = '' + if self.methodattrs: + for (k, v) in self.methodattrs.items(): + a += ' %s="%s"' % (k, v) + + if self.namespace: # Use the namespace info handed to us + methodns, n = self.genns(ns_map, self.namespace) + else: + methodns, n = '', '' + + self.out.append('<%s%s%s%s%s>\n' % ( + methodns, self.method, n, a, self.genroot(ns_map))) + + try: + if type(self.args) != TupleType: + args = (self.args,) + else: + args = self.args + + for i in args: + self.dump(i, typed = typed, ns_map = ns_map) + + if hasattr(self.config, "argsOrdering") and self.config.argsOrdering.has_key(self.method): + for k in self.config.argsOrdering.get(self.method): + self.dump(self.kw.get(k), k, typed = typed, ns_map = ns_map) + else: + for (k, v) in self.kw.items(): + self.dump(v, k, typed = typed, ns_map = ns_map) + + except RecursionError: + if self.use_refs == 0: + # restart + b = SOAPBuilder(args = self.args, kw = self.kw, + method = self.method, namespace = self.namespace, + header = self.header, methodattrs = self.methodattrs, + envelope = self.envelope, encoding = self.encoding, + use_refs = 1, config = self.config) + return b.build() + raise + + if self.method: + self.out.append("\n" % (methodns, self.method)) + # End of the method definition; drop any local namespaces + ns_map = save_ns_map + self.depth -= 1 + + if self.body: + # dump may add to self.multirefs, but the for loop will keep + # going until it has used all of self.multirefs, even those + # entries added while in the loop. + + self.multis = 1 + + for obj, tag in self.multirefs: + self.dump(obj, tag, typed = typed, ns_map = ns_map) + + self.out.append("\n" % body_ns) + self.depth -= 1 + + if self.envelope: + e = map (lambda ns: ' xmlns:%s="%s"\n' % (ns[1], ns[0]), + self.envns.items()) + + self.out = ['<', self._env_top] + e + ['>\n'] + \ + self.out + \ + [self._env_bot] + + if self.encoding != None: + self.out.insert(0, self._xml_enc_top % self.encoding) + return ''.join(self.out).encode(self.encoding) + + self.out.insert(0, self._xml_top) + return ''.join(self.out) + + def gentag(self): + if Config.debug: print "In gentag." + self.tcounter += 1 + return "v%d" % self.tcounter + + def genns(self, ns_map, nsURI): + if nsURI == None: + return ('', '') + + if type(nsURI) == TupleType: # already a tuple + if len(nsURI) == 2: + ns, nsURI = nsURI + else: + ns, nsURI = None, nsURI[0] + else: + ns = None + + if ns_map.has_key(nsURI): + return (ns_map[nsURI] + ':', '') + + if self._env_ns.has_key(nsURI): + ns = self.envns[nsURI] = ns_map[nsURI] = self._env_ns[nsURI] + return (ns + ':', '') + + if not ns: + ns = "ns%d" % self.ncounter + self.ncounter += 1 + ns_map[nsURI] = ns + if self.config.buildWithNamespacePrefix: + return (ns + ':', ' xmlns:%s="%s"' % (ns, nsURI)) + else: + return ('', ' xmlns="%s"' % (nsURI)) + + def genroot(self, ns_map): + if self.noroot: + return '' + + if self.depth != 2: + return '' + + ns, n = self.genns(ns_map, NS.ENC) + return ' %sroot="%d"%s' % (ns, not self.multis, n) + + # checkref checks an element to see if it needs to be encoded as a + # multi-reference element or not. If it returns None, the element has + # been handled and the caller can continue with subsequent elements. + # If it returns a string, the string should be included in the opening + # tag of the marshaled element. + + def checkref(self, obj, tag, ns_map): + if self.depth < 2: + return '' + + if not self.ids.has_key(id(obj)): + n = self.ids[id(obj)] = self.icounter + self.icounter = n + 1 + + if self.use_refs == 0: + return '' + + if self.depth == 2: + return ' id="i%d"' % n + + self.multirefs.append((obj, tag)) + else: + if self.use_refs == 0: + raise RecursionError, "Cannot serialize recursive object" + + n = self.ids[id(obj)] + + if self.multis and self.depth == 2: + return ' id="i%d"' % n + + self.out.append('<%s href="#i%d"%s/>\n' % + (tag, n, self.genroot(ns_map))) + return None + + # dumpers + + def dump(self, obj, tag = None, typed = 1, ns_map = {}): + if Config.debug: print "In dump.", "obj=", obj + ns_map = ns_map.copy() + self.depth += 1 + + if type(tag) not in (NoneType, StringType, UnicodeType): + raise KeyError, "tag must be a string or None" + + self.dump_dispatch(obj, tag, typed, ns_map) + self.depth -= 1 + + # generic dumper + def dumper(self, nsURI, obj_type, obj, tag, typed = 1, ns_map = {}, + rootattr = '', id = '', + xml = '<%(tag)s%(type)s%(id)s%(attrs)s%(root)s>%(data)s\n'): + if Config.debug: print "In dumper." + + if nsURI == None: + nsURI = self.config.typesNamespaceURI + + tag = tag or self.gentag() + + tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding + + a = n = t = '' + if typed and obj_type: + ns, n = self.genns(ns_map, nsURI) + ins = self.genns(ns_map, self.config.schemaNamespaceURI)[0] + t = ' %stype="%s%s"%s' % (ins, ns, obj_type, n) + + try: a = obj._marshalAttrs(ns_map, self) + except: pass + + try: data = obj._marshalData() + except: + if (obj_type != "string"): # strings are already encoded + data = cgi.escape(str(obj)) + else: + data = obj + + + + return xml % {"tag": tag, "type": t, "data": data, "root": rootattr, + "id": id, "attrs": a} + + def dump_float(self, obj, tag, typed = 1, ns_map = {}): + if Config.debug: print "In dump_float." + tag = tag or self.gentag() + + tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding + + if Config.strict_range: + doubleType(obj) + + if fpconst.isPosInf(obj): + obj = "INF" + elif fpconst.isNegInf(obj): + obj = "-INF" + elif fpconst.isNaN(obj): + obj = "NaN" + else: + obj = repr(obj) + + # Note: python 'float' is actually a SOAP 'double'. + self.out.append(self.dumper( + None, "double", obj, tag, typed, ns_map, self.genroot(ns_map))) + + def dump_int(self, obj, tag, typed = 1, ns_map = {}): + if Config.debug: print "In dump_int." + self.out.append(self.dumper(None, 'integer', obj, tag, typed, + ns_map, self.genroot(ns_map))) + + def dump_bool(self, obj, tag, typed = 1, ns_map = {}): + if Config.debug: print "In dump_bool." + self.out.append(self.dumper(None, 'boolean', obj, tag, typed, + ns_map, self.genroot(ns_map))) + + def dump_string(self, obj, tag, typed = 0, ns_map = {}): + if Config.debug: print "In dump_string." + tag = tag or self.gentag() + tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding + + id = self.checkref(obj, tag, ns_map) + if id == None: + return + + try: data = obj._marshalData() + except: data = obj + + self.out.append(self.dumper(None, "string", cgi.escape(data), tag, + typed, ns_map, self.genroot(ns_map), id)) + + dump_str = dump_string # For Python 2.2+ + dump_unicode = dump_string + + def dump_None(self, obj, tag, typed = 0, ns_map = {}): + if Config.debug: print "In dump_None." + tag = tag or self.gentag() + tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding + ns = self.genns(ns_map, self.config.schemaNamespaceURI)[0] + + self.out.append('<%s %snull="1"%s/>\n' % + (tag, ns, self.genroot(ns_map))) + + dump_NoneType = dump_None # For Python 2.2+ + + def dump_list(self, obj, tag, typed = 1, ns_map = {}): + if Config.debug: print "In dump_list.", "obj=", obj + tag = tag or self.gentag() + tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding + + if type(obj) == InstanceType: + data = obj.data + else: + data = obj + + if typed: + id = self.checkref(obj, tag, ns_map) + if id == None: + return + + try: + sample = data[0] + empty = 0 + except: + # preserve type if present + if getattr(obj,"_typed",None) and getattr(obj,"_type",None): + if getattr(obj, "_complexType", None): + sample = typedArrayType(typed=obj._type, + complexType = obj._complexType) + sample._typename = obj._type + if not getattr(obj,"_ns",None): obj._ns = NS.URN + else: + sample = typedArrayType(typed=obj._type) + else: + sample = structType() + empty = 1 + + # First scan list to see if all are the same type + same_type = 1 + + if not empty: + for i in data[1:]: + if type(sample) != type(i) or \ + (type(sample) == InstanceType and \ + sample.__class__ != i.__class__): + same_type = 0 + break + + ndecl = '' + if same_type: + if (isinstance(sample, structType)) or \ + type(sample) == DictType or \ + (isinstance(sample, anyType) and \ + (getattr(sample, "_complexType", None) and \ + sample._complexType)): # force to urn struct + try: + tns = obj._ns or NS.URN + except: + tns = NS.URN + + ns, ndecl = self.genns(ns_map, tns) + + try: + typename = sample._typename + except: + typename = "SOAPStruct" + + t = ns + typename + + elif isinstance(sample, anyType): + ns = sample._validNamespaceURI(self.config.typesNamespaceURI, + self.config.strictNamespaces) + if ns: + ns, ndecl = self.genns(ns_map, ns) + t = ns + str(sample._type) + else: + t = 'ur-type' + else: + typename = type(sample).__name__ + + # For Python 2.2+ + if type(sample) == StringType: typename = 'string' + + # HACK: unicode is a SOAP string + if type(sample) == UnicodeType: typename = 'string' + + # HACK: python 'float' is actually a SOAP 'double'. + if typename=="float": typename="double" + t = self.genns( + ns_map, self.config.typesNamespaceURI)[0] + typename + + else: + t = self.genns(ns_map, self.config.typesNamespaceURI)[0] + \ + "ur-type" + + try: a = obj._marshalAttrs(ns_map, self) + except: a = '' + + ens, edecl = self.genns(ns_map, NS.ENC) + ins, idecl = self.genns(ns_map, self.config.schemaNamespaceURI) + + if typed: + self.out.append( + '<%s %sarrayType="%s[%d]" %stype="%sArray"%s%s%s%s%s%s>\n' % + (tag, ens, t, len(data), ins, ens, ndecl, edecl, idecl, + self.genroot(ns_map), id, a)) + + if typed: + try: elemsname = obj._elemsname + except: elemsname = "item" + else: + elemsname = tag + + if isinstance(data, (list, tuple, arrayType)): + should_drill = True + else: + should_drill = not same_type + + for i in data: + self.dump(i, elemsname, should_drill, ns_map) + + if typed: self.out.append('\n' % tag) + + dump_tuple = dump_list + + def dump_exception(self, obj, tag, typed = 0, ns_map = {}): + if isinstance(obj, faultType): # Fault + cns, cdecl = self.genns(ns_map, NS.ENC) + vns, vdecl = self.genns(ns_map, NS.ENV) + self.out.append('<%sFault %sroot="1"%s%s>' % (vns, cns, vdecl, cdecl)) + self.dump(obj.faultcode, "faultcode", typed, ns_map) + self.dump(obj.faultstring, "faultstring", typed, ns_map) + if hasattr(obj, "detail"): + self.dump(obj.detail, "detail", typed, ns_map) + self.out.append("\n" % vns) + + def dump_dictionary(self, obj, tag, typed = 1, ns_map = {}): + if Config.debug: print "In dump_dictionary." + tag = tag or self.gentag() + tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding + + id = self.checkref(obj, tag, ns_map) + if id == None: + return + + try: a = obj._marshalAttrs(ns_map, self) + except: a = '' + + self.out.append('<%s%s%s%s>\n' % + (tag, id, a, self.genroot(ns_map))) + + for (k, v) in obj.items(): + if k[0] != "_": + self.dump(v, k, 1, ns_map) + + self.out.append('\n' % tag) + + dump_dict = dump_dictionary # For Python 2.2+ + + def dump_dispatch(self, obj, tag, typed = 1, ns_map = {}): + if not tag: + # If it has a name use it. + if isinstance(obj, anyType) and obj._name: + tag = obj._name + else: + tag = self.gentag() + + # watch out for order! + dumpmap = ( + (Exception, self.dump_exception), + (arrayType, self.dump_list), + (basestring, self.dump_string), + (NoneType, self.dump_None), + (bool, self.dump_bool), + (int, self.dump_int), + (long, self.dump_int), + (list, self.dump_list), + (tuple, self.dump_list), + (dict, self.dump_dictionary), + (float, self.dump_float), + ) + for dtype, func in dumpmap: + if isinstance(obj, dtype): + func(obj, tag, typed, ns_map) + return + + r = self.genroot(ns_map) + + try: a = obj._marshalAttrs(ns_map, self) + except: a = '' + + if isinstance(obj, voidType): # void + self.out.append("<%s%s%s>\n" % (tag, a, r, tag)) + else: + id = self.checkref(obj, tag, ns_map) + if id == None: + return + + if isinstance(obj, structType): + # Check for namespace + ndecl = '' + ns = obj._validNamespaceURI(self.config.typesNamespaceURI, + self.config.strictNamespaces) + if ns: + ns, ndecl = self.genns(ns_map, ns) + tag = ns + tag + self.out.append("<%s%s%s%s%s>\n" % (tag, ndecl, id, a, r)) + + keylist = obj.__dict__.keys() + + # first write out items with order information + if hasattr(obj, '_keyord'): + for i in range(len(obj._keyord)): + self.dump(obj._aslist(i), obj._keyord[i], 1, ns_map) + keylist.remove(obj._keyord[i]) + + # now write out the rest + for k in keylist: + if (k[0] != "_"): + self.dump(getattr(obj,k), k, 1, ns_map) + + if isinstance(obj, bodyType): + self.multis = 1 + + for v, k in self.multirefs: + self.dump(v, k, typed = typed, ns_map = ns_map) + + self.out.append('\n' % tag) + + elif isinstance(obj, anyType): + t = '' + + if typed: + ns = obj._validNamespaceURI(self.config.typesNamespaceURI, + self.config.strictNamespaces) + if ns: + ons, ondecl = self.genns(ns_map, ns) + ins, indecl = self.genns(ns_map, + self.config.schemaNamespaceURI) + t = ' %stype="%s%s"%s%s' % \ + (ins, ons, obj._type, ondecl, indecl) + + self.out.append('<%s%s%s%s%s>%s\n' % + (tag, t, id, a, r, obj._marshalData(), tag)) + + else: # Some Class + self.out.append('<%s%s%s>\n' % (tag, id, r)) + + d1 = getattr(obj, '__dict__', None) + if d1 is not None: + for (k, v) in d1: + if k[0] != "_": + self.dump(v, k, 1, ns_map) + + self.out.append('\n' % tag) + + + +################################################################################ +# SOAPBuilder's more public interface +################################################################################ + +def buildSOAP(args=(), kw={}, method=None, namespace=None, + header=None, methodattrs=None, envelope=1, encoding='UTF-8', + config=Config, noroot = 0): + t = SOAPBuilder(args=args, kw=kw, method=method, namespace=namespace, + header=header, methodattrs=methodattrs,envelope=envelope, + encoding=encoding, config=config,noroot=noroot) + return t.build()