--- /dev/null
+"""
+################################################################################
+# 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 = '<?xml version="1.0"?>\n'
+ _xml_enc_top = '<?xml version="1.0" encoding="%s"?>\n'
+ _env_top = ( '%(ENV_T)s:Envelope\n' + \
+ ' %(ENV_T)s:encodingStyle="%(ENC)s"\n' ) % \
+ NS.__dict__
+ _env_bot = '</%(ENV_T)s:Envelope>\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("</%s%s>\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("</%sBody>\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</%(tag)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('</%s>\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("</%sFault>\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('</%s>\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></%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('</%s>\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</%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('</%s>\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()