Repository: twisted/twisted

Issue:
Add a withRequest decorator for SOAP methods
|<img alt="wam's avatar" src="https://avatars.githubusercontent.com/u/0?s=50" width="50" height="50">| wam reported|
|-|-|
|Trac ID|trac#4729|
|Type|enhancement|
|Created|2010-11-05 12:56:26Z|

As stated in [#3073](https://github.com/twisted/twisted/issues/3073) it's very useful to have the request inside a soap method. So here is the patch for twisted/web/soap.py.

Attachments:

* [withRequest-soap.patch](https://raw.githubusercontent.com/twisted/twistedmatrix.com-trac-attachments/trunk/ticket/541/541f66976e0f98189884f38b6f907def43c6c2cf/0dfb020335f9b5e14fe05a9c7c28d36ff388678f.patch) (2698 bytes) - added by wam on 2010-11-05 12:56:35Z - 

<details><summary>Searchable metadata</summary>

```
trac-id__4729 4729
type__enhancement enhancement
reporter__wam wam
priority__normal normal
milestone__ 
branch__ 
branch_author__ 
status__new new
resolution__None None
component__web web
keywords__ 
time__1288961786000000 1288961786000000
changetime__1289231181000000 1289231181000000
version__None None
owner__jknight jknight
cc__wam
```
</details>



Discussion:
|<img alt="wam's avatar" src="https://avatars.githubusercontent.com/u/0?s=50" width="50" height="50"><a name="note_1"></a>|wam commented|
|-|-|

The patch will be fixed in the next few days according to the guidelines in [#3073](https://github.com/twisted/twisted/issues/3073).

Resolving patch:
diff --git a/docs/web/examples/soap.py b/docs/web/examples/soap.py
deleted file mode 100644
--- a/docs/web/examples/soap.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (c) Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-"""
-This is an example of a simple SOAP server.
-
-Usage:
-    $ python soap.py
-
-An example session (assuming the server is running):
-
-   >>> import SOAPpy
-   >>> p = SOAPpy.SOAPProxy('http://localhost:8080/')
-   >>> p.add(a=1)
-   1
-   >>> p.add(a=1, b=3)
-   4
-   >>> p.echo("Hello World")
-   'Hello World'
-
-"""
-
-from twisted.internet import defer, reactor
-from twisted.web import server, soap
-
-
-class Example(soap.SOAPPublisher):
-    """
-    It publishs two methods, 'add' and 'echo'.
-    """
-
-    def soap_echo(self, x):
-        return x
-
-    def soap_add(self, a=0, b=0):
-        return a + b
-
-    soap_add.useKeywords = 1
-
-    def soap_deferred(self):
-        return defer.succeed(2)
-
-
-reactor.listenTCP(8080, server.Site(Example()))
-reactor.run()
diff --git a/docs/web/howto/listings/xmlAndSoapQuote.py b/docs/web/howto/listings/xmlAndSoapQuote.py
deleted file mode 100644
--- a/docs/web/howto/listings/xmlAndSoapQuote.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import os
-
-from twisted.internet import endpoints
-from twisted.web import resource, server, soap, xmlrpc
-
-
-def getQuote():
-    return "Victory to the burgeois, you capitalist swine!"
-
-
-class XMLRPCQuoter(xmlrpc.XMLRPC):
-    def xmlrpc_quote(self):
-        return getQuote()
-
-
-class SOAPQuoter(soap.SOAPPublisher):
-    def soap_quote(self):
-        return getQuote()
-
-
-def main():
-    from twisted.internet import reactor
-
-    root = resource.Resource()
-    root.putChild("RPC2", XMLRPCQuoter())
-    root.putChild("SOAP", SOAPQuoter())
-    endpoint = endpoints.TCP4ServerEndpoint(reactor, 7080)
-    endpoint.listen(server.Site(root))
-    reactor.run()
-
-
-if __name__ == "__main__":
-    main()
diff --git a/src/twisted/web/soap.py b/src/twisted/web/soap.py
deleted file mode 100644
--- a/src/twisted/web/soap.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# -*- test-case-name: twisted.web.test.test_soap -*-
-# Copyright (c) Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-
-"""
-SOAP support for twisted.web.
-
-Requires SOAPpy 0.10.1 or later.
-
-Maintainer: Itamar Shtull-Trauring
-
-Future plans:
-SOAPContext support of some kind.
-Pluggable method lookup policies.
-"""
-
-# SOAPpy
-import SOAPpy
-
-from twisted.internet import defer
-
-# twisted imports
-from twisted.web import client, resource, server
-
-
-class SOAPPublisher(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()
-
-        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("Content-type", mimeType)
-        request.setHeader("Content-length", str(len(response)))
-        request.write(response)
-        request.finish()
-
-
-class Proxy:
-    """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, *args, **kwargs):
-        payload = SOAPpy.buildSOAP(
-            args=args,
-            kw=kwargs,
-            method=method,
-            header=self.header,
-            namespace=self.namespace,
-        )
-        return client.getPage(
-            self.url,
-            postdata=payload,
-            method="POST",
-            headers={"content-type": "text/xml", "SOAPAction": method},
-        ).addCallback(self._cbGotResult)


Test patch:
diff --git a/src/twisted/web/test/test_soap.py b/src/twisted/web/test/test_soap.py
deleted file mode 100644
--- a/src/twisted/web/test/test_soap.py
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# Copyright (c) Twisted Matrix Laboratories.
-# See LICENSE for details.
-#
-
-"""Test SOAP support."""
-
-
-from unittest import skipIf
-
-from twisted.internet import defer, reactor
-from twisted.trial.unittest import TestCase
-from twisted.web import error, server
-
-try:
-    import SOAPpy
-
-    from twisted.web import soap
-    from twisted.web.soap import SOAPPublisher
-except ImportError:
-    SOAPpy = None
-    SOAPPublisher = object  # type: ignore[misc,assignment]
-
-
-class Test(SOAPPublisher):
-    def soap_add(self, a, b):
-        return a + b
-
-    def soap_kwargs(self, a=1, b=2):
-        return a + b
-
-    soap_kwargs.useKeywords = True  # type: ignore[attr-defined]
-
-    def soap_triple(self, string, num):
-        return [string, num, None]
-
-    def soap_struct(self):
-        return SOAPpy.structType({"a": "c"})
-
-    def soap_defer(self, x):
-        return defer.succeed(x)
-
-    def soap_deferFail(self):
-        return defer.fail(ValueError())
-
-    def soap_fail(self):
-        raise RuntimeError
-
-    def soap_deferFault(self):
-        return defer.fail(ValueError())
-
-    def soap_complex(self):
-        return {"a": ["b", "c", 12, []], "D": "foo"}
-
-    def soap_dict(self, map, key):
-        return map[key]
-
-
-@skipIf(not SOAPpy, "SOAPpy not installed")
-class SOAPTests(TestCase):
-    def setUp(self):
-        self.publisher = Test()
-        self.p = reactor.listenTCP(
-            0, server.Site(self.publisher), interface="127.0.0.1"
-        )
-        self.port = self.p.getHost().port
-
-    def tearDown(self):
-        return self.p.stopListening()
-
-    def proxy(self):
-        return soap.Proxy("http://127.0.0.1:%d/" % self.port)
-
-    def testResults(self):
-        inputOutput = [
-            ("add", (2, 3), 5),
-            ("defer", ("a",), "a"),
-            ("dict", ({"a": 1}, "a"), 1),
-            ("triple", ("a", 1), ["a", 1, None]),
-        ]
-
-        dl = []
-        for meth, args, outp in inputOutput:
-            d = self.proxy().callRemote(meth, *args)
-            d.addCallback(self.assertEqual, outp)
-            dl.append(d)
-
-        # SOAPpy kinda blows.
-        d = self.proxy().callRemote("complex")
-        d.addCallback(lambda result: result._asdict())
-        d.addCallback(self.assertEqual, {"a": ["b", "c", 12, []], "D": "foo"})
-        dl.append(d)
-
-        # We now return to our regularly scheduled program,
-        # already in progress.
-        return defer.DeferredList(dl, fireOnOneErrback=True)
-
-    def testMethodNotFound(self):
-        """
-        Check that a non existing method return error 500.
-        """
-        d = self.proxy().callRemote("doesntexist")
-        self.assertFailure(d, error.Error)
-
-        def cb(err):
-            self.assertEqual(int(err.status), 500)
-
-        d.addCallback(cb)
-        return d
-
-    def testLookupFunction(self):
-        """
-        Test lookupFunction method on publisher, to see available remote
-        methods.
-        """
-        self.assertTrue(self.publisher.lookupFunction("add"))
-        self.assertTrue(self.publisher.lookupFunction("fail"))
-        self.assertFalse(self.publisher.lookupFunction("foobar"))