[Zope-CVS] CVS: Packages/WebService - LICENSE.txt:1.1 PKG-INFO:1.1 SOAPCallInfo.py:1.1 SOAPMessage.py:1.1 SOAPReader.py:1.1 SOAPWriter.py:1.1 Serializer.py:1.1 ServiceProxy.py:1.1 TimeoutSocket.py:1.1 Transports.py:1.1 Utility.py:1.1 WSDLTools.py:1.1 XMLSchema.py:1.1 XMLWriter.py:1.1 __init__.py:1.1 setup.py:1.1 todo.txt:1.1
Brian Lloyd
brian@digicool.com
Mon, 26 Nov 2001 11:18:24 -0500
Update of /cvs-repository/Packages/WebService
In directory cvs.zope.org:/tmp/cvs-serv23779/WebService
Added Files:
LICENSE.txt PKG-INFO SOAPCallInfo.py SOAPMessage.py
SOAPReader.py SOAPWriter.py Serializer.py ServiceProxy.py
TimeoutSocket.py Transports.py Utility.py WSDLTools.py
XMLSchema.py XMLWriter.py __init__.py setup.py todo.txt
Log Message:
Initial commit of web services package.
=== Added File Packages/WebService/LICENSE.txt ===
Zope Public License (ZPL) Version 2.0
-----------------------------------------------
This software is Copyright (c) Zope Corporation (tm) and
Contributors. All rights reserved.
This license has been certified as open source. It has also
been designated as GPL compatible by the Free Software
Foundation (FSF).
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
1. Redistributions in source code must retain the above
copyright notice, this list of conditions, and the following
disclaimer.
2. 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.
3. The name Zope Corporation (tm) must not be used to
endorse or promote products derived from this software
without prior written permission from Zope Corporation.
4. The right to distribute this software or to use it for
any purpose does not give you the right to use Servicemarks
(sm) or Trademarks (tm) of Zope Corporation. Use of them is
covered in a separate agreement (see
http://www.zope.com/Marks).
5. If any files are modified, you must cause the modified
files to carry prominent notices stating that you changed
the files and the date of any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
AND ANY EXPRESSED 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 ZOPE CORPORATION OR ITS 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.
This software consists of contributions made by Zope
Corporation and many individuals on behalf of Zope
Corporation. Specific attributions are listed in the
accompanying credits file.
=== Added File Packages/WebService/PKG-INFO ===
Metadata-Version: 1.0
Name: WebService
Version: 1.0a
Summary: Web Services for Python
Home-page: UNKNOWN
Author: Brian Lloyd
Author-email: brian@zope.com
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
=== Added File Packages/WebService/SOAPCallInfo.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
from Utility import DOM
import WSDLTools
class SOAPCallInfo:
"""SOAPCallInfo captures the important binding information about a
SOAP operation, in a structure that is easier to work with than
raw WSDL structures."""
def __init__(self, methodName):
self.methodName = methodName
self.inheaders = []
self.outheaders = []
self.inparams = []
self.outparams = []
self.retval = None
encodingStyle = DOM.NS_SOAP_ENC
documentation = ''
soapAction = None
transport = None
namespace = None
location = None
use = 'encoded'
style = 'rpc'
def addInParameter(self, name, type, namespace=None, element_type=0):
"""Add an input parameter description to the call info."""
parameter = ParameterInfo(name, type, namespace, element_type)
self.inparams.append(parameter)
return parameter
def addOutParameter(self, name, type, namespace=None, element_type=0):
"""Add an output parameter description to the call info."""
parameter = ParameterInfo(name, type, namespace, element_type)
self.outparams.append(parameter)
return parameter
def setReturnParameter(self, name, type, namespace=None, element_type=0):
"""Set the return parameter description for the call info."""
parameter = ParameterInfo(name, type, namespace, element_type)
self.retval = parameter
return parameter
def addInHeaderInfo(self, name, type, namespace, element_type=0,
mustUnderstand=0):
"""Add an input SOAP header description to the call info."""
headerinfo = HeaderInfo(name, type, namespace, element_type)
if mustUnderstand:
headerinfo.mustUnderstand = 1
self.inheaders.append(headerinfo)
return headerinfo
def addOutHeaderInfo(self, name, type, namespace, element_type=0,
mustUnderstand=0):
"""Add an output SOAP header description to the call info."""
headerinfo = HeaderInfo(name, type, namespace, element_type)
if mustUnderstand:
headerinfo.mustUnderstand = 1
self.outheaders.append(headerinfo)
return headerinfo
def getInParameters(self):
"""Return a sequence of the in parameters of the method."""
return self.inparams
def getOutParameters(self):
"""Return a sequence of the out parameters of the method."""
return self.outparams
def getReturnParameter(self):
"""Return param info about the return value of the method."""
return self.retval
def getInHeaders(self):
"""Return a sequence of the in headers of the method."""
return self.inheaders
def getOutHeaders(self):
"""Return a sequence of the out headers of the method."""
return self.outheaders
class ParameterInfo:
"""A ParameterInfo object captures parameter binding information."""
def __init__(self, name, type, namespace=None, element_type=0):
if element_type:
self.element_type = 1
if namespace is not None:
self.namespace = namespace
self.name = name
self.type = type
element_type = 0
namespace = None
default = None
class HeaderInfo(ParameterInfo):
"""A HeaderInfo object captures SOAP header binding information."""
def __init__(self, name, type, namespace, element_type=None):
ParameterInfo.__init__(self, name, type, namespace, element_type)
mustUnderstand = 0
actor = None
def callInfoFromWSDL(port, name):
"""Return a SOAPCallInfo given a WSDL port and operation name."""
wsdl = port.getService().getWSDL()
binding = port.getBinding()
portType = binding.getPortType()
operation = portType.operations[name]
opbinding = binding.operations[name]
messages = wsdl.messages
callinfo = SOAPCallInfo(name)
addrbinding = port.getAddressBinding()
if not isinstance(addrbinding, WSDLTools.SoapAddressBinding):
raise ValueError, 'Unsupported binding type.'
callinfo.location = addrbinding.location
soapbinding = binding.findBinding(WSDLTools.SoapBinding)
if soapbinding is None:
raise ValueError, 'Missing soap:binding element.'
callinfo.transport = soapbinding.transport
callinfo.style = soapbinding.style or 'document'
soap_op_binding = opbinding.findBinding(WSDLTools.SoapOperationBinding)
if soap_op_binding is not None:
callinfo.soapAction = soap_op_binding.soapAction
callinfo.style = soap_op_binding.style or callinfo.style
parameterOrder = operation.parameterOrder
if operation.input is not None:
message = messages[operation.input.message]
msgrole = opbinding.input
mime = msgrole.findBinding(WSDLTools.MimeMultipartRelatedBinding)
if mime is not None:
raise ValueError, 'Mime bindings are not supported.'
else:
for item in msgrole.findBindings(WSDLTools.SoapHeaderBinding):
part = messages[item.message].parts[item.part]
header = callinfo.addInHeaderInfo(
part.name,
part.element or part.type,
item.namespace,
element_type = part.element and 1 or 0
)
header.encodingStyle = item.encodingStyle
body = msgrole.findBinding(WSDLTools.SoapBodyBinding)
if body is None:
raise ValueError, 'Missing soap:body binding.'
callinfo.encodingStyle = body.encodingStyle
callinfo.namespace = body.namespace
callinfo.use = body.use
if body.parts is not None:
parts = []
for name in body.parts:
parts.append(message.parts[name])
else:
parts = message.parts.values()
for part in parts:
callinfo.addInParameter(
part.name,
part.element or part.type,
element_type = part.element and 1 or 0
)
if operation.output is not None:
message = messages[operation.output.message]
msgrole = opbinding.output
mime = msgrole.findBinding(WSDLTools.MimeMultipartRelatedBinding)
if mime is not None:
raise ValueError, 'Mime bindings are not supported.'
else:
for item in msgrole.findBindings(WSDLTools.SoapHeaderBinding):
part = messages[item.message].parts[item.part]
header = callinfo.addOutHeaderInfo(
part.name,
part.element or part.type,
item.namespace,
element_type = part.element and 1 or 0
)
header.encodingStyle = item.encodingStyle
body = msgrole.findBinding(WSDLTools.SoapBodyBinding)
if body is None:
raise ValueError, 'Missing soap:body binding.'
callinfo.encodingStyle = body.encodingStyle
callinfo.namespace = body.namespace
callinfo.use = body.use
if body.parts is not None:
parts = []
for name in body.parts:
parts.append(message.parts[name])
else:
parts = message.parts.values()
if parts:
callinfo.setReturnParameter(
part.name,
part.element or part.type,
element_type = part.element and 1 or 0
)
for part in parts[1:]:
callinfo.addOutParameter(
part.name,
part.element or part.type,
element_type = part.element and 1 or 0
)
return callinfo
=== Added File Packages/WebService/SOAPMessage.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
from Serializer import Serializer, SerializationContext
from Transports import HTTPTransport
from MimeWriter import MimeWriter
from SOAPReader import SOAPReader
from SOAPWriter import SOAPWriter
from StringIO import StringIO
from Utility import DOM
class SOAPMessage:
"""A SOAPMessage provides a higher level interface for working with
SOAP messages that handles most of the details of serialization."""
def __init__(self, message_body=None):
self.body = message_body
self.parameters = []
self.mimeparts = []
self.headers = []
serializer = Serializer()
soapAction = None
methodName = None
namespace = None
version = '1.1'
style = 'rpc'
def getContentType(self):
"""Return the content type of the serialized message body."""
return getattr(self, 'content_type', 'text/xml')
def getMessageBody(self):
"""Return the serialized message body of the SOAP message."""
return self.body
def getMimePart(self, name):
"""Return the named MIME message part associated with the message,
or None if the named MIME part is not present in the message."""
for item in self.mimeparts:
if item.name == name:
return item
return None
def getMimeParts(self):
"""Return the MIME message parts associated with the SOAP message."""
return self.mimeparts
def addSoapHeader(self, name, namespace, value, type, actor=None,
mustUnderstand=0,):
"""Add a SOAP header with the given values to the message."""
header = SOAPHeader(name, namespace, value, type)
header.mustUnderstand = mustUnderstand
header.actor = actor
self.headers.append(header)
def getSoapHeader(self, name):
"""Return the named SOAP header from the message, or None if
the named header is not present in the message."""
for item in self.headers:
if item.name == name:
return item
return None
def getSoapHeaders(self):
"""Return a sequence of the SOAP headers found in the message."""
return self.headers
def getMustUnderstandHeaders(self):
"""Return a sequence of those headers in the SOAP message that
have true mustUnderstand attribute values."""
result = []
for item in self.headers:
if item.mustUnderstand == 1:
result.append(item)
return result
def getHeadersForActor(self, actorURI):
"""Return a sequence of those headers in the SOAP message that
have actor attributes matching the given value."""
nexturi = DOM.GetSOAPActorNext(self.getSOAPVersion())
result = []
for item in self.headers:
if ((actorURI == nexturi and item.actor is None) or
(item.actor == actor)):
result.append(item)
return result
def isFault(self):
"""Return true if the SOAP response message contains a fault."""
return self.getFault() is not None
def getFault(self):
"""Return the fault associated with the SOAP response message,
as a SOAPFault or None if no fault is present in the message."""
return getattr(self, 'fault', None)
def addMimeParam(self, name, content_type, data):
"""Add a MIME part to the message. Note that adding MIME parts
causes the message to be sent as a mime/multipart message."""
part = MIMEPart(name, content_type, data)
self.mimeparts.append(part)
param = Parameter(name, part, None)
self.parameters.append(param)
def addParameter(self, name, value, type, namespace=None):
"""Add a parameter to the SOAP request message. Parameters are
serialized and sent in the order they are added."""
param = Parameter(name, value, type, namespace)
self.parameters.append(param)
def getParameter(self, name):
"""Return the named message parameter of the SOAP response."""
for item in self.params:
if item.name == name:
return item
return None
def getParameters(self):
"""Return a sequence of the parameters in the SOAP response."""
return self.parameters
def getReturnValue(self):
"""A convenience method that returns the value of the first
parameter of the message (which represents the return
value in a reply from a server). This returns None if no
parameters appear in the SOAP message."""
if not len(self.parameters):
return None
return self.parameters[0].value
def beforeSerialize(self):
"""This method is called before serialization of a SOAP message.
Subclasses can implement this to customize message processing."""
pass
def afterSerialize(self):
"""This method is called after serialization of a SOAP message.
Subclasses can implement this to customize message processing."""
pass
def beforeDeserialize(self):
"""This method is called before deserialization of a SOAP message.
Subclasses can implement this to customize message processing."""
pass
def afterDeserialize(self):
"""This method is called after deserialization of a SOAP message.
Subclasses can implement this to customize message processing."""
pass
def serialize(self):
"""Encode the message data into a valid SOAP XML message, using the
standard SOAP encoding for rpc style messages."""
self.beforeSerialize()
serializer = self.serializer
context = SerializationContext(serializer)
context.writer = SOAPWriter(self.version)
writer = context.writer
writer.startEnvelope()
if self.style == 'rpc':
writer.writeEncodingStyle(writer.NS_SOAP_ENC)
values = []
for item in self.headers:
values.append(getattr(item, 'value', None))
for item in self.parameters:
values.append(getattr(item, 'value', None))
context.mapValueRefs(filter(None, values))
if self.headers:
writer.startHeader()
for item in self.headers:
if self.mimeparts and isinstance(item.value, MIMEPart):
writer.startElement(item.name, item.namespace)
writer.writeAttr('href', 'cid:%s@message' % item.name)
writer.endElement()
else:
serializer.serialize(
item.name,
item.value,
item.type,
context,
item.namespace,
)
element = writer.currentElement().childNodes[-1]
if item.mustUnderstand is not None:
element.setAttribute('mustUnderstand', '1')
if item.actor is not None:
element.setAttribute('actor', actor)
writer.startBody()
if self.style == 'rpc':
# Standard SOAP rpc style message. We use the serializer to
# convert the param values using the standard SOAP encoding.
writer.startElement(self.methodName, self.namespace)
for item in self.parameters:
if self.mimeparts and isinstance(item.value, MIMEPart):
writer.startElement(item.name, item.namespace)
writer.writeAttr('href', 'cid:%s@message' % item.name)
writer.endElement()
else:
serializer.serialize(
item.name,
item.value,
item.type,
context
)
writer.endElement()
else:
# Document style message. Be warned that exactly what we should
# be doing here is subject to change. For now, we allow either
# a string, a DOM document object, or a DOM element as the
# parameter values.
for item in self.parameters:
value = item.value
if self.mimeparts and isinstance(value, MIMEPart):
writer.startElement(item.name, item.namespace)
writer.writeAttr('href', 'cid:%s@message' % item.name)
writer.endElement()
elif hasattr(value, 'nodeType'):
if value.nodeType == value.DOCUMENT_NODE:
element = value.documentElement
strval = DOM.elementToString(element)
writer.writeXML(strval)
continue
if value.nodeType == value.ELEMENT_NODE:
strval = DOM.elementToString(value)
writer.writeXML(strval)
continue
raise ValueError(
'Parameters of document-style SOAP messages must ' \
'be a string, DOM document or DOM element object.'
)
elif type(value) in (type(''), type(u'')):
writer.writeXML(value)
continue
raise ValueError(
'Unknown parameter type.'
)
writer.endBody()
writer.endEnvelope()
output = writer.toString()
# If the message contains MIME parts, create a multipart message.
if self.mimeparts:
stream = StringIO()
writer = MimeWriter(stream)
writer.startmultipartbody('related',
plist=[('type', 'text/xml'),
('start', '<soap-envelope@message>')
]
)
soap = writer.nextpart()
soap.addheader('Content-Transfer-Encoding', '8bit')
soap.addheader('Content-ID', '<soap-envelope@message>')
body = soap.startbody('text/xml')
body.write(output)
for mimepart in self.mimeparts:
part = writer.nextpart()
part.addheader('Content-ID', '<%s@message>' % mimepart.name)
if mimepart.content_type[:4] == 'text':
part.addheader('Content-Transfer-Encoding', '8bit')
body = part.startbody(mimepart.content_type)
body.write(mimepart.data)
else:
part.addheader('Content-Transfer-Encoding', 'base64')
body = part.startbody(mimepart.content_type)
body.write(base64.encodestring(mimepart.data))
writer.lastpart()
output = stream.getvalue()
output = output.split('--', 1)[-1]
self.body = output
self.afterSerialize()
def deserialize(self):
"""Decode the response SOAP message."""
serializer = self.serializer
context = SerializationContext(serializer)
if self.body is None:
raise InvalidMessage(
'Cannot deserialize without a message body.'
)
context.reader = SOAPReader(self.body)
reader = self.reader = context.reader
self.version = reader.version
# xxx - handle mime response here.
envelope = reader.getEnvelope()
self.checkEncodingStyle(envelope)
# Check for a fault in the response message first.
fault = reader.getFault()
if fault is not None:
faultcode = reader.getFaultCode()
faultcodeNS = reader.getFaultCodeNS()
faultstring = reader.getFaultString()
faultactor = reader.getFaultActor()
# For now, save fault detail as an xml string.
detail = reader.getFaultDetail()
if detail is not None:
detail = reader.derefElement(detail)
detail = DOM.elementToString(detail)
self.fault = SOAPFault(
faultcode, faultstring, faultactor, detail, faultcodeNS
)
return
# Hmm - think about this!
headers = reader.getHeader()
if headers is not None:
self.checkEncodingStyle(headers)
for element in reader.getHeaderElements():
self.checkEncodingStyle(element)
name = element.localName
namespace = element.namespaceURI or None
type = DOM.getTypeRef(element)
try: value = serializer.deserialize(element, context)
except: value = element
header = SOAPHeader(name, value, type, namespace)
header.actor = DOM.getAttr(element, 'actor', default=None)
header.mustUnderstand = (
DOM.getAttr(element, 'mustUnderstand') == '1'
)
self.headers.append(header)
body = reader.getBody()
self.checkEncodingStyle(body)
if self.style == 'rpc':
struct = reader.getRPCStruct()
self.namespace = struct.namespaceURI
self.methodName = struct.localName
# Standard SOAP rpc style message. We use the serializer to
# convert the param values using the standard SOAP encoding.
for element in reader.getRPCParams():
self.checkEncodingStyle(element)
name = element.localName
namespace = element.namespaceURI or None
type = DOM.getTypeRef(element)
value = serializer.deserialize(element, context)
param = Parameter(name, value, type, namespace)
self.parameters.append(param)
else:
# Document style message. In this case the value attributes of
# the out params are set to reference the actual DOM elements
# that represent "document" elements in the SOAP message.
#
# XXX - actually, this is broken because we don't keep a ref to
# the reader :(
self.reader = reader
for element in reader.getBodyElements():
name = element.localName
namespace = element.namespaceURI or None
param = Parameter(name, element, None, namespace)
self.parameters.append(param)
return
def checkEncodingStyle(self, element):
encoding = DOM.getAttr(element, 'encodingStyle', None, default=None)
if encoding is not None and encoding.find(
DOM.GetSOAPEncUri(self.version)) < 0:
raise ValueError, 'Unknown encoding style: %s' % encoding
class Parameter:
"""A Parameter object models a single SOAP message parameter."""
def __init__(self, name, value, type, namespace=None):
self.namespace = namespace
self.name = name
self.type = type
self.value = value
class SOAPHeader(Parameter):
"""A SOAPHeader object models a single SOAP message header."""
def __init__(self, name, value, type, namespace):
Parameter.__init__(self, name, value, type, namespace)
mustUnderstand = 0
actor = None
class SOAPFault:
"""A SOAPFault object captures SOAP fault information."""
def __init__(self, faultcode, faultstring, faultactor=None,
detail=None, faultcodeNS=DOM.NS_SOAP_ENV,
typeref=None):
self.faultcodeNS = faultcodeNS
self.faultcode = faultcode
self.faultstring = faultstring
self.faultactor = faultactor
self.detail = detail
self.typeref = typeref
class MIMEPart:
"""A MIMEPart object represents a MIME part of a SOAP message."""
def __init__(self, name, content_type, data, headers={}):
self.name = name
self.content_type = content_type
self.headers = headers
if hasattr(data, 'read'):
data = data.read()
self.data = data
class InvalidMessage(Exception):
pass
if __name__ == '__main__':
message = SOAPMessage()
message.soapAction = 'urn:xmethodsBabelFish#BabelFish'
message.methodName = 'BabelFish'
message.namespace = 'urn:xmethodsBabelFish'
message.addParameter('translationmode', 'en_de', (DOM.NS_XSD, 'string'))
message.addParameter('sourcedata', 'This is a test!', (DOM.NS_XSD, 'string'))
message.serialize()
print message.getMessageBody()
message = SOAPMessage()
message.soapAction = 'urn:xmethodsBabelFish#BabelFish'
message.methodName = 'BabelFish'
message.namespace = 'urn:xmethodsBabelFish'
message.addParameter('translationmode', 'en_de', (DOM.NS_XSD, 'string'))
message.addParameter('sourcedata', 'This is a test!', (DOM.NS_XSD, 'string'))
message.addMimeParam('signature', 'text/html',
'<html>This is my sig!</html>'
)
message.serialize()
print message.getMessageBody()
=== Added File Packages/WebService/SOAPReader.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
from StringIO import StringIO
from string import split
from Utility import DOM
class SOAPReader:
"""A SOAPReader provides a simplified (but still low level) interface
for reading SOAP xml messages. It provides quick access to the major
components of the message, which are returned as DOM elements."""
def __init__(self, xmldata):
if not hasattr(xmldata, 'read'):
xmldata = StringIO(xmldata)
self.document = DOM.loadDocument(xmldata)
self.getEnvelope()
def __del__(self):
self.document.unlink()
def getEnvelope(self):
"""Return the SOAP envelope element of the message."""
element = getattr(self, '_env_element', None)
if element is not None:
return element
element = self.document.documentElement
if element.localName != 'Envelope':
raise InvalidMessage(
'Malformed or missing SOAP envelope element.'
)
# Initialize version information.
version = DOM.SOAPUriToVersion(element.namespaceURI)
if version is None:
raise InvalidMessage(
'Unknown SOAP envelope namespace uri.'
)
self.version = version
self.NS_SOAP_ENV = DOM.GetSOAPEnvUri(self.version)
self.NS_SOAP_ENC = DOM.GetSOAPEncUri(self.version)
self._env_element = element
return element
def getHeader(self):
"""Return the SOAP header element of the message, or None if
no header element exists in the message."""
element = getattr(self, '_header_element', None)
if element is not None:
return element
envelope = self.getEnvelope()
element = DOM.getElement(envelope, 'Header', self.NS_SOAP_ENV, None)
self._header_element = element
return element
def getHeaderElements(self):
"""Return the sequence of elements that appear in the SOAP header."""
elements = getattr(self, '_header_elements', None)
if elements is not None:
return elements
header = self.getHeader()
if header is not None:
self._header_elements = DOM.getElements(header, None, None)
else:
self._header_elements = []
return self._header_elements
def getHeaderElement(self, name, namespaceURI=None):
"""Return the element that appears in the SOAP header matching the
given name and (optional) namespace uri. A KeyError will be
raised if the named element is not present in the SOAP header."""
header = self.getHeader()
if header is None:
raise KeyError, name
result = DOM.getElement(header, name, namespaceURI, None)
if result is None:
raise KeyError, name
return result
def getMustUnderstandHeaders(self):
"""Return a sequence of the elements that appear in the SOAP header
that have a true value for the 'mustUnderstand' attribute."""
elements = getattr(self, '_must_understand', None)
if elements is not None:
return elements
self._must_understand = []
for element in self.getHeaderElements():
if DOM.getAttr(element, 'mustUnderstand', None) == '1':
self._must_understand.append(element)
return self._must_understand
def getBody(self):
"""Return the SOAP body element of the message."""
element = getattr(self, '_body_element', None)
if element is not None:
return element
envelope = self.getEnvelope()
element = DOM.getElement(envelope, 'Body', self.NS_SOAP_ENV, None)
if element is None:
raise InvalidMessage(
'Malformed or missing SOAP body element.'
)
self._body_element = element
return element
def getBodyElements(self):
"""Return the sequence elements that appear in the SOAP body."""
elements = getattr(self, '_body_elements', None)
if elements is not None:
return elements
self._body_elements = DOM.getElements(self.getBody(), None, None)
return self._body_elements
def getBodyElement(self, name, namespaceURI=None):
"""Return the element that appears in the SOAP body matching the
given name and (optional) namespace uri. A KeyError will be
raised if the named element is not present in the SOAP body."""
result = DOM.getElement(self.getBody(), name, namespaceURI, None)
if result is None:
raise KeyError, name
return result
def getRPCStruct(self):
"""Return the rpc struct (first element) of the SOAP body, or
None if the SOAP body element contains no child elements."""
element = getattr(self, '_rpc_element', self)
if element is not self:
return element
body_elements = self.getBodyElements()
self._rpc_element = None
if len(body_elements):
self._rpc_element = body_elements[0]
else:
self._rpc_element = None
return self._rpc_element
def getRPCParams(self):
"""Return the child elements of the rpc struct of the SOAP message."""
params = getattr(self, '_rpc_params', None)
if params is not None:
return params
rpc_struct = self.getRPCStruct()
if rpc_struct is not None:
self._rpc_params = DOM.getElements(self.getRPCStruct(), None, None)
else:
self._rpc_params = []
return self._rpc_params
def getRPCParam(self, name, namespaceURI=None):
"""Return the child element of the rpc struct of the message that
matches the given name and (optional) namespace uri. A KeyError
will be raised if the named parameter is not present."""
rpc_struct = self.getRPCStruct()
if rpc_struct is None:
raise KeyError, name
result = DOM.getElement(rpc_struct, name, namespaceURI, None)
if result is None:
raise KeyError, name
return result
def getRPCResult(self):
"""Return the first element of the rpc struct of the message, or
None if the rpc struct of the message has no child elements."""
elements = DOM.getElements(self.getRPCStruct(), None, None)
if not len(elements):
return None
return elements[0]
def getElementByRef(self, reference):
"""Return an (independent) element given an element id reference,
or raise a KeyError if the referenced element is not found."""
if reference[0] != '#':
raise ValueError, 'Invalid reference: %s' % reference
return self.getElementById(reference[1:])
def getElementById(self, element_id):
"""Return an (independent) element given an element id, or raise
a KeyError if no matching element is found in the document."""
element_map = getattr(self, '_element_map', None)
if element_map is not None:
return element_map[element_id]
self._element_map = DOM.getMappingById(self.document)
return self._element_map[element_id]
def derefElement(self, element):
"""Dereference a DOM element if it has an href attribute. If the
element has no href attribute, the passed in element is returned.
If the referenced element is not found, a KeyError is raised."""
reference = DOM.getAttr(element, 'href', None, None)
if reference is not None:
return self.getElementByRef(reference)
return element
def getEncodingStyle(self, element):
"""Given a DOM element of the SOAP message, return the encoding
style that applies in the context of that element, or None
if no encoding claims are made in the context of element."""
result = None
while 1:
if element.nodeType is not 1:
element = element.parentNode
continue
result = DOM.getAttr(
element, 'encodingStyle', self.NS_SOAP_ENV, None
)
if result is not None:
break
element = element.parentNode
if element.nodeType is 9:
break
return result == '' and None or result
def isFaultMessage(self):
"""Return true if the SOAP message contains a fault."""
return self.getFault() is not None
def getFault(self):
"""Return the SOAP fault element of the message, or None if the
document does not contain a fault element."""
element = getattr(self, '_fault_element', self)
if element is not self:
return element
body = self.getBody()
element = DOM.getElement(body, 'Fault', None, None)
self._fault_element = element
return element
def getFaultString(self):
"""Return the SOAP faultstring *value* as a string, or None if
the document contains no fault or no faultstring element."""
return self._faultElementStr('faultstring')
def getFaultCode(self):
"""Return the SOAP faultcode *value* as a string, or None if
the document contains no fault or no faultcode element."""
result = getattr(self, '_fault_code', self)
if result is not self:
return result
fault = self.getFault()
self._fault_code_ns = None
self._fault_code = None
if fault is None:
return None
element = DOM.getElement(fault, 'faultcode', None, None)
if element is None:
return None
fault_code = DOM.getElementText(element)
parts = split(fault_code, ':', 1)
self._fault_code = parts[-1]
if len(parts) == 2:
self._fault_code_ns = DOM.findNamespaceURI(parts[0], element)
return self._fault_code
def getFaultCodeNS(self):
"""Return the namespace uri of the faultcode value, or None if
the document contains no faultcode element or the value is
not qualified with a namespace prefix."""
self.getFaultCode()
return self._fault_code_ns
def getFaultActor(self):
"""Return the SOAP faultactor *value* as a string, or None."""
return self._faultElementStr('faultactor')
def getFaultDetail(self):
"""Return the SOAP faultdetail element of the message, or None."""
fault = self.getFault()
if fault is None:
return None
return DOM.getElement(fault, 'detail', None, None)
def _faultElementStr(self, attrname):
fault = self.getFault()
if fault is None:
return None
element = DOM.getElement(fault, attrname, None, None)
if element is None:
return None
return DOM.getElementText(element)
class InvalidMessage(Exception):
pass
=== Added File Packages/WebService/SOAPWriter.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
from XMLWriter import XMLWriter
from Utility import DOM
class SOAPWriter(XMLWriter):
"""A SOAPWriter provides a simplified interface for writing SOAP xml
messages. It extends the basic XMLWriter utility with convenience
methods for writing the major structures of a SOAP message."""
def __init__(self, version='1.1', encoding='UTF-8'):
XMLWriter.__init__(self, encoding)
self.NS_SOAP_ENV = DOM.GetSOAPEnvUri(version)
self.NS_SOAP_ENC = DOM.GetSOAPEncUri(version)
self.version = version
self.declareNSPrefix('SOAP-ENV', self.NS_SOAP_ENV)
self.declareNSPrefix('SOAP-ENC', self.NS_SOAP_ENC)
def startEnvelope(self, encodingStyle=None):
"""Begin the SOAP envelope element of the message."""
self._dup_check('_envelope')
element = self.startElement('Envelope', self.NS_SOAP_ENV)
if encodingStyle is not None:
self.writeEncodingStyle(encodingStyle)
self._envelope = element
return element
def endEnvelope(self):
"""End the SOAP envelope element of the message."""
self._stack.pop()
def startHeader(self, encodingStyle=None):
"""Begin the SOAP header element of the message."""
self._assert('_envelope')
self._dup_check('_header')
element = self.startElement('Header', self.NS_SOAP_ENV)
if encodingStyle is not None:
self.writeEncodingStyle(encodingStyle)
self._header = element
return element
def endHeader(self):
"""End the SOAP header element of the message."""
self._stack.pop()
def startHeaderElement(self, name, namespaceURI, mustUnderstand=None,
actor=None):
"""Begin a SOAP header entry element in the message."""
self._assert('_header')
self._stack.append(self._header)
element = self.startElement(name, namespaceURI)
if mustUnderstand is not None:
self.writeAttr('mustUnderstand', '1')
if actor is not None:
self.writeAttr('actor', actor)
return element
def endHeaderElement(self):
"""End a SOAP header entry element of the message."""
self._stack.pop()
self._stack.pop()
def startBody(self, encodingStyle=None):
"""Begin the SOAP body element of the message."""
self._assert('_envelope')
self._dup_check('_body')
element = self.startElement('Body', self.NS_SOAP_ENV)
if encodingStyle is not None:
self.writeEncodingStyle(encodingStyle)
self._body = element
return element
def endBody(self):
"""End the SOAP body element of the message."""
self._stack.pop()
def startBodyElement(self, name, namespaceURI=None):
"""Begin a new child element of the SOAP body element."""
self._assert('_body')
self._stack.append(self._body)
return self.startElement(name, namespaceURI)
def endBodyElement(self):
"""End a child element of the SOAP body element."""
self._stack.pop()
self._stack.pop()
def startTrailerElement(self, name, namespaceURI=None):
"""Begin a new child element of the SOAP envelope element that
follows the SOAP header and body elements of the message."""
self._assert('_envelope')
self._stack.append(self._envelope)
return self.startElement(name, namespaceURI)
def endTrailerElement(self):
"""End a trailing child element of the SOAP envelope element."""
self._stack.pop()
self._stack.pop()
def writeEncodingStyle(self, encodingStyle):
"""Write a SOAP encoding style attribute the current element."""
self.writeAttr('encodingStyle', encodingStyle, self.NS_SOAP_ENV)
def startFault(self, faultcode, faultstring, faultactor=None,
faultcodeNS=None):
"""Begin the SOAP fault element of the message."""
self._assert('_body')
self._dup_check('_fault')
self._stack.append(self._body)
element = self.startElement('Fault', self.NS_SOAP_ENV)
self._fault = element
codestr = self.makeQName(faultcodeNS or self.NS_SOAP_ENV, faultcode)
self.startElement('faultcode')
self.writeString(codestr)
self.endElement()
self.startElement('faultstring')
self.writeString(faultstring)
self.endElement()
if faultactor is not None:
self.startElement('faultactor')
self.writeString(faultactor)
self.endElement()
def endFault(self):
"""End the SOAP fault element of the message."""
self._stack.pop()
self._stack.pop()
def startFaultDetail(self):
"""Begin the fault detail element of the message."""
self._assert('_fault')
self._stack.append(self._fault)
element = self.startElement('detail', None)
return element
def endFaultDetail(self):
"""End the fault detail element of the message."""
self._stack.pop()
self._stack.pop()
def _dup_check(self, name):
if getattr(self, name, None) is not None:
raise WriterError(
'%s element already started.' % name[1:]
)
def _assert(self, name):
if not hasattr(self, name):
raise WriterError(
'%s element must be started.' % name[1:]
)
class WriterError(Exception):
pass
=== Added File Packages/WebService/Serializer.py === (851/951 lines abridged)
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
import string, types, base64, re
from Utility import DOM
class SerializationContext:
"""A SerializationContext maintains per-message serialization state
and provides a common container used to pass around various data
structures used in data serialization (including a reference to
the serializer, which is required by complex type handlers)."""
def __init__(self, serializer):
self.serializer = serializer
self.write_ns = 0
self._refmap = {}
NS_XSI = DOM.NS_XSI
reader = None
writer = None
strict = 1
def isMultiRef(self, value):
"""Return true if a value should be encoded as multi-reference."""
return self.ref_map.get(id(value), 1) != 1
def mapValueRefs(self, values):
"""Map reference relationships amongst a set of values to
enable correct SOAP serialization of multiref values."""
self._refmap = mapping = {}
for value in values:
self._map_object(value, mapping)
def _map_object(self, object, mapping, isclass=0):
ob_type = type(object)
ob_id = id(object)
if ob_type in (types.StringType, types.UnicodeType, types.BufferType):
mapping[ob_id] = mapping.get(ob_id, 0) + 1
return
elif ob_type in (types.TupleType, types.ListType):
mapping[ob_id] = mapping.get(ob_id, 0) + 1
for value in object:
self._map_object(value, mapping)
[-=- -=- -=- 851 lines omitted -=- -=- -=-]
xsd:long long
xsd:int int
xsd:short int
xsd:unsignedShort int
xsd:byte int
xsd:unsignedByte int
xsd:decimal float
xsd:double float
xsd:float float
xsd:string string
xsd:normalizedString string
xsd:anyURI string
xsd:QName string
xsd:Name string
xsd:NCName string
xsd:token string
xsd:language string
xsd:NOTATION string
xsd:NMTOKENS sequence
xsd:NMTOKEN string
xsd:IDREFS sequence
xsd:IDREF string
xsd:ENTITIES sequence
xsd:ENTITY string
xsd:ID string
xsd:dateTime ?
xsd:timePeriod ? (99)
xsd:duration ? (01)
xsd:time ?
xsd:date ?
xsd:gYearMonth ? (01)
xsd:gYear ? (01)
xsd:gMonthDay ? (01)
xsd:gDay ? (01)
xsd:gMonth ? (01)
xsd:base64Binary string
xsd:base64 string
xsd:hexBinary string
enc:base64 string
enc:arrayType sequence
"""
=== Added File Packages/WebService/ServiceProxy.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
from Serializer import Serializer, SerializationContext
from SOAPCallInfo import callInfoFromWSDL
from Transports import HTTPTransport
from SOAPMessage import SOAPMessage
from WSDLTools import WSDLReader
import weakref
class ServiceProxy:
"""A ServiceProxy provides a convenient way to call a remote web
service that is described with WSDL. The proxy exposes methods
that reflect the methods of the remote web service."""
def __init__(self, wsdl, service=None, port=None):
if not hasattr(wsdl, 'targetNamespace'):
wsdl = WSDLReader().loadFromURL(wsdl)
self._serializer = Serializer()
self._transport = HTTPTransport()
for item in wsdl.types.items():
self._serializer.loadSchema(item)
self._service = wsdl.services[service or 0]
self.__doc__ = self._service.documentation
self._port = self._service.ports[port or 0]
self._name = self._service.name
self._wsdl = wsdl
binding = self._port.getBinding()
portType = binding.getPortType()
for item in portType.operations:
callinfo = callInfoFromWSDL(self._port, item.name)
method = MethodProxy(self, callinfo)
setattr(self, item.name, method)
def _call(self, name, *args, **kwargs):
"""Call the named remote web service method."""
if len(args) and len(kwargs):
raise TypeError(
'Use positional or keyword argument only.'
)
callinfo = getattr(self, name).callinfo
params = callinfo.getInParameters()
arglist = args or []
if kwargs:
for item in params:
if not kwargs.has_key(item.name):
raise TypeError(
'Missing keyword argument: %s.' % item.name
)
argval = kwargs[item.name]
arglist.append(argval)
if len(arglist) != len(params):
raise TypeError(
'%s takes exactly %d argument%s (%d given)' % (
self.name,
len(params),
len(params) != 1 and 's' or '',
len(arglist)
))
message = SOAPMessage()
message.serializer = self._serializer
message.methodName = callinfo.methodName
message.namespace = callinfo.namespace
message.style = callinfo.style
for i in range(len(params)):
param = params[i]
value = arglist[i]
message.addParameter(param.name, value, param.type)
message.serialize()
xmldata = message.getMessageBody()
print xmldata
resp = self._transport.send(
callinfo.location,
callinfo.soapAction,
xmldata
)
if resp is None:
return None
message = SOAPMessage(resp.body)
message.serializer = self._serializer
message.style = callinfo.style
print resp.body
message.deserialize()
params = message.getParameters()
pcount = len(params)
if pcount == 0:
return None
if pcount == 1:
return params[0].value
values = {}
for param in params:
value[param.name] = param.value
return values
class MethodProxy:
""" """
def __init__(self, parent, callinfo):
self.__name__ = callinfo.methodName
self.__doc__ = callinfo.documentation
self.callinfo = callinfo
self.parent = weakref.ref(parent)
def __call__(self, *args, **kwargs):
return self.parent()._call(self.__name__, *args, **kwargs)
=== Added File Packages/WebService/TimeoutSocket.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
"""Based on code from timeout_socket.py, with some tweaks for compatibility
and efficiency. The original timeout_socket is by:
Scott Cotton <scott@chronis.pobox.com>
Lloyd Zusman <ljz@asfast.com
Phil Mayes <pmayes@olivebr.com>
Piers Lauder <piers@cs.su.oz.au>
Radovan Garabik <garabik@melkor.dnp.fmph.uniba.sk>
"""
import string, socket, select, errno
WSAEINVAL = getattr(errno, 'WSAEINVAL', 10022)
class TimeoutSocket:
"""A socket imposter that supports timeout limits."""
def __init__(self, timeout=20, sock=None):
self.timeout = float(timeout)
self.inbuf = ''
if sock is None:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock = sock
self.sock.setblocking(0)
self._rbuf = ''
self._wbuf = ''
def __getattr__(self, name):
# Delegate to real socket attributes.
return getattr(self.sock, name)
def connect(self, *addr):
timeout = self.timeout
sock = self.sock
try:
# Non-blocking mode
sock.setblocking(0)
apply(sock.connect, addr)
sock.setblocking(timeout != 0)
return 1
except socket.error,why:
if not timeout:
raise
sock.setblocking(1)
if len(why.args) == 1:
code = 0
else:
code, why = why
if code not in (
errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK
):
raise
r,w,e = select.select([],[sock],[],timeout)
if w:
try:
apply(sock.connect, addr)
return 1
except socket.error,why:
if len(why.args) == 1:
code = 0
else:
code, why = why
if code in (errno.EISCONN, WSAEINVAL):
return 1
raise
raise TimeoutError('socket connect() timeout.')
def send(self, data, flags=0):
total = len(data)
next = 0
while 1:
r, w, e = select.select([],[self.sock], [], self.timeout)
if w:
buff = data[next:next + 8192]
sent = self.sock.send(buff, flags)
next = next + sent
if next == total:
return total
continue
raise TimeoutError('socket send() timeout.')
def recv(self, amt, flags=0):
if select.select([self.sock], [], [], self.timeout)[0]:
return self.sock.recv(amt, flags)
raise Timeout('socket recv() timeout.')
buffsize = 4096
handles = 1
def makefile(self, mode="r", buffsize=-1):
self.handles = self.handles + 1
self.mode = mode
return self
def close(self):
self.handles = self.handles - 1
if self.handles == 0 and self.sock.fileno() >= 0:
self.sock.close()
def read(self, n=-1):
if not isinstance(n, type(1)):
n = -1
if n >= 0:
k = len(self._rbuf)
if n <= k:
data = self._rbuf[:n]
self._rbuf = self._rbuf[n:]
return data
n = n - k
L = [self._rbuf]
self._rbuf = ""
while n > 0:
new = self.recv(max(n, self.buffsize))
if not new: break
k = len(new)
if k > n:
L.append(new[:n])
self._rbuf = new[n:]
break
L.append(new)
n = n - k
return "".join(L)
k = max(4096, self.buffsize)
L = [self._rbuf]
self._rbuf = ""
while 1:
new = self.recv(k)
if not new: break
L.append(new)
k = min(k*2, 1024**2)
return "".join(L)
def readline(self, limit=-1):
data = ""
i = self._rbuf.find('\n')
while i < 0 and not (0 < limit <= len(self._rbuf)):
new = self.recv(self.buffsize)
if not new: break
i = new.find('\n')
if i >= 0: i = i + len(self._rbuf)
self._rbuf = self._rbuf + new
if i < 0: i = len(self._rbuf)
else: i = i+1
if 0 <= limit < len(self._rbuf): i = limit
data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
return data
def readlines(self, sizehint = 0):
total = 0
list = []
while 1:
line = self.readline()
if not line: break
list.append(line)
total += len(line)
if sizehint and total >= sizehint:
break
return list
def writelines(self, list):
self.send(''.join(list))
def write(self, data):
self.send(data)
def flush(self):
pass
class TimeoutError(Exception):
pass
=== Added File Packages/WebService/Transports.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
import string, httplib, smtplib, socket
from TimeoutSocket import TimeoutSocket
from TimeoutSocket import TimeoutError
from urlparse import urlparse
class HTTPTransport:
"""Manages the transport of SOAP messages over the HTTP protocol."""
def __init__(self, timeout=20):
self.timeout = timeout
self.redirects = {}
userAgent = 'Python WebService Toolkit'
followRedirects = 1
# This implementation maintains a cache shared by transport instances
# that remembers redirects and demands for M-POST (if anyone uses it).
_req_cache = {}
def send(self, address, soapAction, message, contentType='text/xml',
headers={}):
"""Send a SOAP message using HTTP, returning an HTTPResponse."""
address, verb = self._req_cache.get(address, (address, 'POST'))
scheme, host, path, params, query, frag = urlparse(address)
if params: path = '%s;%s' % (path, params)
if query: path = '%s?%s' % (path, query)
if frag: path = '%s#%s' % (path, frag)
if hasattr(message, 'getvalue'):
body = message.getvalue()
elif hasattr(message, 'read'):
body = message.read()
else: body = message
if scheme == 'https':
if not hasattr(socket, 'ssl'):
raise ValueError(
'This Python installation does not have SSL support.'
)
conn = TimeoutHTTPS(host, None, self.timeout)
else:
conn = TimeoutHTTP(host, None, self.timeout)
conn.putrequest(verb, path)
if verb == 'M-POST':
conn.putheader(
'Man', '"http://schemas.xmlsoap.org/soap/envelope/"; ns=01'
)
for name, value in headers.items():
conn.putheader(name, value)
sentheader = headers.has_key
if not sentheader('SOAPAction') or sentheader('01-SOAPAction'):
header = verb == 'POST' and 'SOAPAction' or '01-SOAPAction'
conn.putheader(header, soapAction)
if not sentheader('Connection'):
conn.putheader('Connection', 'close')
if not sentheader('User-Agent'):
conn.putheader('User-Agent', self.userAgent)
if not sentheader('Content-Type'):
conn.putheader('Content-Type', contentType)
if not sentheader('Content-Length'):
conn.putheader('Content-Length', str(len(body)))
conn.endheaders()
conn.send(body)
response = None
while 1:
response = conn.getresponse()
if response.status != 100:
break
conn._HTTPConnection__state = httplib._CS_REQ_SENT
conn._HTTPConnection__response = None
status = response.status
# If the server only supports M-POST, we will try again using
# the HTTP extension framework (may never happen in practice).
if status == 510 and verb == 'POST':
response.close()
self._req_cache[address] = address, 'M-POST'
return self.send(address, soapAction, message, headers)
# If we get an HTTP redirect, we will follow it automatically.
if status >= 300 and status < 400 and self.followRedirects:
location = response.msg.getheader('location')
if location is not None:
response.close()
if self.redirects.has_key(location):
raise RecursionError(
'Circular HTTP redirection detected.'
)
self.redirects[location] = 1
self._req_cache[address] = location, verb
return self.send(location, soapAction, message, headers)
raise HTTPResponse(response)
# Otherwise, any non-2xx response may or may not not contain a
# meaningful SOAP message. A 500 error *may* contain a message,
# so we will raise an HTTP error unless the response is xml.
if not (status >= 200 and status < 300):
content_type = response.msg.getheader('content-type', '')
if content_type[:8] != 'text/xml':
raise HTTPResponse(response)
return HTTPResponse(response)
class HTTPResponse:
"""Captures the information in an HTTP response message."""
def __init__(self, response):
self.status = response.status
self.reason = response.reason
self.headers = response.msg
self.body = response.read() or None
response.close()
class SMTPTransport:
"""Manages the transport of SOAP messages over the SMTP protocol."""
def __init__(self, smtphost, fromaddr, subject, timeout=20):
self.smtphost = smtphost
self.fromaddr = fromaddr
self.subject = subject
self.timeout = timeout
def send(self, address, soapAction, message, contentType='text/xml',
headers=None):
"""Send a SOAP message object via SMTP. Messages sent via
SMTP are by definition one-way, so this always returns
None on success or raises an exception from smtplib if
an error occurs while attempting to send the message."""
if address[:7] == 'mailto:':
scheme, host, address, params, query, frag = urlparse(address)
if hasattr(message, 'read'):
message = message.read()
smtpheaders = []
if headers is not None:
for name, value in headers.items():
smtpheaders.append('%s: %s' % (name, value))
smtpheaders.append('SOAPAction: %s' % soapAction)
smtpheaders.append('Subject: %s' % self.subject)
smtpheaders.append('To: <%s>' % address)
if not headers.has_key('Content-Type'):
smtpheaders.append('Content-Type: %s' % contentType)
msgdata = '%s\r\n%s' % (
string.join(smtpheaders, '\r\n'),
message
)
server = TimeoutSMTP(self.smtphost, 0, self.timeout)
server.sendmail(self.fromaddr, address, msgdata)
server.quit()
return None
from httplib import HTTPConnection, HTTPSConnection
from smtplib import SMTP, SMTP_PORT
class TimeoutHTTP(HTTPConnection):
"""A custom http object that supports socket timeout."""
def __init__(self, host, port=None, timeout=20):
HTTPConnection.__init__(self, host, port)
self.timeout = timeout
def connect(self):
self.sock = TimeoutSocket(self.timeout)
#self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((self.host, self.port))
class TimeoutHTTPS(HTTPSConnection):
"""A custom http object that supports socket timeout."""
def __init__(self, host, port=None, timeout=20):
HTTPConnection.__init__(self, str(host), port)
self.timeout = timeout
def connect(self):
# sock = TimeoutSocket(self.timeout)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.host, self.port))
realsock = getattr(sock, '_sock', sock)
ssl = socket.ssl(realsock, self.key_file, self.cert_file)
self.sock = httplib.FakeSocket(sock, ssl)
class TimeoutSMTP(SMTP):
"""A custom smtp object that supports socket timeout."""
def __init__(self, host='', port=0, timeout=20):
self.timeout = timeout
SMTP.__init__(self, str(host), port)
def connect(self, host='localhost', port = 0):
if not port:
i = host.find(':')
if i >= 0:
host, port = host[:i], host[i+1:]
try: port = int(port)
except ValueError:
raise socket.error, "nonnumeric port"
if not port: port = SMTP_PORT
self.sock = TimeoutSocket(self.timeout)
if self.debuglevel > 0: print 'connect:', (host, port)
try:
self.sock.connect((host, port))
except socket.error:
self.close()
raise
(code,msg)=self.getreply()
if self.debuglevel >0 : print "connect:", msg
return (code,msg)
=== Added File Packages/WebService/Utility.py === (463/563 lines abridged)
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
from string import join, strip, split
from UserDict import UserDict
from StringIO import StringIO
from urllib import urlopen
import xml.dom.minidom
import weakref
class DOM:
"""The DOM singleton defines a number of XML related constants and
provides a number of utility methods for DOM related tasks. It
also provides some basic abstractions so that the rest of the
package need not care about actual DOM implementation in use."""
# Namespace stuff related to the SOAP specification.
NS_SOAP_ENV_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/'
NS_SOAP_ENC_1_1 = 'http://schemas.xmlsoap.org/soap/encoding/'
NS_SOAP_ENV_1_2 = 'http://www.w3.org/2001/06/soap-envelope'
NS_SOAP_ENC_1_2 = 'http://www.w3.org/2001/06/soap-encoding'
NS_SOAP_ENV_ALL = (NS_SOAP_ENV_1_1, NS_SOAP_ENV_1_2)
NS_SOAP_ENC_ALL = (NS_SOAP_ENC_1_1, NS_SOAP_ENC_1_2)
NS_SOAP_ENV = NS_SOAP_ENV_1_1
NS_SOAP_ENC = NS_SOAP_ENC_1_1
_soap_uri_mapping = {
NS_SOAP_ENV_1_1 : '1.1',
NS_SOAP_ENV_1_2 : '1.2',
}
SOAP_ACTOR_NEXT_1_1 = 'http://schemas.xmlsoap.org/soap/actor/next'
SOAP_ACTOR_NEXT_1_2 = 'http://www.w3.org/2001/06/soap-envelope/actor/next'
SOAP_ACTOR_NEXT_ALL = (SOAP_ACTOR_NEXT_1_1, SOAP_ACTOR_NEXT_1_2)
def SOAPUriToVersion(self, uri):
"""Return the SOAP version related to an envelope uri."""
value = self._soap_uri_mapping.get(uri)
if value is not None:
[-=- -=- -=- 463 lines omitted -=- -=- -=-]
uri, localname = name
if uri:
# When using namespaces, the reader may or may not
# provide us with the original name. If not, create
# *a* valid tagName from the current context.
if tagName is None:
prefix = self._current_context[uri]
if prefix:
tagName = prefix + ":" + localname
else:
tagName = localname
if self.document:
node = self.document.createElementNS(uri, tagName)
else:
node = self.buildDocument(uri, tagName)
else:
# When the tagname is not prefixed, it just appears as
# localname
if self.document:
node = self.document.createElement(localname)
else:
node = self.buildDocument(None, localname)
for aname,value in attrs.items():
a_uri, a_localname = aname
if a_uri == xmlns_uri:
if a_localname == 'xmlns':
qname = a_localname
else:
qname = 'xmlns:' + a_localname
attr = self.document.createAttributeNS(a_uri, qname)
node.setAttributeNodeNS(attr)
elif a_uri:
prefix = self._current_context[a_uri]
if prefix:
qname = prefix + ":" + a_localname
else:
qname = a_localname
attr = self.document.createAttributeNS(a_uri, qname)
node.setAttributeNodeNS(attr)
else:
attr = self.document.createAttribute(a_localname)
node.setAttributeNode(attr)
attr.value = value
self.lastEvent[1] = [(START_ELEMENT, node), None]
self.lastEvent = self.lastEvent[1]
self.push(node)
PullDOM.startElementNS = startElementNS
=== Added File Packages/WebService/WSDLTools.py === (1281/1381 lines abridged)
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
from urllib import urlopen, basejoin
from Utility import DOM, Collection
from XMLSchema import XMLSchema
from XMLWriter import XMLWriter
from StringIO import StringIO
import md5
class WSDLReader:
"""A WSDLReader creates WSDL instances from urls and xml data."""
# Custom subclasses of WSDLReader may wish to implement a caching
# strategy or other optimizations. Because application needs vary
# so widely, we don't try to provide any caching by default.
def loadFromStream(self, file, checksum=None):
"""Return a WSDL instance loaded from a file object."""
document = DOM.loadDocument(file)
wsdl = WSDL()
wsdl.load(document)
self.checkMd5Hash(wsdl, checksum)
return wsdl
def loadFromURL(self, url, checksum=None):
"""Return a WSDL instance loaded from the given url."""
file = urlopen(url)
try: document = DOM.loadDocument(file)
finally: file.close()
wsdl = WSDL()
wsdl.location = url
wsdl.load(document)
self.checkMd5Hash(wsdl, checksum)
return wsdl
def loadFromString(self, data, checksum=None):
"""Return a WSDL instance loaded from an xml string."""
return self.loadFromStream(StringIO(data), checksum)
def loadFromFile(self, filename, checksum=None):
"""Return a WSDL instance loaded from the given file."""
file = open(filename, 'rb')
[-=- -=- -=- 1281 lines omitted -=- -=- -=-]
docnode = DOM.getElement(element, 'documentation', None, None)
if docnode is not None:
return DOM.getElementText(docnode)
return ''
def GetExtensions(element):
result = []
for item in DOM.getElements(element, None, None):
if item.namespaceURI != DOM.NS_WSDL:
result.append(item)
return result
def ExtensionToXML(writer, object):
if hasattr(object, 'toXML'):
object.toXML(writer)
else:
strval = DOM.elementToString(object)
writer.writeXML(strval)
def FindExtensions(object, kind, t_type=type(())):
result = []
if isinstance(kind, t_type):
namespaceURI, name = kind
for item in object.extensions:
if not hasattr(item, 'nodeType'):
continue
if DOM.nsUriMatch(namespaceURI, item.namespaceURI) and \
item.name == name:
result.append(item)
else:
for item in object.extensions:
if isinstance(item, kind):
result.append(item)
return result
def FindExtension(object, kind, t_type=type(())):
if isinstance(kind, t_type):
namespaceURI, name = kind
for item in object.extensions:
if not hasattr(item, 'nodeType'):
continue
if DOM.nsUriMatch(namespaceURI, item.namespaceURI) and \
item.name == name:
return item
else:
for item in object.extensions:
if isinstance(item, kind):
return item
return None
=== Added File Packages/WebService/XMLSchema.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
from Utility import DOM, Collection
from urllib import urlopen, basejoin
from StringIO import StringIO
import string, types, base64, re
class SchemaReader:
"""A SchemaReader creates XMLSchema objects from urls and xml data."""
def loadFromStream(self, file):
"""Return an XMLSchema instance loaded from a file object."""
document = DOM.loadDocument(file)
return self.loadFromDOM(document)
def loadFromString(self, data):
"""Return an XMLSchema instance loaded from an xml string."""
return self.loadFromStream(StringIO(data))
def loadFromURL(self, url):
"""Return an XMLSchema instance loaded from the given url."""
file = urlopen(url)
try: schema = self.loadFromStream(file)
finally: file.close()
return schema
def loadFromFile(self, filename):
"""Return an XMLSchema instance loaded from the given file."""
file = open(filename, 'rb')
try: schema = self.loadFromStream(file)
finally: file.close()
return schema
class XMLSchema:
# This is temporary, for the benefit of WSDL until the real thing works.
def __init__(self, element):
self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
self.element = element
def toXML(self, writer):
strval = DOM.elementToString(self.element)
writer.writeXML(strval)
class realXMLSchema:
"""A schema is a collection of schema components derived from one
or more schema documents, that is, one or more <schema> element
information items. It represents the abstract notion of a schema
rather than a single schema document (or other representation)."""
def __init__(self):
self.simpleTypes = Collection(self)
self.complexTypes = Collection(self)
self.attributes = Collection(self)
self.elements = Collection(self)
self.attrGroups = Collection(self)
self.idConstraints=None
self.modelGroups = None
self.notations = None
def load(self, document):
if document.nodeType == document.DOCUMENT_NODE:
schema = DOM.getElement(document, 'schema', None, None)
else:
schema = document
if schema is None:
raise SchemaError('Missing <schema> element.')
# Check nsuri here
self.namespace = schema.namespaceURI
self.attributeFormDefault = DOM.getAttr(
schema, 'attributeFormDefault', None, 'unqualified'
)
self.elementFormDefault = DOM.getAttr(
schema, 'elementFormDefault', None, 'unqualified'
)
self.targetNamespace = DOM.getAttr(schema, 'targetNamespace',
None, None)
self.name = DOM.getAttr(definitions, 'name', None, None)
self.documentation = GetDocumentation(definitions)
# Resolve (recursively) any import elements in the document.
imported = {}
while 1:
imports = []
for element in DOM.getElements(definitions, 'import', NS_WSDL):
location = DOM.getAttr(element, 'location')
if not imported.has_key(location):
imports.append(element)
if not imports:
break
for element in imports:
self._import(document, element)
imported[location] = 1
for element in DOM.getElements(definitions, None, None):
localName = element.localName
if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
if localName == 'schema':
self.types.addSchema(XMLSchema(element))
else:
self.extensions.append(element)
continue
elif localName == 'message':
name = DOM.getAttr(element, 'name')
docs = GetDocumentation(element)
message = self.addMessage(name, docs)
parts = DOM.getElements(element, 'part', NS_WSDL)
message.load(parts)
continue
elif localName == 'portType':
name = DOM.getAttr(element, 'name')
docs = GetDocumentation(element)
ptype = self.addPortType(name, docs)
operations = DOM.getElements(element, 'operation', NS_WSDL)
ptype.load(operations)
continue
elif localName == 'binding':
name = DOM.getAttr(element, 'name')
type = DOM.getAttr(element, 'type', default=None)
if type is None:
raise WSDLError(
'Missing type attribute for binding %s.' % name
)
type = type.split(':', 1)[-1]
docs = GetDocumentation(element)
binding = self.addBinding(name, type, docs)
operations = DOM.getElements(element, 'operation', NS_WSDL)
binding.load(operations)
binding.load_ex(GetExtensions(element))
continue
elif localName == 'service':
name = DOM.getAttr(element, 'name')
docs = GetDocumentation(element)
service = self.addService(name, docs)
ports = DOM.getElements(element, 'port', NS_WSDL)
service.load(ports)
service.load_ex(GetExtensions(element))
continue
elif localName == 'types':
self.types.documentation = GetDocumentation(element)
for item in DOM.getElements(element, None, None):
if item.localName == 'schema':
self.types.addSchema(XMLSchema(item))
else:
self.types.addExtension(item)
continue
def _import(self, document, element):
namespace = DOM.getAttr(element, 'namespace', default=None)
location = DOM.getAttr(element, 'location', default=None)
if namespace is None or location is None:
raise WSDLError(
'Invalid import element (missing namespace or location).'
)
# Sort-of support relative locations to simplify unit testing. The
# WSDL specification actually doesn't allow relative URLs, so its
# ok that this only works with urls relative to the initial document.
location = basejoin(self.location, location)
obimport = self.addImport(namespace, location)
obimport._loaded = 1
importdoc = DOM.loadFromURL(location)
try:
if location.find('#') > -1:
idref = location.split('#')[-1]
imported = DOM.getElementById(importdoc, idref)
else:
imported = importdoc.documentElement
if imported is None:
raise WSDLError(
'Import target element not found for: %s' % location
)
imported_tns = DOM.getAttr(imported, 'targetNamespace')
importer_tns = namespace
if imported_tns != importer_tns:
return
if imported.localName == 'definitions':
imported_nodes = imported.childNodes
else:
imported_nodes = [imported]
parent = element.parentNode
for node in imported_nodes:
if node.nodeType != node.ELEMENT_NODE:
continue
child = DOM.importNode(document, node, 1)
parent.appendChild(child)
child.setAttribute('targetNamespace', importer_tns)
attrsNS = imported._attrsNS
for attrkey in attrsNS.keys():
if attrkey[0] == DOM.NS_XMLNS:
attr = attrsNS[attrkey].cloneNode(1)
child.setAttributeNode(attr)
finally:
importdoc.unlink()
class SimpleTypeDefinition:
"""Represents an xml schema simple type definition."""
class ComplexTypeDefinition:
"""Represents an xml schema complex type definition."""
class AttributeDeclaration:
"""Represents an xml schema attribute declaration."""
class ElementDeclaration:
"""Represents an xml schema element declaration."""
class AttributeGroupDefinition:
"""Represents an xml schema attribute group definition."""
class IdentityConstraintDefinition:
"""Represents an xml schema identity constraint definition."""
class ModelGroupDefinition:
"""Represents an xml schema model group definition."""
class NotationDeclaration:
"""Represents an xml schema notation declaration."""
class Annotation:
"""Represents an xml schema annotation."""
class ModelGroup:
"""Represents an xml schema model group."""
class Particle:
"""Represents an xml schema particle."""
class WildCard:
"""Represents an xml schema wildcard."""
class AttributeUse:
"""Represents an xml schema attribute use."""
class ElementComponent:
namespace = ''
name = ''
type = None
form = 'qualified | unqualified'
scope = 'global or complex def'
constraint = ('value', 'default | fixed')
nillable = 0
id_constraint_defs = None
sub_group_affil = None
sub_group_exclusions = None
disallowed_subs = 'substitution, extension, restriction'
abstract = 0
minOccurs = 1
maxOccurs = 1
ref = ''
class AttributeThing:
name = ''
namespace = ''
typeName = ''
typeUri = ''
scope = 'global | local to complex def'
constraint = ('value:default', 'value:fixed')
use = 'optional | prohibited | required'
class ElementDataType:
namespace = ''
name = ''
element_form = 'qualified | unqualified'
attr_form = None
type_name = ''
type_uri = ''
def __init__(self, name, namespace, type_name, type_uri):
self.namespace = namespace
self.name = name
# type may be anonymous...
self.type_name = type_name
self.type_uri = type_uri
def checkValue(self, value, context):
# Delegate value checking to the type of the element.
typeref = (self.type_uri, self.type_name)
handler = context.serializer.getType(typeref)
return handler.checkValue(value, context)
def serialize(self, name, namespace, value, context, **kwargs):
if context.check_values:
self.checkValue(value, context)
# Delegate serialization to the type of the element.
typeref = (self.type_uri, self.type_name)
handler = context.serializer.getType(typeref)
return handler.serialize(self.name, self.namespace, value, context)
def deserialize(self, element, context):
if element_is_null(element, context):
return None
# Delegate deserialization to the type of the element.
typeref = (self.type_uri, self.type_name)
handler = context.serializer.getType(typeref)
return handler.deserialize(element, context)
def parse_schema(data):
targetNS = ''
attributeFormDefault = 0
elementFormDefault = 0
blockDefault = ''
finalDefault = ''
language = None
version = None
id = ''
=== Added File Packages/WebService/XMLWriter.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
from StringIO import StringIO
from Utility import DOM
class XMLWriter:
"""An XMLWriter provides a simplified interface for writing xml
documents. The underlying DOM document may be accessed through
the 'document' attribute of the writer. Note that the lifetime
of the DOM document is bound to the lifetime of the writer."""
def __init__(self, encoding='UTF-8'):
self.encoding = encoding
self.document = None
self._stack = []
self._nsmap = {}
def __del__(self):
if self.document is not None:
self.document.unlink()
self.document = None
def startElement(self, name, namespaceURI=None, **attrs):
"""Begin a new element at the current point in the document."""
tagname = self.makeQName(namespaceURI, name)
if self.document is None:
self.document = DOM.createDocument(namespaceURI, tagname)
element = self.document.childNodes[0]
self._stack.append(element)
for name, value in attrs.items():
self.writeAttr(name, value)
return element
if namespaceURI is not None:
element = self.document.createElementNS(namespaceURI, tagname)
else:
element = self.document.createElement(tagname)
stack = self._stack
stack[-1].appendChild(element)
stack.append(element)
for name, value in attrs.items():
self.writeAttr(name, value)
return element
def endElement(self):
"""End the last started element."""
self._stack.pop()
def currentElement(self):
"""Return the currently active DOM element of the writer."""
return self._stack[-1]
def writeAttr(self, name, value, namespaceURI=None):
"""Write an attribute to the current open element."""
element = self._stack[-1]
if namespaceURI is not None:
attrname = self.makeQName(namespaceURI, name)
element.setAttributeNS(namespaceURI, attrname, value)
else:
element.setAttribute(name, value)
def writeText(self, value):
"""Write a string to the current element in the xml stream.
Special characters in the given value will be escaped."""
textnode = self.document.createTextNode(value)
self._stack[-1].appendChild(textnode)
def writeCDATA(self, value):
"""Write a CDATA section at the current point in the stream."""
textnode = self.document.createCDATASection(value)
self._stack[-1].appendChild(textnode)
def writeXML(self, value):
"""Write a literal xml string to the stream. The data passed
will not be escaped or modified on output."""
textnode = LiteralText(value)
textnode.ownerDocument = self.document
self._stack[-1].appendChild(textnode)
def declareNSDefault(self, namespaceURI):
"""Declare the default namespace uri to be used in the document."""
if hasattr(self, '_nsdefault'):
raise WriterError(
'A default namespace is already set.'
)
self._nsdefault = namespaceURI
self._nsmap[namespaceURI] = None
def declareNSPrefix(self, prefix, namespaceURI):
"""Add an xml namespace prefix declaration to the document. All
namespace declarations are written to the top level element."""
if self._nsmap.has_key(namespaceURI):
raise WriterError(
'A prefix has already been declared for: %s' % namespaceURI
)
self._nsmap[namespaceURI] = prefix
def hasNSPrefix(self, namespaceURI):
"""Return true if a prefix is declared for the given namespace uri."""
return self._nsmap.has_key(namespaceURI)
def getNSPrefix(self, namespaceURI):
"""Return the prefix to be used for the given namespace uri. A
namespace prefix will be generated (and declared in the xml
document) if a prefix for that uri has not been declared."""
prefix = self._nsmap.get(namespaceURI, 0)
if prefix is not 0:
return prefix
nsnum = getattr(self, '_nsnum', 0)
self._nsnum = nsnum + 1
prefix = 'ns%d' % nsnum
self.declareNSPrefix(prefix, namespaceURI)
return prefix
def makeQName(self, namespaceURI, name):
"""Return an appropriate qname, given a name and namespace uri."""
if namespaceURI is None:
return name
prefix = self.getNSPrefix(namespaceURI)
if prefix is None: # default ns
return name
return '%s:%s' % (prefix, name)
def makeRefId(self):
"""Return a generated unique id for use as an element reference."""
idnum = getattr(self, '_idnum', 0)
self._idnum = idnum + 1
return 'ref-%d' % idnum
def toString(self, format=0):
"""Return the xml stream as a string. If the format argument is
a true value, the output will be formatted for readability."""
if len(self._stack):
tagname = self._stack[-1].tagName
raise WriterError(
'Missing endElement call for: %s' % tagname
)
element = self.document.childNodes[0]
default = getattr(self, '_nsdefault', None)
if default is not None:
element.setAttribute('xmlns', default)
for namespaceURI, prefix in self._nsmap.items():
if prefix is not None:
element.setAttribute('xmlns:%s' % prefix, namespaceURI)
stream = StringIO()
stream.write('<?xml version="1.0" encoding="%s"?>\n' % (
self.encoding
))
for child in self.document.childNodes:
DOM.elementToStream(child, stream, format)
return stream.getvalue()
from xml.dom.minidom import Text
class LiteralText(Text):
def writexml(self, writer, indent="", addindent="", newl=""):
writer.write("%s%s" % (self.data, newl))
class WriterError(Exception):
pass
=== Added File Packages/WebService/__init__.py ===
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
"""Web services package for Python."""
__version__='$Revision: 1.1 $'[11:-2]
# We make use of weak references in various places to prevent leaking of
# circular structures, so we need to setup pickling support for weak refs
# to allow certain objects to be pickled.
import weakref, copy_reg
def save_weakref(object):
return (weakref.ref, (object(),))
copy_reg.pickle(weakref.ReferenceType, save_weakref, weakref.ref)
=== Added File Packages/WebService/setup.py ===
#!/usr/bin/env python
from distutils.core import setup
setup(name="WebService",
version="1.0a",
description="Web Services for Python",
author="Brian Lloyd",
author_email="brian@zope.com",
url="",
py_modules=(
'__init__',
'ServiceProxy',
'SOAPCallInfo',
'SOAPMessage',
'SOAPReader',
'SOAPWriter',
'Serializer',
'WSDLTools',
'XMLSchema',
'XMLWriter',
'Transports',
'TimeoutSocket',
'Utility',
),
)
=== Added File Packages/WebService/todo.txt ===
Punch-out list:
- finish SOAPMessage docs
- connection timeouts for WSDL & schema readers
- rethink Message params apis
- write getting started docs
- finish Schema and serializer docs
- implement DWIM SOAP method proxy
- Support HTTP basic auth for default proxies
- implement schema model and update serializer
- null handling in serializer
- decide how to handle document-style parameters
- implement client interop test suite + reports
- add copyright / legal mumbo jumbo to files
- finish MIME part handling in SOAPMessage
- speed tweaks for WSDL parsing
- see if HTTPS works with timeouts
- decide if WSDL really needs to keep DOM document /
loss of ns in wsdl on read / change?