@@ -1,6 +1,14 @@ | |||||
CHANGELOG | CHANGELOG | ||||
===================== | ===================== | ||||
0.52.23 (unreleased) | |||||
-------------------- | |||||
- Port to Python 3.5 | |||||
- Added TwistedSOAPPublisher and Proxy | |||||
These are located in twisted.web.soap, but thats only for Python 2. | |||||
0.12.23 (unreleased) | 0.12.23 (unreleased) | ||||
-------------------- | -------------------- | ||||
@@ -1 +0,0 @@ | |||||
*.pyc |
@@ -1 +0,0 @@ | |||||
*.pyc |
@@ -2,33 +2,36 @@ | |||||
# | # | ||||
# $Id: setup.py,v 1.11 2005/02/15 16:32:22 warnes Exp $ | # $Id: setup.py,v 1.11 2005/02/15 16:32:22 warnes Exp $ | ||||
CVS=0 | |||||
CVS = 0 | |||||
from setuptools import setup, find_packages | |||||
import os | import os | ||||
from setuptools import setup, find_packages | |||||
def read(*rnames): | def read(*rnames): | ||||
return "\n"+ open( | |||||
return "\n" + open( | |||||
os.path.join('.', *rnames) | os.path.join('.', *rnames) | ||||
).read() | ).read() | ||||
url="https://github.com/kiorky/SOAPpy.git" | |||||
long_description="SOAPpy provides tools for building SOAP clients and servers. For more information see " + url\ | |||||
+'\n'+read('README.txt')\ | |||||
+'\n'+read('CHANGES.txt') | |||||
url = "https://github.com/Synerty/SOAPpy-py3" | |||||
long_description = "SOAPpy-py3 provides tools for building SOAP clients and servers. For more information see " + url \ | |||||
+ '\n' + read('README.txt') \ | |||||
+ '\n' + read('CHANGES.txt') | |||||
setup( | setup( | ||||
name="SOAPpy", | |||||
version='0.12.23.dev0', | |||||
name="SOAPpy-py3", | |||||
version='0.52.23', # Add 0.40.0 for the SOAPpy-py3 port | |||||
description="SOAP Services for Python", | description="SOAP Services for Python", | ||||
maintainer="Gregory Warnes, kiorky", | |||||
maintainer_email="Gregory.R.Warnes@Pfizer.com, kiorky@cryptelium.net", | |||||
url = url, | |||||
maintainer="Synerty", | |||||
maintainer_email="contact@synerty.com", | |||||
url=url, | |||||
long_description=long_description, | long_description=long_description, | ||||
packages=find_packages('src'), | packages=find_packages('src'), | ||||
package_dir = {'': 'src'}, | |||||
package_dir={'': 'src'}, | |||||
include_package_data=True, | include_package_data=True, | ||||
install_requires=[ | install_requires=[ | ||||
'wstools', | 'wstools', | ||||
'defusedxml', | 'defusedxml', | ||||
] | ] | ||||
) | ) | ||||
@@ -55,7 +55,7 @@ import socket, http.client | |||||
from http.client import HTTPConnection | from http.client import HTTPConnection | ||||
import http.cookies | import http.cookies | ||||
# SOAPpy modules | |||||
# SOAPpy-py3 modules | |||||
from .Errors import * | from .Errors import * | ||||
from .Config import Config | from .Config import Config | ||||
from .Parser import parseSOAPRPC | from .Parser import parseSOAPRPC | ||||
@@ -71,7 +71,7 @@ import collections | |||||
def SOAPUserAgent(): | def SOAPUserAgent(): | ||||
return "SOAPpy " + __version__ + " (pywebsvcs.sf.net)" | |||||
return "SOAPpy-py3 " + __version__ + " (pywebsvcs.sf.net)" | |||||
class HTTP: | class HTTP: | ||||
@@ -137,7 +137,7 @@ class HTTP: | |||||
#only add this keyword if non-default for compatibility | #only add this keyword if non-default for compatibility | ||||
#with other connection classes | #with other connection classes | ||||
response = self._conn.getresponse(buffering) | response = self._conn.getresponse(buffering) | ||||
except BadStatusLine as e: | |||||
except http.BadStatusLine as e: | |||||
### hmm. if getresponse() ever closes the socket on a bad request, | ### hmm. if getresponse() ever closes the socket on a bad request, | ||||
### then we are going to have problems with self.sock | ### then we are going to have problems with self.sock | ||||
@@ -329,7 +329,7 @@ class HTTPTransport: | |||||
debugHeader(s) | debugHeader(s) | ||||
print("POST %s %s" % (real_path, r._http_vsn_str)) | print("POST %s %s" % (real_path, r._http_vsn_str)) | ||||
print("Host:", addr.host) | print("Host:", addr.host) | ||||
print("User-agent: SOAPpy " + __version__ + " (http://pywebsvcs.sf.net)") | |||||
print("User-agent: SOAPpy-py3 " + __version__ + " (http://pywebsvcs.sf.net)") | |||||
print("Content-type:", t) | print("Content-type:", t) | ||||
print("Content-length:", len(data)) | print("Content-length:", len(data)) | ||||
print('SOAPAction: "%s"' % soapaction) | print('SOAPAction: "%s"' % soapaction) | ||||
@@ -56,7 +56,7 @@ import socketserver | |||||
from SOAPpy.Types import * | from SOAPpy.Types import * | ||||
import http.server | import http.server | ||||
# SOAPpy modules | |||||
# SOAPpy-py3 modules | |||||
from .Parser import parseSOAPRPC | from .Parser import parseSOAPRPC | ||||
from .Config import SOAPConfig | from .Config import SOAPConfig | ||||
from .Types import faultType, voidType, simplify | from .Types import faultType, voidType, simplify | ||||
@@ -40,7 +40,7 @@ | |||||
################################################################################ | ################################################################################ | ||||
""" | """ | ||||
from SOAPpy.Errors import Error | |||||
ident = '$Id: NS.py 1468 2008-05-24 01:55:33Z warnes $' | ident = '$Id: NS.py 1468 2008-05-24 01:55:33Z warnes $' | ||||
from .version import __version__ | from .version import __version__ | ||||
@@ -298,7 +298,7 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||||
if null: | if null: | ||||
if len(cur) or \ | if len(cur) or \ | ||||
(self._data != None and string.join(self._data, "").strip() != ''): | |||||
(self._data != None and ''.join(self._data).strip() != ''): | |||||
raise Error("nils can't have data") | raise Error("nils can't have data") | ||||
data = None | data = None | ||||
@@ -943,7 +943,7 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||||
raise OverflowError("%s too large: %s" % (t[1], s)) | raise OverflowError("%s too large: %s" % (t[1], s)) | ||||
elif d == 0: | elif d == 0: | ||||
if type(self.zerofloatre) == StringType: | if type(self.zerofloatre) == StringType: | ||||
self.zerofloatre = re.compile(self.zerofloatre) | |||||
self.zerofloatre = re.compile(str(self.zerofloatre)) | |||||
if self.zerofloatre.search(s): | if self.zerofloatre.search(s): | ||||
raise UnderflowError("invalid %s: %s" % (t[1], s)) | raise UnderflowError("invalid %s: %s" % (t[1], s)) | ||||
@@ -22,17 +22,17 @@ from warnings import warn | |||||
warn(""" | warn(""" | ||||
The sub-module SOAPpy.SOAP is deprecated and is only | |||||
The sub-module SOAPpy-py3.SOAP is deprecated and is only | |||||
provided for short-term backward compatibility. Objects are now | provided for short-term backward compatibility. Objects are now | ||||
available directly within the SOAPpy module. Thus, instead of | |||||
available directly within the SOAPpy-py3 module. Thus, instead of | |||||
from SOAPpy import SOAP | |||||
from SOAPpy-py3 import SOAP | |||||
... | ... | ||||
SOAP.SOAPProxy(...) | SOAP.SOAPProxy(...) | ||||
use | use | ||||
from SOAPpy import SOAPProxy | |||||
from SOAPpy-py3 import SOAPProxy | |||||
... | ... | ||||
SOAPProxy(...) | SOAPProxy(...) | ||||
@@ -39,7 +39,7 @@ from .version import __version__ | |||||
import cgi | import cgi | ||||
from wstools.XMLname import toXMLname, fromXMLname | from wstools.XMLname import toXMLname, fromXMLname | ||||
# SOAPpy modules | |||||
# SOAPpy-py3 modules | |||||
from .Config import Config | from .Config import Config | ||||
from .NS import NS | from .NS import NS | ||||
from SOAPpy.Types import * | from SOAPpy.Types import * | ||||
@@ -53,7 +53,7 @@ from SOAPpy.Types import * | |||||
import http.server | import http.server | ||||
import _thread | import _thread | ||||
# SOAPpy modules | |||||
# SOAPpy-py3 modules | |||||
from .Parser import parseSOAPRPC | from .Parser import parseSOAPRPC | ||||
from .Config import Config | from .Config import Config | ||||
from SOAPpy.Types import faultType, voidType, simplify | from SOAPpy.Types import faultType, voidType, simplify | ||||
@@ -194,7 +194,7 @@ class SOAPRequestHandler(http.server.BaseHTTPRequestHandler): | |||||
ignore_ext = True | ignore_ext = True | ||||
def version_string(self): | def version_string(self): | ||||
return '<a href="http://pywebsvcs.sf.net">' + \ | return '<a href="http://pywebsvcs.sf.net">' + \ | ||||
'SOAPpy ' + __version__ + '</a> (Python ' + \ | |||||
'SOAPpy-py3 ' + __version__ + '</a> (Python ' + \ | |||||
sys.version.split()[0] + ')' | sys.version.split()[0] + ')' | ||||
def date_time_string(self): | def date_time_string(self): | ||||
@@ -0,0 +1,41 @@ | |||||
# twisted imports | |||||
from twisted.web import client | |||||
import SOAPpy | |||||
class TwistedSOAPProxy: | |||||
"""A Proxy for making remote SOAP calls. | |||||
Pass the URL of the remote SOAP server to the constructor. | |||||
Use proxy.callRemote('foobar', 1, 2) to call remote method | |||||
'foobar' with args 1 and 2, proxy.callRemote('foobar', x=1) | |||||
will call foobar with named argument 'x'. | |||||
""" | |||||
# at some point this should have encoding etc. kwargs | |||||
def __init__(self, url, namespace=None, header=None): | |||||
self.url = url | |||||
self.namespace = namespace | |||||
self.header = header | |||||
def _cbGotResult(self, result): | |||||
result = SOAPpy.parseSOAPRPC(result) | |||||
if hasattr(result, 'Result'): | |||||
return result.Result | |||||
elif len(result) == 1: | |||||
## SOAPpy 0.11.6 wraps the return results in a containing structure. | |||||
## This check added to make Proxy behaviour emulate SOAPProxy, which | |||||
## flattens the structure by default. | |||||
## This behaviour is OK because even singleton lists are wrapped in | |||||
## another singleton structType, which is almost always useless. | |||||
return result[0] | |||||
else: | |||||
return result | |||||
def callRemote(self, method: str, *args, **kwargs): | |||||
payload = SOAPpy.buildSOAP(args=args, kw=kwargs, method=method, | |||||
header=self.header, namespace=self.namespace) | |||||
return client.getPage(self.url.encode, postdata=payload, method=b"POST", | |||||
headers={b'content-type': b'text/xml', | |||||
b'SOAPAction': method.encode()} | |||||
).addCallback(self._cbGotResult) |
@@ -0,0 +1,98 @@ | |||||
# twisted imports | |||||
import logging | |||||
from twisted.internet import defer | |||||
from twisted.web import server, resource | |||||
import SOAPpy | |||||
class TwistedSOAPPublisher(resource.Resource): | |||||
"""Publish SOAP methods. | |||||
By default, publish methods beginning with 'soap_'. If the method | |||||
has an attribute 'useKeywords', it well get the arguments passed | |||||
as keyword args. | |||||
""" | |||||
isLeaf = 1 | |||||
# override to change the encoding used for responses | |||||
encoding = "UTF-8" | |||||
def lookupFunction(self, functionName): | |||||
"""Lookup published SOAP function. | |||||
Override in subclasses. Default behaviour - publish methods | |||||
starting with soap_. | |||||
@return: callable or None if not found. | |||||
""" | |||||
return getattr(self, "soap_%s" % functionName, None) | |||||
def render(self, request): | |||||
"""Handle a SOAP command.""" | |||||
data = request.content.read() | |||||
# Convert it to str from bytes | |||||
data = data.decode() | |||||
p, header, body, attrs = SOAPpy.parseSOAPRPC(data, 1, 1, 1) | |||||
methodName, args, kwargs = p._name, p._aslist, p._asdict | |||||
# deal with changes in SOAPpy 0.11 | |||||
if callable(args): | |||||
args = args() | |||||
if callable(kwargs): | |||||
kwargs = kwargs() | |||||
function = self.lookupFunction(methodName) | |||||
if not function: | |||||
self._methodNotFound(request, methodName) | |||||
return server.NOT_DONE_YET | |||||
else: | |||||
if hasattr(function, "useKeywords"): | |||||
keywords = {} | |||||
for k, v in kwargs.items(): | |||||
keywords[str(k)] = v | |||||
d = defer.maybeDeferred(function, **keywords) | |||||
else: | |||||
d = defer.maybeDeferred(function, *args) | |||||
d.addCallback(self._gotResult, request, methodName) | |||||
d.addErrback(self._gotError, request, methodName) | |||||
return server.NOT_DONE_YET | |||||
def _methodNotFound(self, request, methodName): | |||||
response = SOAPpy.buildSOAP(SOAPpy.faultType("%s:Client" % | |||||
SOAPpy.NS.ENV_T, | |||||
"Method %s not found" % methodName), | |||||
encoding=self.encoding) | |||||
self._sendResponse(request, response, status=500) | |||||
def _gotResult(self, result, request, methodName): | |||||
if not isinstance(result, SOAPpy.voidType): | |||||
result = {"Result": result} | |||||
response = SOAPpy.buildSOAP(kw={'%sResponse' % methodName: result}, | |||||
encoding=self.encoding) | |||||
self._sendResponse(request, response) | |||||
def _gotError(self, failure, request, methodName): | |||||
e = failure.value | |||||
if isinstance(e, SOAPpy.faultType): | |||||
fault = e | |||||
else: | |||||
fault = SOAPpy.faultType("%s:Server" % SOAPpy.NS.ENV_T, | |||||
"Method %s failed." % methodName) | |||||
response = SOAPpy.buildSOAP(fault, encoding=self.encoding) | |||||
self._sendResponse(request, response, status=500) | |||||
def _sendResponse(self, request, response, status=200): | |||||
request.setResponseCode(status) | |||||
if self.encoding is not None: | |||||
mimeType = 'text/xml; charset="%s"' % self.encoding | |||||
else: | |||||
mimeType = "text/xml" | |||||
request.setHeader(b"Content-type", mimeType.encode()) | |||||
request.setHeader(b"Content-length", str(len(response)).encode()) | |||||
request.write(response) | |||||
request.finish() |
@@ -34,6 +34,18 @@ | |||||
""" | """ | ||||
## PYTHON3 - These types are no longer present in the builtin "types" module | |||||
StringType = str | |||||
UnicodeType = str | |||||
ListType = list | |||||
DictType = dict | |||||
IntType = int | |||||
LongType = int | |||||
FloatType = float | |||||
BooleanType = bool | |||||
TupleType = tuple | |||||
InstanceType = object | |||||
ident = '$Id: Types.py 1496 2010-03-04 23:46:17Z pooryorick $' | ident = '$Id: Types.py 1496 2010-03-04 23:46:17Z pooryorick $' | ||||
from .version import __version__ | from .version import __version__ | ||||
@@ -47,7 +59,7 @@ import re | |||||
import time | import time | ||||
from SOAPpy.Types import * | from SOAPpy.Types import * | ||||
# SOAPpy modules | |||||
# SOAPpy-py3 modules | |||||
from .Errors import * | from .Errors import * | ||||
from .NS import NS | from .NS import NS | ||||
from .Utilities import encodeHexString, cleanDate | from .Utilities import encodeHexString, cleanDate | ||||
@@ -1110,7 +1122,7 @@ class longType(anyType): | |||||
return data | return data | ||||
class int(anyType): | |||||
class intType(anyType): | |||||
_validURIs = (NS.XSD2, NS.XSD3, NS.ENC) | _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) | ||||
def _checkValueSpace(self, data): | def _checkValueSpace(self, data): | ||||
@@ -1611,7 +1623,7 @@ class faultType(structType, Error): | |||||
class SOAPException(Exception): | class SOAPException(Exception): | ||||
def __init__(self, code="", string="", detail=None): | def __init__(self, code="", string="", detail=None): | ||||
self.value = ("SOAPpy SOAP Exception", code, string, detail) | |||||
self.value = ("SOAPpy-py3 SOAP Exception", code, string, detail) | |||||
self.code = code | self.code = code | ||||
self.string = string | self.string = string | ||||
self.detail = detail | self.detail = detail | ||||
@@ -1650,12 +1662,12 @@ class MethodFailed(Exception): | |||||
return repr(self.value) | return repr(self.value) | ||||
####### | ####### | ||||
# Convert complex SOAPpy objects to native python equivalents | |||||
# Convert complex SOAPpy-py3 objects to native python equivalents | |||||
####### | ####### | ||||
def simplify(object, level=0): | def simplify(object, level=0): | ||||
""" | """ | ||||
Convert the SOAPpy objects and their contents to simple python types. | |||||
Convert the SOAPpy-py3 objects and their contents to simple python types. | |||||
This function recursively converts the passed 'container' object, | This function recursively converts the passed 'container' object, | ||||
and all public subobjects. (Private subobjects have names that | and all public subobjects. (Private subobjects have names that | ||||
@@ -1709,7 +1721,7 @@ def simplify(object, level=0): | |||||
def simplify_contents(object, level=0): | def simplify_contents(object, level=0): | ||||
""" | """ | ||||
Convert the contents of SOAPpy objects to simple python types. | |||||
Convert the contents of SOAPpy-py3 objects to simple python types. | |||||
This function recursively converts the sub-objects contained in a | This function recursively converts the sub-objects contained in a | ||||
'container' object to simple python types. | 'container' object to simple python types. | ||||
@@ -41,7 +41,7 @@ import string | |||||
import sys | import sys | ||||
from SOAPpy.Types import * | from SOAPpy.Types import * | ||||
# SOAPpy modules | |||||
# SOAPpy-py3 modules | |||||
from .Errors import * | from .Errors import * | ||||
################################################################################ | ################################################################################ | ||||
@@ -1,5 +1,5 @@ | |||||
try: | try: | ||||
import pkg_resources | import pkg_resources | ||||
__version__ = pkg_resources.get_distribution("SOAPpy").version | |||||
__version__ = pkg_resources.get_distribution("SOAPpy-py3").version | |||||
except: | except: | ||||
__version__="xxx" | __version__="xxx" |
@@ -3750,7 +3750,7 @@ if __name__ == '__main__': | |||||
NOTE: The 'testArray' test will fail because 'referenced' elements are | NOTE: The 'testArray' test will fail because 'referenced' elements are | ||||
included in the return object. This is a known shortcoming of | included in the return object. This is a known shortcoming of | ||||
the current version of SOAPpy. | |||||
the current version of SOAPpy-py3. | |||||
All other tests should succeed. | All other tests should succeed. | ||||
@@ -6,7 +6,7 @@ ident = '$Id: alanbushTest.py,v 1.5 2003/05/21 14:52:37 warnes Exp $' | |||||
import os, re,sys | import os, re,sys | ||||
# add local SOAPpy code to search path | |||||
# add local SOAPpy-py3 code to search path | |||||
sys.path.insert(1, "..") | sys.path.insert(1, "..") | ||||
from SOAPpy import * | from SOAPpy import * | ||||
@@ -16,7 +16,7 @@ except: | |||||
PROXY="http://www.soapware.org/xmlStorageSystem" | PROXY="http://www.soapware.org/xmlStorageSystem" | ||||
EMAIL="SOAPpy@actzero.com" | |||||
EMAIL="SOAPpy-py3@actzero.com" | |||||
NAME="test_user" | NAME="test_user" | ||||
PASSWORD="mypasswd" | PASSWORD="mypasswd" | ||||
SERIAL=1123214 | SERIAL=1123214 | ||||
@@ -7,7 +7,7 @@ import sys | |||||
sys.path.insert(1, "..") | sys.path.insert(1, "..") | ||||
import SOAPpy | import SOAPpy | ||||
#SOAPpy.Config.debug=1 | |||||
#SOAPpy-py3.Config.debug=1 | |||||
# global to shut down server | # global to shut down server | ||||
quit = 0 | quit = 0 | ||||
@@ -107,7 +107,7 @@ class ClientTestCase(unittest.TestCase): | |||||
# gc.set_debug(gc.DEBUG_SAVEALL) | # gc.set_debug(gc.DEBUG_SAVEALL) | ||||
# for i in range(400): | # for i in range(400): | ||||
# server = SOAPpy.Client.SOAPProxy('127.0.0.1:8000') | |||||
# server = SOAPpy-py3.Client.SOAPProxy('127.0.0.1:8000') | |||||
# s = 'Hello World' | # s = 'Hello World' | ||||
# server.echo(s) | # server.echo(s) | ||||
# gc.collect() | # gc.collect() | ||||
@@ -1 +0,0 @@ | |||||
*.pyc |
@@ -1 +0,0 @@ | |||||
*.pyc |