Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

bundled wstools-0.3

  • Loading branch information...
commit a98ef1f178022ecab298d6bb08198f466d6856e5 1 parent 092c60e
@forrestv authored
View
1  wstools/.cvsignore
@@ -0,0 +1 @@
+*.pyc
View
110 wstools/MIMEAttachment.py
@@ -0,0 +1,110 @@
+#TODO add the license
+#I had to rewrite this class because the python MIME email.mime (version 2.5)
+#are buggy, they use \n instead \r\n for new line which is not compliant
+#to standard!
+# http://bugs.python.org/issue5525
+
+#TODO do not load all the message in memory stream it from the disk
+
+import re
+import random
+import sys
+
+
+#new line
+NL='\r\n'
+
+_width = len(repr(sys.maxint-1))
+_fmt = '%%0%dd' % _width
+
+class MIMEMessage:
+
+ def __init__(self):
+ self._files = []
+ self._xmlMessage = ""
+ self._startCID = ""
+ self._boundary = ""
+
+ def makeBoundary(self):
+ #create the boundary
+ msgparts = []
+ msgparts.append(self._xmlMessage)
+ for i in self._files:
+ msgparts.append(i.read())
+ #this sucks, all in memory
+ alltext = NL.join(msgparts)
+ self._boundary = _make_boundary(alltext)
+ #maybe I can save some memory
+ del alltext
+ del msgparts
+ self._startCID = "<" + (_fmt % random.randrange(sys.maxint)) + (_fmt % random.randrange(sys.maxint)) + ">"
+
+
+ def toString(self):
+ '''it return a string with the MIME message'''
+ if len(self._boundary) == 0:
+ #the makeBoundary hasn't been called yet
+ self.makeBoundary()
+ #ok we have everything let's start to spit the message out
+ #first the XML
+ returnstr = NL + "--" + self._boundary + NL
+ returnstr += "Content-Type: text/xml; charset=\"us-ascii\"" + NL
+ returnstr += "Content-Transfer-Encoding: 7bit" + NL
+ returnstr += "Content-Id: " + self._startCID + NL + NL
+ returnstr += self._xmlMessage + NL
+ #then the files
+ for file in self._files:
+ returnstr += "--" + self._boundary + NL
+ returnstr += "Content-Type: application/octet-stream" + NL
+ returnstr += "Content-Transfer-Encoding: binary" + NL
+ returnstr += "Content-Id: <" + str(id(file)) + ">" + NL + NL
+ file.seek(0)
+ returnstr += file.read() + NL
+ #closing boundary
+ returnstr += "--" + self._boundary + "--" + NL
+ return returnstr
+
+ def attachFile(self, file):
+ '''
+ it adds a file to this attachment
+ '''
+ self._files.append(file)
+
+ def addXMLMessage(self, xmlMessage):
+ '''
+ it adds the XML message. we can have only one XML SOAP message
+ '''
+ self._xmlMessage = xmlMessage
+
+ def getBoundary(self):
+ '''
+ this function returns the string used in the mime message as a
+ boundary. First the write method as to be called
+ '''
+ return self._boundary
+
+ def getStartCID(self):
+ '''
+ This function returns the CID of the XML message
+ '''
+ return self._startCID
+
+
+def _make_boundary(text=None):
+ #some code taken from python stdlib
+ # Craft a random boundary. If text is given, ensure that the chosen
+ # boundary doesn't appear in the text.
+ token = random.randrange(sys.maxint)
+ boundary = ('=' * 10) + (_fmt % token) + '=='
+ if text is None:
+ return boundary
+ b = boundary
+ counter = 0
+ while True:
+ cre = re.compile('^--' + re.escape(b) + '(--)?$', re.MULTILINE)
+ if not cre.search(text):
+ break
+ b = boundary + '.' + str(counter)
+ counter += 1
+ return b
+
View
214 wstools/Namespaces.py
@@ -0,0 +1,214 @@
+# 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.
+"""Namespace module, so you don't need PyXML
+"""
+
+ident = "$Id$"
+try:
+ from xml.ns import SOAP, SCHEMA, WSDL, XMLNS, DSIG, ENCRYPTION
+ DSIG.C14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
+
+except:
+ class SOAP:
+ ENV = "http://schemas.xmlsoap.org/soap/envelope/"
+ ENC = "http://schemas.xmlsoap.org/soap/encoding/"
+ ACTOR_NEXT = "http://schemas.xmlsoap.org/soap/actor/next"
+
+ class SCHEMA:
+ XSD1 = "http://www.w3.org/1999/XMLSchema"
+ XSD2 = "http://www.w3.org/2000/10/XMLSchema"
+ XSD3 = "http://www.w3.org/2001/XMLSchema"
+ XSD_LIST = [ XSD1, XSD2, XSD3]
+ XSI1 = "http://www.w3.org/1999/XMLSchema-instance"
+ XSI2 = "http://www.w3.org/2000/10/XMLSchema-instance"
+ XSI3 = "http://www.w3.org/2001/XMLSchema-instance"
+ XSI_LIST = [ XSI1, XSI2, XSI3 ]
+ BASE = XSD3
+
+ class WSDL:
+ BASE = "http://schemas.xmlsoap.org/wsdl/"
+ BIND_HTTP = "http://schemas.xmlsoap.org/wsdl/http/"
+ BIND_MIME = "http://schemas.xmlsoap.org/wsdl/mime/"
+ BIND_SOAP = "http://schemas.xmlsoap.org/wsdl/soap/"
+ BIND_SOAP12 = "http://schemas.xmlsoap.org/wsdl/soap12/"
+
+ class XMLNS:
+ BASE = "http://www.w3.org/2000/xmlns/"
+ XML = "http://www.w3.org/XML/1998/namespace"
+ HTML = "http://www.w3.org/TR/REC-html40"
+
+ class DSIG:
+ BASE = "http://www.w3.org/2000/09/xmldsig#"
+ C14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
+ C14N_COMM = "http://www.w3.org/TR/2000/CR-xml-c14n-20010315#WithComments"
+ C14N_EXCL = "http://www.w3.org/2001/10/xml-exc-c14n#"
+ DIGEST_MD2 = "http://www.w3.org/2000/09/xmldsig#md2"
+ DIGEST_MD5 = "http://www.w3.org/2000/09/xmldsig#md5"
+ DIGEST_SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
+ ENC_BASE64 = "http://www.w3.org/2000/09/xmldsig#base64"
+ ENVELOPED = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
+ HMAC_SHA1 = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"
+ SIG_DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"
+ SIG_RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
+ XPATH = "http://www.w3.org/TR/1999/REC-xpath-19991116"
+ XSLT = "http://www.w3.org/TR/1999/REC-xslt-19991116"
+
+ class ENCRYPTION:
+ BASE = "http://www.w3.org/2001/04/xmlenc#"
+ BLOCK_3DES = "http://www.w3.org/2001/04/xmlenc#des-cbc"
+ BLOCK_AES128 = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
+ BLOCK_AES192 = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"
+ BLOCK_AES256 = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
+ DIGEST_RIPEMD160 = "http://www.w3.org/2001/04/xmlenc#ripemd160"
+ DIGEST_SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256"
+ DIGEST_SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512"
+ KA_DH = "http://www.w3.org/2001/04/xmlenc#dh"
+ KT_RSA_1_5 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ KT_RSA_OAEP = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
+ STREAM_ARCFOUR = "http://www.w3.org/2001/04/xmlenc#arcfour"
+ WRAP_3DES = "http://www.w3.org/2001/04/xmlenc#kw-3des"
+ WRAP_AES128 = "http://www.w3.org/2001/04/xmlenc#kw-aes128"
+ WRAP_AES192 = "http://www.w3.org/2001/04/xmlenc#kw-aes192"
+ WRAP_AES256 = "http://www.w3.org/2001/04/xmlenc#kw-aes256"
+
+
+class WSRF_V1_2:
+ '''OASIS WSRF Specifications Version 1.2
+ '''
+ class LIFETIME:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd"
+ XSD_DRAFT4 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceLifetime-1.2-draft-04.xsd"
+
+ WSDL_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.wsdl"
+ WSDL_DRAFT4 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceLifetime-1.2-draft-04.wsdl"
+ LATEST = WSDL_DRAFT4
+ WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT4)
+ XSD_LIST = (XSD_DRAFT1, XSD_DRAFT4)
+
+ class PROPERTIES:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"
+ XSD_DRAFT5 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceProperties-1.2-draft-05.xsd"
+
+ WSDL_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
+ WSDL_DRAFT5 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceProperties-1.2-draft-05.wsdl"
+ LATEST = WSDL_DRAFT5
+ WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT5)
+ XSD_LIST = (XSD_DRAFT1, XSD_DRAFT5)
+
+ class BASENOTIFICATION:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd"
+
+ WSDL_DRAFT1 = "http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl"
+ LATEST = WSDL_DRAFT1
+ WSDL_LIST = (WSDL_DRAFT1,)
+ XSD_LIST = (XSD_DRAFT1,)
+
+ class BASEFAULTS:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-BaseFaults-1.2-draft-01.xsd"
+ XSD_DRAFT3 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-BaseFaults-1.2-draft-03.xsd"
+ #LATEST = DRAFT3
+ #WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT3)
+ XSD_LIST = (XSD_DRAFT1, XSD_DRAFT3)
+
+WSRF = WSRF_V1_2
+WSRFLIST = (WSRF_V1_2,)
+
+
+class OASIS:
+ '''URLs for Oasis specifications
+ '''
+ WSSE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
+ UTILITY = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
+
+ class X509TOKEN:
+ Base64Binary = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
+ STRTransform = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0"
+ PKCS7 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#PKCS7"
+ X509 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509"
+ X509PKIPathv1 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"
+ X509v3SubjectKeyIdentifier = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3SubjectKeyIdentifier"
+
+ LIFETIME = WSRF_V1_2.LIFETIME.XSD_DRAFT1
+ PROPERTIES = WSRF_V1_2.PROPERTIES.XSD_DRAFT1
+ BASENOTIFICATION = WSRF_V1_2.BASENOTIFICATION.XSD_DRAFT1
+ BASEFAULTS = WSRF_V1_2.BASEFAULTS.XSD_DRAFT1
+
+
+class APACHE:
+ '''This name space is defined by AXIS and it is used for the TC in TCapache.py,
+ Map and file attachment (DataHandler)
+ '''
+ AXIS_NS = "http://xml.apache.org/xml-soap"
+
+
+class WSTRUST:
+ BASE = "http://schemas.xmlsoap.org/ws/2004/04/trust"
+ ISSUE = "http://schemas.xmlsoap.org/ws/2004/04/trust/Issue"
+
+class WSSE:
+ BASE = "http://schemas.xmlsoap.org/ws/2002/04/secext"
+ TRUST = WSTRUST.BASE
+
+
+class WSU:
+ BASE = "http://schemas.xmlsoap.org/ws/2002/04/utility"
+ UTILITY = "http://schemas.xmlsoap.org/ws/2002/07/utility"
+
+
+class WSR:
+ PROPERTIES = "http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceProperties"
+ LIFETIME = "http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceLifetime"
+
+
+class WSA200508:
+ ADDRESS = "http://www.w3.org/2005/08/addressing"
+ ANONYMOUS = "%s/anonymous" %ADDRESS
+ FAULT = "%s/fault" %ADDRESS
+
+class WSA200408:
+ ADDRESS = "http://schemas.xmlsoap.org/ws/2004/08/addressing"
+ ANONYMOUS = "%s/role/anonymous" %ADDRESS
+ FAULT = "%s/fault" %ADDRESS
+
+class WSA200403:
+ ADDRESS = "http://schemas.xmlsoap.org/ws/2004/03/addressing"
+ ANONYMOUS = "%s/role/anonymous" %ADDRESS
+ FAULT = "%s/fault" %ADDRESS
+
+class WSA200303:
+ ADDRESS = "http://schemas.xmlsoap.org/ws/2003/03/addressing"
+ ANONYMOUS = "%s/role/anonymous" %ADDRESS
+ FAULT = None
+
+
+WSA = WSA200408
+WSA_LIST = (WSA200508, WSA200408, WSA200403, WSA200303)
+
+class _WSAW(str):
+ """ Define ADDRESS attribute to be compatible with WSA* layout """
+ ADDRESS = property(lambda s: s)
+
+WSAW200605 = _WSAW("http://www.w3.org/2006/05/addressing/wsdl")
+
+WSAW_LIST = (WSAW200605,)
+
+class WSP:
+ POLICY = "http://schemas.xmlsoap.org/ws/2002/12/policy"
+
+class BEA:
+ SECCONV = "http://schemas.xmlsoap.org/ws/2004/04/sc"
+ SCTOKEN = "http://schemas.xmlsoap.org/ws/2004/04/security/sc/sct"
+
+class GLOBUS:
+ SECCONV = "http://wsrf.globus.org/core/2004/07/security/secconv"
+ CORE = "http://www.globus.org/namespaces/2004/06/core"
+ SIG = "http://www.globus.org/2002/04/xmlenc#gssapi-sign"
+ TOKEN = "http://www.globus.org/ws/2004/09/security/sc#GSSAPI_GSI_TOKEN"
+
+ZSI_SCHEMA_URI = 'http://www.zolera.com/schemas/ZSI/'
View
179 wstools/TimeoutSocket.py
@@ -0,0 +1,179 @@
+"""Based on code from timeout_socket.py, with some tweaks for compatibility.
+ These tweaks should really be rolled back into timeout_socket, but it's
+ not totally clear who is maintaining it at this point. In the meantime,
+ we'll use a different module name for our tweaked version to avoid any
+ confusion.
+
+ 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>
+"""
+
+ident = "$Id$"
+
+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 TimeoutError('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
View
99 wstools/UserTuple.py
@@ -0,0 +1,99 @@
+"""
+A more or less complete user-defined wrapper around tuple objects.
+Adapted version of the standard library's UserList.
+
+Taken from Stefan Schwarzer's ftputil library, available at
+<http://www.ndh.net/home/sschwarzer/python/python_software.html>, and used under this license:
+
+
+
+
+Copyright (C) 1999, Stefan Schwarzer
+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 the above author nor the names of the
+ contributors to the software 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.
+
+"""
+
+
+
+
+# $Id$
+
+#XXX tuple instances (in Python 2.2) contain also:
+# __class__, __delattr__, __getattribute__, __hash__, __new__,
+# __reduce__, __setattr__, __str__
+# What about these?
+
+class UserTuple:
+ def __init__(self, inittuple=None):
+ self.data = ()
+ if inittuple is not None:
+ # XXX should this accept an arbitrary sequence?
+ if type(inittuple) == type(self.data):
+ self.data = inittuple
+ elif isinstance(inittuple, UserTuple):
+ # this results in
+ # self.data is inittuple.data
+ # but that's ok for tuples because they are
+ # immutable. (Builtin tuples behave the same.)
+ self.data = inittuple.data[:]
+ else:
+ # the same applies here; (t is tuple(t)) == 1
+ self.data = tuple(inittuple)
+ def __repr__(self): return repr(self.data)
+ def __lt__(self, other): return self.data < self.__cast(other)
+ def __le__(self, other): return self.data <= self.__cast(other)
+ def __eq__(self, other): return self.data == self.__cast(other)
+ def __ne__(self, other): return self.data != self.__cast(other)
+ def __gt__(self, other): return self.data > self.__cast(other)
+ def __ge__(self, other): return self.data >= self.__cast(other)
+ def __cast(self, other):
+ if isinstance(other, UserTuple): return other.data
+ else: return other
+ def __cmp__(self, other):
+ return cmp(self.data, self.__cast(other))
+ def __contains__(self, item): return item in self.data
+ def __len__(self): return len(self.data)
+ def __getitem__(self, i): return self.data[i]
+ def __getslice__(self, i, j):
+ i = max(i, 0); j = max(j, 0)
+ return self.__class__(self.data[i:j])
+ def __add__(self, other):
+ if isinstance(other, UserTuple):
+ return self.__class__(self.data + other.data)
+ elif isinstance(other, type(self.data)):
+ return self.__class__(self.data + other)
+ else:
+ return self.__class__(self.data + tuple(other))
+ # dir( () ) contains no __radd__ (at least in Python 2.2)
+ def __mul__(self, n):
+ return self.__class__(self.data*n)
+ __rmul__ = __mul__
+
View
1,382 wstools/Utility.py
@@ -0,0 +1,1382 @@
+# Copyright (c) 2003, The Regents of the University of California,
+# through Lawrence Berkeley National Laboratory (subject to receipt of
+# any required approvals from the U.S. Dept. of Energy). All rights
+# reserved.
+#
+# 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.
+
+ident = "$Id$"
+
+import sys, types, httplib, urllib, socket, weakref
+from os.path import isfile
+from string import join, strip, split
+from UserDict import UserDict
+from cStringIO import StringIO
+from TimeoutSocket import TimeoutSocket, TimeoutError
+from urlparse import urlparse
+from httplib import HTTPConnection, HTTPSConnection
+from exceptions import Exception
+try:
+ from ZSI import _get_idstr
+except:
+ def _get_idstr(pyobj):
+ '''Python 2.3.x generates a FutureWarning for negative IDs, so
+ we use a different prefix character to ensure uniqueness, and
+ call abs() to avoid the warning.'''
+ x = id(pyobj)
+ if x < 0:
+ return 'x%x' % abs(x)
+ return 'o%x' % x
+
+import xml.dom.minidom
+from xml.dom import Node
+
+import logging
+from c14n import Canonicalize
+from Namespaces import SCHEMA, SOAP, XMLNS, ZSI_SCHEMA_URI
+
+
+try:
+ from xml.dom.ext import SplitQName
+except:
+ def SplitQName(qname):
+ '''SplitQName(qname) -> (string, string)
+
+ Split Qualified Name into a tuple of len 2, consisting
+ of the prefix and the local name.
+
+ (prefix, localName)
+
+ Special Cases:
+ xmlns -- (localName, 'xmlns')
+ None -- (None, localName)
+ '''
+
+ l = qname.split(':')
+ if len(l) == 1:
+ l.insert(0, None)
+ elif len(l) == 2:
+ if l[0] == 'xmlns':
+ l.reverse()
+ else:
+ return
+ return tuple(l)
+
+#
+# python2.3 urllib.basejoin does not remove current directory ./
+# from path and this causes problems on subsequent basejoins.
+#
+basejoin = urllib.basejoin
+if sys.version_info[0:2] < (2, 4, 0, 'final', 0)[0:2]:
+ #basejoin = lambda base,url: urllib.basejoin(base,url.lstrip('./'))
+ token = './'
+ def basejoin(base, url):
+ if url.startswith(token) is True:
+ return urllib.basejoin(base,url[2:])
+ return urllib.basejoin(base,url)
+
+class NamespaceError(Exception):
+ """Used to indicate a Namespace Error."""
+
+
+class RecursionError(Exception):
+ """Used to indicate a HTTP redirect recursion."""
+
+
+class ParseError(Exception):
+ """Used to indicate a XML parsing error."""
+
+
+class DOMException(Exception):
+ """Used to indicate a problem processing DOM."""
+
+
+class Base:
+ """Base class for instance level Logging"""
+ def __init__(self, module=__name__):
+ self.logger = logging.getLogger('%s-%s(%s)' %(module, self.__class__, _get_idstr(self)))
+
+
+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 TimeoutHTTP(HTTPConnection):
+ """A custom http connection 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.connect((self.host, self.port))
+
+
+class TimeoutHTTPS(HTTPSConnection):
+ """A custom https object that supports socket timeout. Note that this
+ is not really complete. The builtin SSL support in the Python socket
+ module requires a real socket (type) to be passed in to be hooked to
+ SSL. That means our fake socket won't work and our timeout hacks are
+ bypassed for send and recv calls. Since our hack _is_ in place at
+ connect() time, it should at least provide some timeout protection."""
+ def __init__(self, host, port=None, timeout=20, **kwargs):
+ HTTPSConnection.__init__(self, str(host), port, **kwargs)
+ self.timeout = timeout
+
+ def connect(self):
+ sock = TimeoutSocket(self.timeout)
+ sock.connect((self.host, self.port))
+ realsock = getattr(sock.sock, '_sock', sock.sock)
+ ssl = socket.ssl(realsock, self.key_file, self.cert_file)
+ self.sock = httplib.FakeSocket(sock, ssl)
+
+
+def urlopen(url, timeout=20, redirects=None):
+ """A minimal urlopen replacement hack that supports timeouts for http.
+ Note that this supports GET only."""
+ scheme, host, path, params, query, frag = urlparse(url)
+
+ if not scheme in ('http', 'https'):
+ return urllib.urlopen(url)
+ if params: path = '%s;%s' % (path, params)
+ if query: path = '%s?%s' % (path, query)
+ if frag: path = '%s#%s' % (path, frag)
+
+ if scheme == 'https':
+ # If ssl is not compiled into Python, you will not get an exception
+ # until a conn.endheaders() call. We need to know sooner, so use
+ # getattr.
+ try:
+ import M2Crypto
+ except ImportError:
+ if not hasattr(socket, 'ssl'):
+ raise RuntimeError, 'no built-in SSL Support'
+
+ conn = TimeoutHTTPS(host, None, timeout)
+ else:
+ ctx = M2Crypto.SSL.Context()
+ ctx.set_session_timeout(timeout)
+ conn = M2Crypto.httpslib.HTTPSConnection(host, ssl_context=ctx)
+ conn.set_debuglevel(1)
+
+ else:
+ conn = TimeoutHTTP(host, None, timeout)
+
+ conn.putrequest('GET', path)
+ conn.putheader('Connection', 'close')
+ conn.endheaders()
+ 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 we get an HTTP redirect, we will follow it automatically.
+ if status >= 300 and status < 400:
+ location = response.msg.getheader('location')
+ if location is not None:
+ response.close()
+ if redirects is not None and redirects.has_key(location):
+ raise RecursionError(
+ 'Circular HTTP redirection detected.'
+ )
+ if redirects is None:
+ redirects = {}
+ redirects[location] = 1
+ return urlopen(location, timeout, redirects)
+ raise HTTPResponse(response)
+
+ if not (status >= 200 and status < 300):
+ raise HTTPResponse(response)
+
+ body = StringIO(response.read())
+ response.close()
+ return body
+
+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:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP envelope uri: %s' % uri
+ )
+
+ def GetSOAPEnvUri(self, version):
+ """Return the appropriate SOAP envelope uri for a given
+ human-friendly SOAP version string (e.g. '1.1')."""
+ attrname = 'NS_SOAP_ENV_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attrname, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP version: %s' % version
+ )
+
+ def GetSOAPEncUri(self, version):
+ """Return the appropriate SOAP encoding uri for a given
+ human-friendly SOAP version string (e.g. '1.1')."""
+ attrname = 'NS_SOAP_ENC_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attrname, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP version: %s' % version
+ )
+
+ def GetSOAPActorNextUri(self, version):
+ """Return the right special next-actor uri for a given
+ human-friendly SOAP version string (e.g. '1.1')."""
+ attrname = 'SOAP_ACTOR_NEXT_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attrname, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP version: %s' % version
+ )
+
+
+ # Namespace stuff related to XML Schema.
+
+ NS_XSD_99 = 'http://www.w3.org/1999/XMLSchema'
+ NS_XSI_99 = 'http://www.w3.org/1999/XMLSchema-instance'
+
+ NS_XSD_00 = 'http://www.w3.org/2000/10/XMLSchema'
+ NS_XSI_00 = 'http://www.w3.org/2000/10/XMLSchema-instance'
+
+ NS_XSD_01 = 'http://www.w3.org/2001/XMLSchema'
+ NS_XSI_01 = 'http://www.w3.org/2001/XMLSchema-instance'
+
+ NS_XSD_ALL = (NS_XSD_99, NS_XSD_00, NS_XSD_01)
+ NS_XSI_ALL = (NS_XSI_99, NS_XSI_00, NS_XSI_01)
+
+ NS_XSD = NS_XSD_01
+ NS_XSI = NS_XSI_01
+
+ _xsd_uri_mapping = {
+ NS_XSD_99 : NS_XSI_99,
+ NS_XSD_00 : NS_XSI_00,
+ NS_XSD_01 : NS_XSI_01,
+ }
+
+ for key, value in _xsd_uri_mapping.items():
+ _xsd_uri_mapping[value] = key
+
+
+ def InstanceUriForSchemaUri(self, uri):
+ """Return the appropriate matching XML Schema instance uri for
+ the given XML Schema namespace uri."""
+ return self._xsd_uri_mapping.get(uri)
+
+ def SchemaUriForInstanceUri(self, uri):
+ """Return the appropriate matching XML Schema namespace uri for
+ the given XML Schema instance namespace uri."""
+ return self._xsd_uri_mapping.get(uri)
+
+
+ # Namespace stuff related to WSDL.
+
+ NS_WSDL_1_1 = 'http://schemas.xmlsoap.org/wsdl/'
+ NS_WSDL_ALL = (NS_WSDL_1_1,)
+ NS_WSDL = NS_WSDL_1_1
+
+ NS_SOAP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/'
+ NS_HTTP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/http/'
+ NS_MIME_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/mime/'
+
+ NS_SOAP_BINDING_ALL = (NS_SOAP_BINDING_1_1,)
+ NS_HTTP_BINDING_ALL = (NS_HTTP_BINDING_1_1,)
+ NS_MIME_BINDING_ALL = (NS_MIME_BINDING_1_1,)
+
+ NS_SOAP_BINDING = NS_SOAP_BINDING_1_1
+ NS_HTTP_BINDING = NS_HTTP_BINDING_1_1
+ NS_MIME_BINDING = NS_MIME_BINDING_1_1
+
+ NS_SOAP_HTTP_1_1 = 'http://schemas.xmlsoap.org/soap/http'
+ NS_SOAP_HTTP_ALL = (NS_SOAP_HTTP_1_1,)
+ NS_SOAP_HTTP = NS_SOAP_HTTP_1_1
+
+
+ _wsdl_uri_mapping = {
+ NS_WSDL_1_1 : '1.1',
+ }
+
+ def WSDLUriToVersion(self, uri):
+ """Return the WSDL version related to a WSDL namespace uri."""
+ value = self._wsdl_uri_mapping.get(uri)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP envelope uri: %s' % uri
+ )
+
+ def GetWSDLUri(self, version):
+ attr = 'NS_WSDL_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLSoapBindingUri(self, version):
+ attr = 'NS_SOAP_BINDING_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLHttpBindingUri(self, version):
+ attr = 'NS_HTTP_BINDING_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLMimeBindingUri(self, version):
+ attr = 'NS_MIME_BINDING_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLHttpTransportUri(self, version):
+ attr = 'NS_SOAP_HTTP_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+
+ # Other xml namespace constants.
+ NS_XMLNS = 'http://www.w3.org/2000/xmlns/'
+
+
+
+ def isElement(self, node, name, nsuri=None):
+ """Return true if the given node is an element with the given
+ name and optional namespace uri."""
+ if node.nodeType != node.ELEMENT_NODE:
+ return 0
+ return node.localName == name and \
+ (nsuri is None or self.nsUriMatch(node.namespaceURI, nsuri))
+
+ def getElement(self, node, name, nsuri=None, default=join):
+ """Return the first child of node with a matching name and
+ namespace uri, or the default if one is provided."""
+ nsmatch = self.nsUriMatch
+ ELEMENT_NODE = node.ELEMENT_NODE
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if ((child.localName == name or name is None) and
+ (nsuri is None or nsmatch(child.namespaceURI, nsuri))
+ ):
+ return child
+ if default is not join:
+ return default
+ raise KeyError, name
+
+ def getElementById(self, node, id, default=join):
+ """Return the first child of node matching an id reference."""
+ attrget = self.getAttr
+ ELEMENT_NODE = node.ELEMENT_NODE
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if attrget(child, 'id') == id:
+ return child
+ if default is not join:
+ return default
+ raise KeyError, name
+
+ def getMappingById(self, document, depth=None, element=None,
+ mapping=None, level=1):
+ """Create an id -> element mapping of those elements within a
+ document that define an id attribute. The depth of the search
+ may be controlled by using the (1-based) depth argument."""
+ if document is not None:
+ element = document.documentElement
+ mapping = {}
+ attr = element._attrs.get('id', None)
+ if attr is not None:
+ mapping[attr.value] = element
+ if depth is None or depth > level:
+ level = level + 1
+ ELEMENT_NODE = element.ELEMENT_NODE
+ for child in element.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ self.getMappingById(None, depth, child, mapping, level)
+ return mapping
+
+ def getElements(self, node, name, nsuri=None):
+ """Return a sequence of the child elements of the given node that
+ match the given name and optional namespace uri."""
+ nsmatch = self.nsUriMatch
+ result = []
+ ELEMENT_NODE = node.ELEMENT_NODE
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if ((child.localName == name or name is None) and (
+ (nsuri is None) or nsmatch(child.namespaceURI, nsuri))):
+ result.append(child)
+ return result
+
+ def hasAttr(self, node, name, nsuri=None):
+ """Return true if element has attribute with the given name and
+ optional nsuri. If nsuri is not specified, returns true if an
+ attribute exists with the given name with any namespace."""
+ if nsuri is None:
+ if node.hasAttribute(name):
+ return True
+ return False
+ return node.hasAttributeNS(nsuri, name)
+
+ def getAttr(self, node, name, nsuri=None, default=join):
+ """Return the value of the attribute named 'name' with the
+ optional nsuri, or the default if one is specified. If
+ nsuri is not specified, an attribute that matches the
+ given name will be returned regardless of namespace."""
+ if nsuri is None:
+ result = node._attrs.get(name, None)
+ if result is None:
+ for item in node._attrsNS.keys():
+ if item[1] == name:
+ result = node._attrsNS[item]
+ break
+ else:
+ result = node._attrsNS.get((nsuri, name), None)
+ if result is not None:
+ return result.value
+ if default is not join:
+ return default
+ return ''
+
+ def getAttrs(self, node):
+ """Return a Collection of all attributes
+ """
+ attrs = {}
+ for k,v in node._attrs.items():
+ attrs[k] = v.value
+ return attrs
+
+ def getElementText(self, node, preserve_ws=None):
+ """Return the text value of an xml element node. Leading and trailing
+ whitespace is stripped from the value unless the preserve_ws flag
+ is passed with a true value."""
+ result = []
+ for child in node.childNodes:
+ nodetype = child.nodeType
+ if nodetype == child.TEXT_NODE or \
+ nodetype == child.CDATA_SECTION_NODE:
+ result.append(child.nodeValue)
+ value = join(result, '')
+ if preserve_ws is None:
+ value = strip(value)
+ return value
+
+ def findNamespaceURI(self, prefix, node):
+ """Find a namespace uri given a prefix and a context node."""
+ attrkey = (self.NS_XMLNS, prefix)
+ DOCUMENT_NODE = node.DOCUMENT_NODE
+ ELEMENT_NODE = node.ELEMENT_NODE
+ while 1:
+ if node is None:
+ raise DOMException('Value for prefix %s not found.' % prefix)
+ if node.nodeType != ELEMENT_NODE:
+ node = node.parentNode
+ continue
+ result = node._attrsNS.get(attrkey, None)
+ if result is not None:
+ return result.value
+ if hasattr(node, '__imported__'):
+ raise DOMException('Value for prefix %s not found.' % prefix)
+ node = node.parentNode
+ if node.nodeType == DOCUMENT_NODE:
+ raise DOMException('Value for prefix %s not found.' % prefix)
+
+ def findDefaultNS(self, node):
+ """Return the current default namespace uri for the given node."""
+ attrkey = (self.NS_XMLNS, 'xmlns')
+ DOCUMENT_NODE = node.DOCUMENT_NODE
+ ELEMENT_NODE = node.ELEMENT_NODE
+ while 1:
+ if node.nodeType != ELEMENT_NODE:
+ node = node.parentNode
+ continue
+ result = node._attrsNS.get(attrkey, None)
+ if result is not None:
+ return result.value
+ if hasattr(node, '__imported__'):
+ raise DOMException('Cannot determine default namespace.')
+ node = node.parentNode
+ if node.nodeType == DOCUMENT_NODE:
+ raise DOMException('Cannot determine default namespace.')
+
+ def findTargetNS(self, node):
+ """Return the defined target namespace uri for the given node."""
+ attrget = self.getAttr
+ attrkey = (self.NS_XMLNS, 'xmlns')
+ DOCUMENT_NODE = node.DOCUMENT_NODE
+ ELEMENT_NODE = node.ELEMENT_NODE
+ while 1:
+ if node.nodeType != ELEMENT_NODE:
+ node = node.parentNode
+ continue
+ result = attrget(node, 'targetNamespace', default=None)
+ if result is not None:
+ return result
+ node = node.parentNode
+ if node.nodeType == DOCUMENT_NODE:
+ raise DOMException('Cannot determine target namespace.')
+
+ def getTypeRef(self, element):
+ """Return (namespaceURI, name) for a type attribue of the given
+ element, or None if the element does not have a type attribute."""
+ typeattr = self.getAttr(element, 'type', default=None)
+ if typeattr is None:
+ return None
+ parts = typeattr.split(':', 1)
+ if len(parts) == 2:
+ nsuri = self.findNamespaceURI(parts[0], element)
+ else:
+ nsuri = self.findDefaultNS(element)
+ return (nsuri, parts[1])
+
+ def importNode(self, document, node, deep=0):
+ """Implements (well enough for our purposes) DOM node import."""
+ nodetype = node.nodeType
+ if nodetype in (node.DOCUMENT_NODE, node.DOCUMENT_TYPE_NODE):
+ raise DOMException('Illegal node type for importNode')
+ if nodetype == node.ENTITY_REFERENCE_NODE:
+ deep = 0
+ clone = node.cloneNode(deep)
+ self._setOwnerDoc(document, clone)
+ clone.__imported__ = 1
+ return clone
+
+ def _setOwnerDoc(self, document, node):
+ node.ownerDocument = document
+ for child in node.childNodes:
+ self._setOwnerDoc(document, child)
+
+ def nsUriMatch(self, value, wanted, strict=0, tt=type(())):
+ """Return a true value if two namespace uri values match."""
+ if value == wanted or (type(wanted) is tt) and value in wanted:
+ return 1
+ if not strict and value is not None:
+ wanted = type(wanted) is tt and wanted or (wanted,)
+ value = value[-1:] != '/' and value or value[:-1]
+ for item in wanted:
+ if item == value or item[:-1] == value:
+ return 1
+ return 0
+
+ def createDocument(self, nsuri, qname, doctype=None):
+ """Create a new writable DOM document object."""
+ impl = xml.dom.minidom.getDOMImplementation()
+ return impl.createDocument(nsuri, qname, doctype)
+
+ def loadDocument(self, data):
+ """Load an xml file from a file-like object and return a DOM
+ document instance."""
+ return xml.dom.minidom.parse(data)
+
+ def loadFromURL(self, url):
+ """Load an xml file from a URL and return a DOM document."""
+ if isfile(url) is True:
+ file = open(url, 'r')
+ else:
+ file = urlopen(url)
+
+ try:
+ result = self.loadDocument(file)
+ except Exception, ex:
+ file.close()
+ raise ParseError(('Failed to load document %s' %url,) + ex.args)
+ else:
+ file.close()
+ return result
+
+DOM = DOM()
+
+
+class MessageInterface:
+ '''Higher Level Interface, delegates to DOM singleton, must
+ be subclassed and implement all methods that throw NotImplementedError.
+ '''
+ def __init__(self, sw):
+ '''Constructor, May be extended, do not override.
+ sw -- soapWriter instance
+ '''
+ self.sw = None
+ if type(sw) != weakref.ReferenceType and sw is not None:
+ self.sw = weakref.ref(sw)
+ else:
+ self.sw = sw
+
+ def AddCallback(self, func, *arglist):
+ self.sw().AddCallback(func, *arglist)
+
+ def Known(self, obj):
+ return self.sw().Known(obj)
+
+ def Forget(self, obj):
+ return self.sw().Forget(obj)
+
+ def canonicalize(self):
+ '''canonicalize the underlying DOM, and return as string.
+ '''
+ raise NotImplementedError, ''
+
+ def createDocument(self, namespaceURI=SOAP.ENV, localName='Envelope'):
+ '''create Document
+ '''
+ raise NotImplementedError, ''
+
+ def createAppendElement(self, namespaceURI, localName):
+ '''create and append element(namespaceURI,localName), and return
+ the node.
+ '''
+ raise NotImplementedError, ''
+
+ def findNamespaceURI(self, qualifiedName):
+ raise NotImplementedError, ''
+
+ def resolvePrefix(self, prefix):
+ raise NotImplementedError, ''
+
+ def setAttributeNS(self, namespaceURI, localName, value):
+ '''set attribute (namespaceURI, localName)=value
+ '''
+ raise NotImplementedError, ''
+
+ def setAttributeType(self, namespaceURI, localName):
+ '''set attribute xsi:type=(namespaceURI, localName)
+ '''
+ raise NotImplementedError, ''
+
+ def setNamespaceAttribute(self, namespaceURI, prefix):
+ '''set namespace attribute xmlns:prefix=namespaceURI
+ '''
+ raise NotImplementedError, ''
+
+
+class ElementProxy(Base, MessageInterface):
+ '''
+ '''
+ _soap_env_prefix = 'SOAP-ENV'
+ _soap_enc_prefix = 'SOAP-ENC'
+ _zsi_prefix = 'ZSI'
+ _xsd_prefix = 'xsd'
+ _xsi_prefix = 'xsi'
+ _xml_prefix = 'xml'
+ _xmlns_prefix = 'xmlns'
+
+ _soap_env_nsuri = SOAP.ENV
+ _soap_enc_nsuri = SOAP.ENC
+ _zsi_nsuri = ZSI_SCHEMA_URI
+ _xsd_nsuri = SCHEMA.XSD3
+ _xsi_nsuri = SCHEMA.XSI3
+ _xml_nsuri = XMLNS.XML
+ _xmlns_nsuri = XMLNS.BASE
+
+ standard_ns = {\
+ _xml_prefix:_xml_nsuri,
+ _xmlns_prefix:_xmlns_nsuri
+ }
+ reserved_ns = {\
+ _soap_env_prefix:_soap_env_nsuri,
+ _soap_enc_prefix:_soap_enc_nsuri,
+ _zsi_prefix:_zsi_nsuri,
+ _xsd_prefix:_xsd_nsuri,
+ _xsi_prefix:_xsi_nsuri,
+ }
+ name = None
+ namespaceURI = None
+
+ def __init__(self, sw, message=None):
+ '''Initialize.
+ sw -- SoapWriter
+ '''
+ self._indx = 0
+ MessageInterface.__init__(self, sw)
+ Base.__init__(self)
+ self._dom = DOM
+ self.node = None
+ if type(message) in (types.StringType,types.UnicodeType):
+ self.loadFromString(message)
+ elif isinstance(message, ElementProxy):
+ self.node = message._getNode()
+ else:
+ self.node = message
+ self.processorNss = self.standard_ns.copy()
+ self.processorNss.update(self.reserved_ns)
+
+ def __str__(self):
+ return self.toString()
+
+ def evaluate(self, expression, processorNss=None):
+ '''expression -- XPath compiled expression
+ '''
+ from Ft.Xml import XPath
+ if not processorNss:
+ context = XPath.Context.Context(self.node, processorNss=self.processorNss)
+ else:
+ context = XPath.Context.Context(self.node, processorNss=processorNss)
+ nodes = expression.evaluate(context)
+ return map(lambda node: ElementProxy(self.sw,node), nodes)
+
+ #############################################
+ # Methods for checking/setting the
+ # classes (namespaceURI,name) node.
+ #############################################
+ def checkNode(self, namespaceURI=None, localName=None):
+ '''
+ namespaceURI -- namespace of element
+ localName -- local name of element
+ '''
+ namespaceURI = namespaceURI or self.namespaceURI
+ localName = localName or self.name
+ check = False
+ if localName and self.node:
+ check = self._dom.isElement(self.node, localName, namespaceURI)
+ if not check:
+ raise NamespaceError, 'unexpected node type %s, expecting %s' %(self.node, localName)
+
+ def setNode(self, node=None):
+ if node:
+ if isinstance(node, ElementProxy):
+ self.node = node._getNode()
+ else:
+ self.node = node
+ elif self.node:
+ node = self._dom.getElement(self.node, self.name, self.namespaceURI, default=None)
+ if not node:
+ raise NamespaceError, 'cant find element (%s,%s)' %(self.namespaceURI,self.name)
+ self.node = node
+ else:
+ #self.node = self._dom.create(self.node, self.name, self.namespaceURI, default=None)
+ self.createDocument(self.namespaceURI, localName=self.name, doctype=None)
+
+ self.checkNode()
+
+ #############################################
+ # Wrapper Methods for direct DOM Element Node access
+ #############################################
+ def _getNode(self):
+ return self.node
+
+ def _getElements(self):
+ return self._dom.getElements(self.node, name=None)
+
+ def _getOwnerDocument(self):
+ return self.node.ownerDocument or self.node
+
+ def _getUniquePrefix(self):
+ '''I guess we need to resolve all potential prefixes
+ because when the current node is attached it copies the
+ namespaces into the parent node.
+ '''
+ while 1:
+ self._indx += 1
+ prefix = 'ns%d' %self._indx
+ try:
+ self._dom.findNamespaceURI(prefix, self._getNode())
+ except DOMException, ex:
+ break
+ return prefix
+
+ def _getPrefix(self, node, nsuri):
+ '''
+ Keyword arguments:
+ node -- DOM Element Node
+ nsuri -- namespace of attribute value
+ '''
+ try:
+ if node and (node.nodeType == node.ELEMENT_NODE) and \
+ (nsuri == self._dom.findDefaultNS(node)):
+ return None
+ except DOMException, ex:
+ pass
+ if nsuri == XMLNS.XML:
+ return self._xml_prefix
+ if node.nodeType == Node.ELEMENT_NODE:
+ for attr in node.attributes.values():
+ if attr.namespaceURI == XMLNS.BASE \
+ and nsuri == attr.value:
+ return attr.localName
+ else:
+ if node.parentNode:
+ return self._getPrefix(node.parentNode, nsuri)
+ raise NamespaceError, 'namespaceURI "%s" is not defined' %nsuri
+
+ def _appendChild(self, node):
+ '''
+ Keyword arguments:
+ node -- DOM Element Node
+ '''
+ if node is None:
+ raise TypeError, 'node is None'
+ self.node.appendChild(node)
+
+ def _insertBefore(self, newChild, refChild):
+ '''
+ Keyword arguments:
+ child -- DOM Element Node to insert
+ refChild -- DOM Element Node
+ '''
+ self.node.insertBefore(newChild, refChild)
+
+ def _setAttributeNS(self, namespaceURI, qualifiedName, value):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of attribute
+ qualifiedName -- qualified name of new attribute value
+ value -- value of attribute
+ '''
+ self.node.setAttributeNS(namespaceURI, qualifiedName, value)
+
+ #############################################
+ #General Methods
+ #############################################
+ def isFault(self):
+ '''check to see if this is a soap:fault message.
+ '''
+ return False
+
+ def getPrefix(self, namespaceURI):
+ try:
+ prefix = self._getPrefix(node=self.node, nsuri=namespaceURI)
+ except NamespaceError, ex:
+ prefix = self._getUniquePrefix()
+ self.setNamespaceAttribute(prefix, namespaceURI)
+ return prefix
+
+ def getDocument(self):
+ return self._getOwnerDocument()
+
+ def setDocument(self, document):
+ self.node = document
+
+ def importFromString(self, xmlString):
+ doc = self._dom.loadDocument(StringIO(xmlString))
+ node = self._dom.getElement(doc, name=None)
+ clone = self.importNode(node)
+ self._appendChild(clone)
+
+ def importNode(self, node):
+ if isinstance(node, ElementProxy):
+ node = node._getNode()
+ return self._dom.importNode(self._getOwnerDocument(), node, deep=1)
+
+ def loadFromString(self, data):
+ self.node = self._dom.loadDocument(StringIO(data))
+
+ def canonicalize(self):
+ return Canonicalize(self.node)
+
+ def toString(self):
+ return self.canonicalize()
+
+ def createDocument(self, namespaceURI, localName, doctype=None):
+ '''If specified must be a SOAP envelope, else may contruct an empty document.
+ '''
+ prefix = self._soap_env_prefix
+
+ if namespaceURI == self.reserved_ns[prefix]:
+ qualifiedName = '%s:%s' %(prefix,localName)
+ elif namespaceURI is localName is None:
+ self.node = self._dom.createDocument(None,None,None)
+ return
+ else:
+ raise KeyError, 'only support creation of document in %s' %self.reserved_ns[prefix]
+
+ document = self._dom.createDocument(nsuri=namespaceURI, qname=qualifiedName, doctype=doctype)
+ self.node = document.childNodes[0]
+
+ #set up reserved namespace attributes
+ for prefix,nsuri in self.reserved_ns.items():
+ self._setAttributeNS(namespaceURI=self._xmlns_nsuri,
+ qualifiedName='%s:%s' %(self._xmlns_prefix,prefix),
+ value=nsuri)
+
+ #############################################
+ #Methods for attributes
+ #############################################
+ def hasAttribute(self, namespaceURI, localName):
+ return self._dom.hasAttr(self._getNode(), name=localName, nsuri=namespaceURI)
+
+ def setAttributeType(self, namespaceURI, localName):
+ '''set xsi:type
+ Keyword arguments:
+ namespaceURI -- namespace of attribute value
+ localName -- name of new attribute value
+
+ '''
+ self.logger.debug('setAttributeType: (%s,%s)', namespaceURI, localName)
+ value = localName
+ if namespaceURI:
+ value = '%s:%s' %(self.getPrefix(namespaceURI),localName)
+
+ xsi_prefix = self.getPrefix(self._xsi_nsuri)
+ self._setAttributeNS(self._xsi_nsuri, '%s:type' %xsi_prefix, value)
+
+ def createAttributeNS(self, namespace, name, value):
+ document = self._getOwnerDocument()
+ ##this function doesn't exist!! it has only two arguments
+ attrNode = document.createAttributeNS(namespace, name, value)
+
+ def setAttributeNS(self, namespaceURI, localName, value):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of attribute to create, None is for
+ attributes in no namespace.
+ localName -- local name of new attribute
+ value -- value of new attribute
+ '''
+ prefix = None
+ if namespaceURI:
+ try:
+ prefix = self.getPrefix(namespaceURI)
+ except KeyError, ex:
+ prefix = 'ns2'
+ self.setNamespaceAttribute(prefix, namespaceURI)
+ qualifiedName = localName
+ if prefix:
+ qualifiedName = '%s:%s' %(prefix, localName)
+ self._setAttributeNS(namespaceURI, qualifiedName, value)
+
+ def setNamespaceAttribute(self, prefix, namespaceURI):
+ '''
+ Keyword arguments:
+ prefix -- xmlns prefix
+ namespaceURI -- value of prefix
+ '''
+ self._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
+
+ #############################################
+ #Methods for elements
+ #############################################
+ def createElementNS(self, namespace, qname):
+ '''
+ Keyword arguments:
+ namespace -- namespace of element to create
+ qname -- qualified name of new element
+ '''
+ document = self._getOwnerDocument()
+ node = document.createElementNS(namespace, qname)
+ return ElementProxy(self.sw, node)
+
+ def createAppendSetElement(self, namespaceURI, localName, prefix=None):
+ '''Create a new element (namespaceURI,name), append it
+ to current node, then set it to be the current node.
+ Keyword arguments:
+ namespaceURI -- namespace of element to create
+ localName -- local name of new element
+ prefix -- if namespaceURI is not defined, declare prefix. defaults
+ to 'ns1' if left unspecified.
+ '''
+ node = self.createAppendElement(namespaceURI, localName, prefix=None)
+ node=node._getNode()
+ self._setNode(node._getNode())
+
+ def createAppendElement(self, namespaceURI, localName, prefix=None):
+ '''Create a new element (namespaceURI,name), append it
+ to current node, and return the newly created node.
+ Keyword arguments:
+ namespaceURI -- namespace of element to create
+ localName -- local name of new element
+ prefix -- if namespaceURI is not defined, declare prefix. defaults
+ to 'ns1' if left unspecified.
+ '''
+ declare = False
+ qualifiedName = localName
+ if namespaceURI:
+ try:
+ prefix = self.getPrefix(namespaceURI)
+ except:
+ declare = True
+ prefix = prefix or self._getUniquePrefix()
+ if prefix:
+ qualifiedName = '%s:%s' %(prefix, localName)
+ node = self.createElementNS(namespaceURI, qualifiedName)
+ if declare:
+ node._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
+ self._appendChild(node=node._getNode())
+ return node
+
+ def createInsertBefore(self, namespaceURI, localName, refChild):
+ qualifiedName = localName
+ prefix = self.getPrefix(namespaceURI)
+ if prefix:
+ qualifiedName = '%s:%s' %(prefix, localName)
+ node = self.createElementNS(namespaceURI, qualifiedName)
+ self._insertBefore(newChild=node._getNode(), refChild=refChild._getNode())
+ return node
+
+ def getElement(self, namespaceURI, localName):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of element
+ localName -- local name of element
+ '''
+ node = self._dom.getElement(self.node, localName, namespaceURI, default=None)
+ if node:
+ return ElementProxy(self.sw, node)
+ return None
+
+ def getAttributeValue(self, namespaceURI, localName):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of attribute
+ localName -- local name of attribute
+ '''
+ if self.hasAttribute(namespaceURI, localName):
+ attr = self.node.getAttributeNodeNS(namespaceURI,localName)
+ return attr.value
+ return None
+
+ def getValue(self):
+ return self._dom.getElementText(self.node, preserve_ws=True)
+
+ #############################################
+ #Methods for text nodes
+ #############################################
+ def createAppendTextNode(self, pyobj):
+ node = self.createTextNode(pyobj)
+ self._appendChild(node=node._getNode())
+ return node
+
+ def createTextNode(self, pyobj):
+ document = self._getOwnerDocument()
+ node = document.createTextNode(pyobj)
+ return ElementProxy(self.sw, node)
+
+ #############################################
+ #Methods for retrieving namespaceURI's
+ #############################################
+ def findNamespaceURI(self, qualifiedName):
+ parts = SplitQName(qualifiedName)
+ element = self._getNode()
+ if len(parts) == 1:
+ return (self._dom.findTargetNS(element), value)
+ return self._dom.findNamespaceURI(parts[0], element)
+
+ def resolvePrefix(self, prefix):
+ element = self._getNode()
+ return self._dom.findNamespaceURI(prefix, element)
+
+ def getSOAPEnvURI(self):
+ return self._soap_env_nsuri
+
+ def isEmpty(self):
+ return not self.node
+
+
+
+class Collection(UserDict):
+ """Helper class for maintaining ordered named collections."""
+ default = lambda self,k: k.name
+ def __init__(self, parent, key=None):
+ UserDict.__init__(self)
+ self.parent = weakref.ref(parent)
+ self.list = []
+ self._func = key or self.default
+
+ def __getitem__(self, key):
+ if type(key) is type(1):
+ return self.list[key]
+ return self.data[key]
+
+ def __setitem__(self, key, item):
+ item.parent = weakref.ref(self)
+ self.list.append(item)
+ self.data[key] = item
+
+ def keys(self):
+ return map(lambda i: self._func(i), self.list)
+
+ def items(self):
+ return map(lambda i: (self._func(i), i), self.list)
+
+ def values(self):
+ return self.list
+
+
+class CollectionNS(UserDict):
+ """Helper class for maintaining ordered named collections."""
+ default = lambda self,k: k.name
+ def __init__(self, parent, key=None):
+ UserDict.__init__(self)
+ self.parent = weakref.ref(parent)
+ self.targetNamespace = None
+ self.list = []
+ self._func = key or self.default
+
+ def __getitem__(self, key):
+ self.targetNamespace = self.parent().targetNamespace
+ if type(key) is types.IntType:
+ return self.list[key]
+ elif self.__isSequence(key):
+ nsuri,name = key
+ return self.data[nsuri][name]
+ return self.data[self.parent().targetNamespace][key]
+
+ def __setitem__(self, key, item):
+ item.parent = weakref.ref(self)
+ self.list.append(item)
+ targetNamespace = getattr(item, 'targetNamespace', self.parent().targetNamespace)
+ if not self.data.has_key(targetNamespace):
+ self.data[targetNamespace] = {}
+ self.data[targetNamespace][key] = item
+
+ def __isSequence(self, key):
+ return (type(key) in (types.TupleType,types.ListType) and len(key) == 2)
+
+ def keys(self):
+ keys = []
+ for tns in self.data.keys():
+ keys.append(map(lambda i: (tns,self._func(i)), self.data[tns].values()))
+ return keys
+
+ def items(self):
+ return map(lambda i: (self._func(i), i), self.list)
+
+ def values(self):
+ return self.list
+
+
+
+# This is a runtime guerilla patch for pulldom (used by minidom) so
+# that xml namespace declaration attributes are not lost in parsing.
+# We need them to do correct QName linking for XML Schema and WSDL.
+# The patch has been submitted to SF for the next Python version.
+
+from xml.dom.pulldom import PullDOM, START_ELEMENT
+if 1:
+ def startPrefixMapping(self, prefix, uri):
+ if not hasattr(self, '_xmlns_attrs'):
+ self._xmlns_attrs = []
+ self._xmlns_attrs.append((prefix or 'xmlns', uri))
+ self._ns_contexts.append(self._current_context.copy())
+ self._current_context[uri] = prefix or ''
+
+ PullDOM.startPrefixMapping = startPrefixMapping
+
+ def startElementNS(self, name, tagName , attrs):
+ # Retrieve xml namespace declaration attributes.
+ xmlns_uri = 'http://www.w3.org/2000/xmlns/'
+ xmlns_attrs = getattr(self, '_xmlns_attrs', None)
+ if xmlns_attrs is not None:
+ for aname, value in xmlns_attrs:
+ attrs._attrs[(xmlns_uri, aname)] = value
+ self._xmlns_attrs = []
+ 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
+
+#
+# This is a runtime guerilla patch for minidom so
+# that xmlns prefixed attributes dont raise AttributeErrors
+# during cloning.
+#
+# Namespace declarations can appear in any start-tag, must look for xmlns
+# prefixed attribute names during cloning.
+#
+# key (attr.namespaceURI, tag)
+# ('http://www.w3.org/2000/xmlns/', u'xsd') <xml.dom.minidom.Attr instance at 0x82227c4>
+# ('http://www.w3.org/2000/xmlns/', 'xmlns') <xml.dom.minidom.Attr instance at 0x8414b3c>
+#
+# xml.dom.minidom.Attr.nodeName = xmlns:xsd
+# xml.dom.minidom.Attr.value = = http://www.w3.org/2001/XMLSchema
+
+if 1:
+ def _clone_node(node, deep, newOwnerDocument):
+ """
+ Clone a node and give it the new owner document.
+ Called by Node.cloneNode and Document.importNode
+ """
+ if node.ownerDocument.isSameNode(newOwnerDocument):
+ operation = xml.dom.UserDataHandler.NODE_CLONED
+ else:
+ operation = xml.dom.UserDataHandler.NODE_IMPORTED
+ if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
+ clone = newOwnerDocument.createElementNS(node.namespaceURI,
+ node.nodeName)
+ for attr in node.attributes.values():
+ clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value)
+
+ prefix, tag = xml.dom.minidom._nssplit(attr.nodeName)
+ if prefix == 'xmlns':
+ a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
+ elif prefix:
+ a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
+ else:
+ a = clone.getAttributeNodeNS(attr.namespaceURI, attr.nodeName)
+ a.specified = attr.specified
+
+ if deep:
+ for child in node.childNodes:
+ c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
+ clone.appendChild(c)
+ elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_FRAGMENT_NODE:
+ clone = newOwnerDocument.createDocumentFragment()
+ if deep:
+ for child in node.childNodes:
+ c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
+ clone.appendChild(c)
+
+ elif node.nodeType == xml.dom.minidom.Node.TEXT_NODE:
+ clone = newOwnerDocument.createTextNode(node.data)
+ elif node.nodeType == xml.dom.minidom.Node.CDATA_SECTION_NODE:
+ clone = newOwnerDocument.createCDATASection(node.data)
+ elif node.nodeType == xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE:
+ clone = newOwnerDocument.createProcessingInstruction(node.target,
+ node.data)
+ elif node.nodeType == xml.dom.minidom.Node.COMMENT_NODE:
+ clone = newOwnerDocument.createComment(node.data)
+ elif node.nodeType == xml.dom.minidom.Node.ATTRIBUTE_NODE:
+ clone = newOwnerDocument.createAttributeNS(node.namespaceURI,
+ node.nodeName)
+ clone.specified = True
+ clone.value = node.value
+ elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_TYPE_NODE:
+ assert node.ownerDocument is not newOwnerDocument
+ operation = xml.dom.UserDataHandler.NODE_IMPORTED
+ clone = newOwnerDocument.implementation.createDocumentType(
+ node.name, node.publicId, node.systemId)
+ clone.ownerDocument = newOwnerDocument
+ if deep:
+ clone.entities._seq = []
+ clone.notations._seq = []
+ for n in node.notations._seq:
+ notation = xml.dom.minidom.Notation(n.nodeName, n.publicId, n.systemId)
+ notation.ownerDocument = newOwnerDocument
+ clone.notations._seq.append(notation)
+ if hasattr(n, '_call_user_data_handler'):
+ n._call_user_data_handler(operation, n, notation)
+ for e in node.entities._seq:
+ entity = xml.dom.minidom.Entity(e.nodeName, e.publicId, e.systemId,
+ e.notationName)
+ entity.actualEncoding = e.actualEncoding
+ entity.encoding = e.encoding
+ entity.version = e.version
+ entity.ownerDocument = newOwnerDocument
+ clone.entities._seq.append(entity)
+ if hasattr(e, '_call_user_data_handler'):
+ e._call_user_data_handler(operation, n, entity)
+ else:
+ # Note the cloning of Document and DocumentType nodes is
+ # implemenetation specific. minidom handles those cases
+ # directly in the cloneNode() methods.
+ raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node))
+
+ # Check for _call_user_data_handler() since this could conceivably
+ # used with other DOM implementations (one of the FourThought
+ # DOMs, perhaps?).
+ if hasattr(node, '_call_user_data_handler'):
+ node._call_user_data_handler(operation, node, clone)
+ return clone
+
+ xml.dom.minidom._clone_node = _clone_node
+
View
1,668 wstools/WSDLTools.py
@@ -0,0 +1,1668 @@
+# 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.
+
+ident = "$Id$"
+
+import weakref
+from cStringIO import StringIO
+from Namespaces import OASIS, XMLNS, WSA, WSA_LIST, WSAW_LIST, WSRF_V1_2, WSRF
+from Utility import Collection, CollectionNS, DOM, ElementProxy, basejoin
+from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter
+
+
+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, stream, name=None):
+ """Return a WSDL instance loaded from a stream object."""
+ document = DOM.loadDocument(stream)
+ wsdl = WSDL()
+ if name:
+ wsdl.location = name
+ elif hasattr(stream, 'name'):
+ wsdl.location = stream.name
+ wsdl.load(document)
+ return wsdl
+
+ def loadFromURL(self, url):
+ """Return a WSDL instance loaded from the given url."""
+ document = DOM.loadFromURL(url)
+ wsdl = WSDL()
+ wsdl.location = url
+ wsdl.load(document)
+ return wsdl
+
+ def loadFromString(self, data):
+ """Return a WSDL instance loaded from an xml string."""
+ return self.loadFromStream(StringIO(data))
+
+ def loadFromFile(self, filename):
+ """Return a WSDL instance loaded from the given file."""
+ file = open(filename, 'rb')
+ try:
+ wsdl = self.loadFromStream(file)
+ finally:
+ file.close()
+ return wsdl
+
+class WSDL:
+ """A WSDL object models a WSDL service description. WSDL objects
+ may be created manually or loaded from an xml representation
+ using a WSDLReader instance."""
+
+ def __init__(self, targetNamespace=None, strict=1):
+ self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
+ self.documentation = ''
+ self.location = None
+ self.document = None
+ self.name = None
+ self.services = CollectionNS(self)
+ self.messages = CollectionNS(self)
+ self.portTypes = CollectionNS(self)
+ self.bindings = CollectionNS(self)
+ self.imports = Collection(self)
+ self.types = Types(self)
+ self.extensions = []
+ self.strict = strict
+
+ def __del__(self):
+ if self.document is not None:
+ self.document.unlink()
+
+ version = '1.1'
+
+ def addService(self, name, documentation='', targetNamespace=None):
+ if self.services.has_key(name):
+ raise WSDLError(
+ 'Duplicate service element: %s' % name
+ )
+ item = Service(name, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.services[name] = item
+ return item
+
+ def addMessage(self, name, documentation='', targetNamespace=None):
+ if self.messages.has_key(name):
+ raise WSDLError(
+ 'Duplicate message element: %s.' % name
+ )
+ item = Message(name, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.messages[name] = item
+ return item
+
+ def addPortType(self, name, documentation='', targetNamespace=None):
+ if self.portTypes.has_key(name):
+ raise WSDLError(
+ 'Duplicate portType element: name'
+ )
+ item = PortType(name, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.portTypes[name] = item
+ return item
+
+ def addBinding(self, name, type, documentation='', targetNamespace=None):
+ if self.bindings.has_key(name):
+ raise WSDLError(
+ 'Duplicate binding element: %s' % name
+ )
+ item = Binding(name, type, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.bindings[name] = item
+ return item
+
+ def addImport(self, namespace, location):
+ item = ImportElement(namespace, location)
+ self.imports[namespace] = item
+ return item
+
+ def toDom(self):
+ """ Generate a DOM representation of the WSDL instance.
+ Not dealing with generating XML Schema, thus the targetNamespace
+ of all XML Schema elements or types used by WSDL message parts
+ needs to be specified via import information items.
+ """
+ namespaceURI = DOM.GetWSDLUri(self.version)
+ self.document = DOM.createDocument(namespaceURI ,'wsdl:definitions')
+
+ # Set up a couple prefixes for easy reading.
+ child = DOM.getElement(self.document, None)
+ child.setAttributeNS(None, 'targetNamespace', self.targetNamespace)
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:wsdl', namespaceURI)
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:xsd', 'http://www.w3.org/1999/XMLSchema')
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:soap', 'http://schemas.xmlsoap.org/wsdl/soap/')
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:tns', self.targetNamespace)
+
+ if self.name:
+ child.setAttributeNS(None, 'name', self.name)
+
+ # wsdl:import
+ for item in self.imports:
+ item.toDom()
+ # wsdl:message
+ for item in self.messages:
+ item.toDom()
+ # wsdl:portType
+ for item in self.portTypes:
+ item.toDom()
+ # wsdl:binding
+ for item in self.bindings:
+ item.toDom()
+ # wsdl:service
+ for item in self.services:
+ item.toDom()
+
+ def load(self, document):
+ # We save a reference to the DOM document to ensure that elements
+ # saved as "extensions" will continue to have a meaningful context
+ # for things like namespace references. The lifetime of the DOM
+ # document is bound to the lifetime of the WSDL instance.
+ self.document = document
+
+ definitions = DOM.getElement(document, 'definitions', None, None)
+ if definitions is None:
+ raise WSDLError(
+ 'Missing <definitions> element.'
+ )
+ self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
+ NS_WSDL = DOM.GetWSDLUri(self.version)
+
+ self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
+ None, None)
+ self.name = DOM.getAttr(definitions, 'name', None, None)
+ self.documentation = GetDocumentation(definitions)
+
+ #
+ # Retrieve all <wsdl:import>'s, append all children of imported
+ # document to main document. First iteration grab all original
+ # <wsdl:import>'s from document, second iteration grab all
+ # "imported" <wsdl:imports> from document, etc break out when
+ # no more <wsdl:import>'s.
+ #
+ imported = []
+ base_location = self.location
+ do_it = True
+ while do_it:
+ do_it = False
+ for element in DOM.getElements(definitions, 'import', NS_WSDL):
+ location = DOM.getAttr(element, 'location')
+
+ if base_location is not None:
+ location = basejoin(base_location, location)
+
+ if location not in imported:
+ do_it = True
+ self._import(document, element, base_location)
+ imported.append(location)
+ else:
+ definitions.removeChild(element)
+
+ base_location = None
+
+ #
+ # No more <wsdl:import>'s, now load up all other
+ # WSDL information items.
+ #
+ for element in DOM.getElements(definitions, None, None):
+ targetNamespace = DOM.getAttr(element, 'targetNamespace')
+ localName = element.localName
+
+ if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
+ if localName == 'schema':
+ tns = DOM.getAttr(element, 'targetNamespace')
+ reader = SchemaReader(base_url=self.imports[tns].location)
+ schema = reader.loadFromNode(WSDLToolsAdapter(self),
+ element)
+# schema.setBaseUrl(self.location)
+ self.types.addSchema(schema)
+ else:
+ self.extensions.append(element)
+ continue
+
+ elif localName == 'message':
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ message = self.addMessage(name, docs, targetNamespace)
+ 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, targetNamespace)
+ #operations = DOM.getElements(element, 'operation', NS_WSDL)
+ #ptype.load(operations)
+ ptype.load(element)
+ 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 = ParseQName(type, element)
+ docs = GetDocumentation(element)
+ binding = self.addBinding(name, type, docs, targetNamespace)
+ 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, targetNamespace)
+ ports = DOM.getElements(element, 'port', NS_WSDL)
+ service.load(ports)
+ service.load_ex(GetExtensions(element))
+ continue
+
+ elif localName == 'types':
+ self.types.documentation = GetDocumentation(element)
+ base_location = DOM.getAttr(element, 'base-location')
+ if base_location:
+ element.removeAttribute('base-location')
+ base_location = base_location or self.location
+ reader = SchemaReader(base_url=base_location)
+ for item in DOM.getElements(element, None, None):
+ if item.localName == 'schema':
+ schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
+ # XXX <types> could have been imported
+ #schema.setBaseUrl(self.location)
+ schema.setBaseUrl(base_location)
+ self.types.addSchema(schema)
+ else:
+ self.types.addExtension(item)
+ # XXX remove the attribute
+ # element.removeAttribute('base-location')
+ continue
+
+ def _import(self, document, element, base_location=None):
+ '''Algo take <import> element's children, clone them,
+ and add them to the main document. Support for relative
+ locations is a bit complicated. The orig document context
+ is lost, so we need to store base location in DOM elements
+ representing <types>, by creating a special temporary
+ "base-location" attribute, and <import>, by resolving
+ the relative "location" and storing it as "location".
+
+ document -- document we are loading
+ element -- DOM Element representing <import>
+ base_location -- location of document from which this
+ <import> was gleaned.
+ '''
+ 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).'
+ )
+ if base_location:
+ location = basejoin(base_location, location)
+ element.setAttributeNS(None, '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.findTargetNS(imported)
+ if imported_tns != namespace:
+ return
+
+ if imported.localName == 'definitions':
+ imported_nodes = imported.childNodes
+ else:
+ imported_nodes = [imported]
+ parent = element.parentNode
+
+ parent.removeChild(element)
+
+ for node in imported_nodes:
+ if node.nodeType != node.ELEMENT_NODE:
+ continue
+ child = DOM.importNode(document, node, 1)
+ parent.appendChild(child)
+ child.setAttribute('targetNamespace', namespace)
+ attrsNS = imported._attrsNS
+ for attrkey in attrsNS.keys():
+ if attrkey[0] == DOM.NS_XMLNS:
+ attr = attrsNS[attrkey].cloneNode(