00001
00002
00003 '''SOAP message serialization.
00004 '''
00005
00006 from ZSI import _copyright, _get_idstr, ZSI_SCHEMA_URI
00007 from ZSI import _backtrace, _stringtypes, _seqtypes
00008 from ZSI.wstools.Utility import MessageInterface, ElementProxy
00009 from ZSI.wstools.Namespaces import XMLNS, SOAP, SCHEMA
00010 from ZSI.wstools.c14n import Canonicalize
00011 import types
00012
00013 _standard_ns = [ ('xml', XMLNS.XML), ('xmlns', XMLNS.BASE) ]
00014
00015 _reserved_ns = {
00016 'SOAP-ENV': SOAP.ENV,
00017 'SOAP-ENC': SOAP.ENC,
00018 'ZSI': ZSI_SCHEMA_URI,
00019 'xsd': SCHEMA.BASE,
00020 'xsi': SCHEMA.BASE + '-instance',
00021 }
00022
00023 class SoapWriter:
00024 '''SOAP output formatter.
00025 Instance Data:
00026 memo -- memory for id/href
00027 envelope -- add Envelope?
00028 encodingStyle --
00029 header -- add SOAP Header?
00030 outputclass -- ElementProxy class.
00031 '''
00032
00033 def __init__(self, envelope=True, encodingStyle=None, header=True,
00034 nsdict={}, outputclass=None, **kw):
00035 '''Initialize.
00036 '''
00037 outputclass = outputclass or ElementProxy
00038 if not issubclass(outputclass, MessageInterface):
00039 raise TypeError, 'outputclass must subclass MessageInterface'
00040
00041 self.dom, self.memo, self.nsdict= \
00042 outputclass(self), [], nsdict
00043 self.envelope = envelope
00044 self.encodingStyle = encodingStyle
00045 self.header = header
00046 self.body = None
00047 self.callbacks = []
00048 self.closed = False
00049
00050 def __str__(self):
00051 self.close()
00052 return str(self.dom)
00053
00054 def getSOAPHeader(self):
00055 if self.header in (True, False):
00056 return None
00057 return self.header
00058
00059 def serialize_header(self, pyobj, typecode=None, **kw):
00060 '''Serialize a Python object in SOAP-ENV:Header, make
00061 sure everything in Header unique (no #href). Must call
00062 serialize first to create a document.
00063
00064 Parameters:
00065 pyobjs -- instances to serialize in SOAP Header
00066 typecode -- default typecode
00067 '''
00068 kw['unique'] = True
00069 soap_env = _reserved_ns['SOAP-ENV']
00070
00071 header = self._header
00072 if header is None:
00073 header = self._header = self.dom.createAppendElement(soap_env,
00074 'Header')
00075
00076 typecode = getattr(pyobj, 'typecode', typecode)
00077 if typecode is None:
00078 raise RuntimeError(
00079 'typecode is required to serialize pyobj in header')
00080
00081 helt = typecode.serialize(header, self, pyobj, **kw)
00082
00083 def serialize(self, pyobj, typecode=None, root=None, header_pyobjs=(), **kw):
00084 '''Serialize a Python object to the output stream.
00085 pyobj -- python instance to serialize in body.
00086 typecode -- typecode describing body
00087 root -- SOAP-ENC:root
00088 header_pyobjs -- list of pyobj for soap header inclusion, each
00089 instance must specify the typecode attribute.
00090 '''
00091 self.body = None
00092 if self.envelope:
00093 soap_env = _reserved_ns['SOAP-ENV']
00094 self.dom.createDocument(soap_env, 'Envelope')
00095 for prefix, nsuri in _reserved_ns.items():
00096 self.dom.setNamespaceAttribute(prefix, nsuri)
00097 self.writeNSdict(self.nsdict)
00098 if self.encodingStyle:
00099 self.dom.setAttributeNS(soap_env, 'encodingStyle',
00100 self.encodingStyle)
00101 if self.header:
00102 self._header = self.dom.createAppendElement(soap_env, 'Header')
00103
00104 for h in header_pyobjs:
00105 self.serialize_header(h, **kw)
00106
00107 self.body = self.dom.createAppendElement(soap_env, 'Body')
00108 else:
00109 self.dom.createDocument(None,None)
00110
00111 if typecode is None:
00112 typecode = pyobj.__class__.typecode
00113
00114 if self.body is None:
00115 elt = typecode.serialize(self.dom, self, pyobj, **kw)
00116 else:
00117 elt = typecode.serialize(self.body, self, pyobj, **kw)
00118
00119 if root is not None:
00120 if root not in [ 0, 1 ]:
00121 raise ValueError, "SOAP-ENC root attribute not in [0,1]"
00122 elt.setAttributeNS(SOAP.ENC, 'root', root)
00123
00124 return self
00125
00126 def writeNSdict(self, nsdict):
00127 '''Write a namespace dictionary, taking care to not clobber the
00128 standard (or reserved by us) prefixes.
00129 '''
00130 for k,v in nsdict.items():
00131 if (k,v) in _standard_ns: continue
00132 rv = _reserved_ns.get(k)
00133 if rv:
00134 if rv != v:
00135 raise KeyError("Reserved namespace " + str((k,v)) + " used")
00136 continue
00137 if k:
00138 self.dom.setNamespaceAttribute(k, v)
00139 else:
00140 self.dom.setNamespaceAttribute('xmlns', v)
00141
00142
00143 def ReservedNS(self, prefix, uri):
00144 '''Is this namespace (prefix,uri) reserved by us?
00145 '''
00146 return _reserved_ns.get(prefix, uri) != uri
00147
00148 def AddCallback(self, func, *arglist):
00149 '''Add a callback function and argument list to be invoked before
00150 closing off the SOAP Body.
00151 '''
00152 self.callbacks.append((func, arglist))
00153
00154 def Known(self, obj):
00155 '''Seen this object (known by its id()? Return 1 if so,
00156 otherwise add it to our memory and return 0.
00157 '''
00158 obj = _get_idstr(obj)
00159 if obj in self.memo: return 1
00160 self.memo.append(obj)
00161 return 0
00162
00163 def Forget(self, obj):
00164 '''Forget we've seen this object.
00165 '''
00166 obj = _get_idstr(obj)
00167 try:
00168 self.memo.remove(obj)
00169 except ValueError:
00170 pass
00171
00172 def Backtrace(self, elt):
00173 '''Return a human-readable "backtrace" from the document root to
00174 the specified element.
00175 '''
00176 return _backtrace(elt._getNode(), self.dom._getNode())
00177
00178 def close(self):
00179 '''Invoke all the callbacks, and close off the SOAP message.
00180 '''
00181 if self.closed: return
00182 for func,arglist in self.callbacks:
00183 apply(func, arglist)
00184 self.closed = True
00185
00186 def __del__(self):
00187 if not self.closed: self.close()
00188
00189
00190 if __name__ == '__main__': print _copyright