Skip to content
This repository

HttpRpc protocol got some love. #86

Merged
merged 23 commits into from over 2 years ago

1 participant

Burak Arslan
Burak Arslan
Owner

No description provided.

Burak Arslan plq merged commit 202ebe5 into from September 11, 2011
Burak Arslan plq closed this September 11, 2011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
12  CHANGELOG.rst
Source Rendered
@@ -2,6 +2,18 @@
2 2
 Changelog
3 3
 =========
4 4
 
  5
+rpclib-2.3.1-beta
  6
+-----------------
  7
+ * HttpRpc protocol now returns 404 when a requested resource was not found.
  8
+ * New tests added for HttpRpc protocol.
  9
+ * Miscellanous other fixes.
  10
+
  11
+rpclib-2.3.0-beta
  12
+-----------------
  13
+ * Documentation improvements.
  14
+ * rpclib.protocol.xml.XmlObject is now working as out_protocol.
  15
+ * Many fixes.
  16
+
5 17
 rpclib-2.2.3-beta
6 18
 ------------------
7 19
  * Documentation improvements.
6  src/rpclib/__init__.py
@@ -18,3 +18,9 @@
18 18
 #
19 19
 
20 20
 __version__ = '2.3.0-beta'
  21
+
  22
+from _base import TransportContext
  23
+from _base import EventContext
  24
+from _base import MethodContext
  25
+from _base import MethodDescriptor
  26
+from _base import EventManager
3  src/rpclib/error.py
... ...
@@ -0,0 +1,3 @@
  1
+
  2
+class NotFoundError(Exception):
  3
+    """Raised when the requested resource was not found."""
13  src/rpclib/model/complex.py
@@ -24,10 +24,9 @@
24 24
 import logging
25 25
 logger = logging.getLogger(__name__)
26 26
 
27  
-from lxml import etree
28  
-
29 27
 from rpclib.model import ModelBase
30 28
 from rpclib.model import nillable_dict
  29
+from rpclib.model import nillable_string
31 30
 
32 31
 from rpclib.util.odict import odict as TypeInfo
33 32
 from rpclib.const import xml_ns as namespace
@@ -223,6 +222,16 @@ def get_flat_type_info(clz, retval={}):
223 222
         return retval
224 223
 
225 224
     @classmethod
  225
+    @nillable_string
  226
+    def to_string(cls, value):
  227
+        raise ValueError("Only primitives can be serialized to string.")
  228
+
  229
+    @classmethod
  230
+    @nillable_string
  231
+    def from_string(cls, string):
  232
+        raise ValueError("Only primitives can be deserialized from string.")
  233
+
  234
+    @classmethod
226 235
     @nillable_dict
227 236
     def from_dict(cls, in_dict):
228 237
         inst = cls.get_deserialization_instance()
1  src/rpclib/protocol/__init__.py
@@ -18,4 +18,3 @@
18 18
 #
19 19
 
20 20
 from _base import ProtocolBase
21  
-from _base import ValidationError
14  src/rpclib/protocol/_base.py
@@ -30,10 +30,8 @@
30 30
 _ns_xsd = rpclib.const.xml_ns.xsd
31 31
 
32 32
 from rpclib._base import EventManager
33  
-from rpclib.model.fault import Fault
34  
-
35  
-class ValidationError(Fault):
36  
-    pass
  33
+from rpclib.error import NotFoundError
  34
+# from pprint import pformat
37 35
 
38 36
 class ProtocolBase(object):
39 37
     """This is the abstract base class for all protocol implementations. Child
@@ -105,10 +103,10 @@ def set_method_descriptor(self, ctx):
105 103
 
106 104
         ctx.service_class = self.app.interface.service_mapping.get(name, None)
107 105
         if ctx.service_class is None:
108  
-            logger.debug(self.app.interface.service_mapping.keys())
109  
-            raise Exception('Method %r not bound to a service class.' % name)
  106
+            # logger.debug(pformat(self.app.interface.service_mapping.keys()))
  107
+            raise NotFoundError('Method %r not bound to a service class.' % name)
110 108
 
111 109
         ctx.descriptor = ctx.app.interface.method_mapping.get(name, None)
112 110
         if ctx.descriptor is None:
113  
-            logger.debug(ctx.app.interface.method_mapping.keys())
114  
-            raise Exception('Method %r not found.' % name)
  111
+            # logger.debug(pformat(ctx.app.interface.method_mapping.keys()))
  112
+            raise NotFoundError('Method %r not found.' % name)
46  src/rpclib/protocol/http.py
@@ -22,10 +22,23 @@
22 22
 import logging
23 23
 logger = logging.getLogger(__name__)
24 24
 
25  
-from rpclib.protocol import ProtocolBase
26 25
 import urlparse
27 26
 
  27
+from rpclib.error import NotFoundError
  28
+from rpclib.server.wsgi import HTTP_404
  29
+from rpclib.protocol import ProtocolBase
  30
+
28 31
 # this is not exactly rest, because it ignores http verbs.
  32
+
  33
+def _get_http_headers(req_env):
  34
+    retval = {}
  35
+
  36
+    for k,v in req_env.iteritems():
  37
+        if k.startswith("HTTP_"):
  38
+            retval[k[5:].lower()]= v
  39
+
  40
+    return retval
  41
+
29 42
 class HttpRpc(ProtocolBase):
30 43
     """The so-called ReST-minus-the-verbs HttpRpc protocol implementation.
31 44
     It only works with the http server (wsgi) transport.
@@ -42,9 +55,13 @@ def create_in_document(self, ctx, in_string_encoding=None):
42 55
                               ctx.transport.req_env['PATH_INFO'].split('/')[-1])
43 56
         logger.debug("\033[92mMethod name: %r\033[0m" % ctx.method_request_string)
44 57
 
45  
-        self.app.in_protocol.set_method_descriptor(ctx)
  58
+        try:
  59
+            self.app.in_protocol.set_method_descriptor(ctx)
  60
+        except NotFoundError, e:
  61
+            ctx.transport.resp_code = HTTP_404
  62
+            raise
46 63
 
47  
-        ctx.in_header_doc = None
  64
+        ctx.in_header_doc = _get_http_headers(ctx.transport.req_env)
48 65
         ctx.in_body_doc = urlparse.parse_qs(ctx.transport.req_env['QUERY_STRING'])
49 66
 
50 67
         logger.debug(repr(ctx.in_body_doc))
@@ -62,25 +79,20 @@ def deserialize(self, ctx):
62 79
 
63 80
     def serialize(self, ctx):
64 81
         result_message_class = ctx.descriptor.out_message
65  
-        result_message = result_message_class()
66 82
 
67 83
         # assign raw result to its wrapper, result_message
68 84
         out_type_info = result_message_class._type_info
69  
-        if len(out_type_info) > 0:
70  
-             if len(out_type_info) == 1:
71  
-                 attr_name = result_message_class._type_info.keys()[0]
72  
-                 setattr(result_message, attr_name, ctx.out_object)
73  
-
74  
-             else:
75  
-                 for i in range(len(out_type_info)):
76  
-                     attr_name=result_message_class._type_info.keys()[i]
77  
-                     setattr(result_message, attr_name, ctx.out_object[i])
  85
+        if len(out_type_info) == 1:
  86
+            out_class = out_type_info.values()[0]
  87
+            if ctx.out_object is None:
  88
+                ctx.out_document = u''
  89
+            else:
  90
+                ctx.out_document = out_class.to_string(ctx.out_object)
78 91
 
79  
-        wrapped_result = result_message_class.to_dict(result_message)
80  
-
81  
-        ctx.out_document, = wrapped_result.itervalues()
  92
+        else:
  93
+            raise ValueError("HttpRpc protocol can only serialize primitives.")
82 94
 
83 95
         self.event_manager.fire_event('serialize', ctx)
84 96
 
85 97
     def create_out_string(self, ctx, out_string_encoding=None):
86  
-        ctx.out_string = ctx.out_document
  98
+        ctx.out_string = [ctx.out_document.encode('utf8')]
7  src/rpclib/protocol/soap/soap11.py
@@ -34,7 +34,6 @@
34 34
 from rpclib.protocol.soap.mime import collapse_swa
35 35
 
36 36
 from rpclib.model.fault import Fault
37  
-from rpclib.model.primitive import string_encoding
38 37
 
39 38
 class ValidationError(Fault):
40 39
     pass
@@ -71,7 +70,7 @@ def _from_soap(in_envelope_xml, xmlids=None):
71 70
 def _parse_xml_string(xml_string, charset=None):
72 71
     try:
73 72
         if charset is None:
74  
-            charset = string_encoding
  73
+            charset = 'utf-8'
75 74
 
76 75
         root, xmlids = etree.XMLID(xml_string.decode(charset))
77 76
 
@@ -130,7 +129,7 @@ class OUT_WRAPPER:
130 129
         pass
131 130
 
132 131
     allowed_http_verbs = ['POST']
133  
-    mime_type = 'application/soap+xml'
  132
+    mime_type = 'text/xml; charset=utf-8'
134 133
 
135 134
     def __init__(self, app=None):
136 135
         XmlObject.__init__(self, app)
@@ -149,7 +148,7 @@ def create_in_document(self, ctx, charset=None):
149 148
     def create_out_string(self, ctx, charset=None):
150 149
         """Sets an iterable of string fragments to ctx.out_string"""
151 150
         if charset is None:
152  
-            charset = string_encoding
  151
+            charset = 'utf-8'
153 152
 
154 153
         ctx.out_string = [etree.tostring(ctx.out_document, xml_declaration=True,
155 154
                                                               encoding=charset)]
22  src/rpclib/protocol/xml/_base.py
@@ -26,6 +26,7 @@
26 26
 
27 27
 from rpclib.util.cdict import cdict
28 28
 
  29
+from rpclib.error import NotFoundError
29 30
 from rpclib.model import ModelBase
30 31
 
31 32
 from rpclib.model.complex import Array
@@ -82,10 +83,6 @@
82 83
     EnumBase: enum_from_element,
83 84
 })
84 85
 
85  
-class NotFoundError(Exception):
86  
-    """Raised when the requested resource was not found."""
87  
-    pass
88  
-
89 86
 class XmlObject(ProtocolBase):
90 87
     def from_element(self, cls, element):
91 88
         handler = _deserialization_handlers[cls]
@@ -98,15 +95,6 @@ def to_parent_element(self, cls, value, tns, parent_elt, * args, ** kwargs):
98 95
     def create_in_document(self, ctx, charset=None):
99 96
         ctx.in_document = etree.fromstring(ctx.in_string, charset)
100 97
 
101  
-    def create_out_string(self, ctx, charset=None):
102  
-        """Sets an iterable of string fragments to ctx.out_string"""
103  
-        if charset is None:
104  
-            charset = 'utf8'
105  
-
106  
-        ctx.out_string = [etree.tostring(ctx.out_document, xml_declaration=True,
107  
-                                                            encoding=charset)]
108  
-
109  
-    def decompose_incoming_envelope(self, ctx):
110 98
         body_doc = ctx.in_document
111 99
 
112 100
         try:
@@ -146,6 +134,14 @@ def decompose_incoming_envelope(self, ctx):
146 134
             # payload. That's SOAP's job to do.
147 135
         ctx.in_body_doc = body_doc
148 136
 
  137
+    def create_out_string(self, ctx, charset=None):
  138
+        """Sets an iterable of string fragments to ctx.out_string"""
  139
+        if charset is None:
  140
+            charset = 'utf8'
  141
+
  142
+        ctx.out_string = [etree.tostring(ctx.out_document, xml_declaration=True,
  143
+                                                            encoding=charset)]
  144
+
149 145
     def deserialize(self, ctx, way='out'):
150 146
         """Takes a MethodContext instance and a string containing ONE root xml
151 147
         tag.
9  src/rpclib/server/_base.py
@@ -26,12 +26,6 @@
26 26
 from rpclib.model.fault import Fault
27 27
 from rpclib._base import EventManager
28 28
 
29  
-class InternalError(Fault):
30  
-    pass
31  
-
32  
-class ValidationError(Fault):
33  
-    pass
34  
-
35 29
 class ServerBase(object):
36 30
     """This class is the abstract base class for all server transport
37 31
     implementations. Unlike the client transports, this class does not define
@@ -102,3 +96,6 @@ def get_out_string(self, ctx):
102 96
             else:
103 97
                 ctx.service_class.event_manager.fire_event(
104 98
                                             'method_exception_string', ctx)
  99
+
  100
+        if ctx.out_string is None:
  101
+            ctx.out_string = [""]
76  src/rpclib/server/wsgi.py
@@ -26,8 +26,10 @@
26 26
 
27 27
 import cgi
28 28
 
29  
-from rpclib._base import MethodContext
30  
-from rpclib.model.fault import Fault
  29
+from rpclib import TransportContext
  30
+from rpclib import MethodContext
  31
+
  32
+from rpclib.error import NotFoundError
31 33
 from rpclib.protocol.soap.mime import apply_mtom
32 34
 from rpclib.util import reconstruct_url
33 35
 from rpclib.server import ServerBase
@@ -59,18 +61,25 @@ def reconstruct_wsgi_request(http_env):
59 61
     return input.read(length), charset
60 62
 
61 63
 
62  
-class WsgiMethodContext(MethodContext):
63  
-    def __init__(self, app, req_env, content_type):
64  
-        MethodContext.__init__(self, app)
  64
+class WsgiTransportContext(TransportContext):
  65
+    def __init__(self, req_env, content_type):
  66
+        TransportContext.__init__(self, 'wsgi')
65 67
 
66  
-        self.transport.type = 'wsgi'
67  
-        self.transport.req_env = req_env
68  
-        self.transport.resp_headers = {
  68
+        self.req_env = req_env
  69
+        self.resp_headers = {
69 70
             'Content-Type': content_type,
70 71
             'Content-Length': '0',
71 72
         }
72  
-        self.transport.req_method = req_env.get('REQUEST_METHOD', None)
73  
-        self.transport.wsdl_error = None
  73
+        self.resp_code = None
  74
+        self.req_method = req_env.get('REQUEST_METHOD', None)
  75
+        self.wsdl_error = None
  76
+
  77
+
  78
+class WsgiMethodContext(MethodContext):
  79
+    def __init__(self, app, req_env, content_type):
  80
+        MethodContext.__init__(self, app)
  81
+
  82
+        self.transport = WsgiTransportContext(req_env, content_type)
74 83
 
75 84
 
76 85
 class WsgiApplication(ServerBase):
@@ -108,7 +117,7 @@ def __call__(self, req_env, start_response, wsgi_url=None):
108 117
     def __is_wsdl_request(self, req_env):
109 118
         # Get the wsdl for the service. Assume path_info matches pattern:
110 119
         # /stuff/stuff/stuff/serviceName.wsdl or
111  
-        # /stuff/stuff/stuff/serviceName/?wsdl
  120
+        # /stuff/stuff/stuff/serviceName/?wsdl with anything between ? and wsdl.
112 121
 
113 122
         return (
114 123
             req_env['REQUEST_METHOD'].lower() == 'get'
@@ -119,7 +128,7 @@ def __is_wsdl_request(self, req_env):
119 128
         )
120 129
 
121 130
     def __handle_wsdl_request(self, req_env, start_response, url):
122  
-        ctx = WsgiMethodContext(self.app, req_env, 'text/xml')
  131
+        ctx = WsgiMethodContext(self.app, req_env, 'text/xml; charset=utf-8')
123 132
 
124 133
         try:
125 134
             wsdl = self.app.interface.get_interface_document()
@@ -155,34 +164,45 @@ def __handle_rpc(self, req_env, start_response):
155 164
 
156 165
         ctx.in_string, in_string_charset = reconstruct_wsgi_request(req_env)
157 166
 
158  
-        self.get_in_object(ctx, in_string_charset)
  167
+        try:
  168
+            self.get_in_object(ctx, in_string_charset)
  169
+        except NotFoundError, e:
  170
+            pass
159 171
 
160  
-        return_code = HTTP_200
161 172
         if ctx.in_error:
162 173
             out_object = ctx.in_error
163  
-            return_code = HTTP_500
  174
+            if ctx.transport.resp_code is None:
  175
+                ctx.transport.resp_code = HTTP_500
164 176
 
165 177
         else:
166 178
             if ctx.service_class == None:
167  
-                start_response(HTTP_404, ctx.transport.resp_headers.items())
168  
-                return ['']
  179
+                if ctx.transport.resp_code is None:
  180
+                    ctx.transport.resp_code = HTTP_500
  181
+
  182
+                ctx.out_string = [ctx.transport.resp_code]
  183
+
  184
+                self.event_manager.fire_event('wsgi_method_not_found', ctx)
  185
+
  186
+                start_response(ctx.transport.resp_code, ctx.transport.resp_headers.items())
  187
+                return ctx.out_string
169 188
 
170 189
             self.get_out_object(ctx)
171  
-            if not (ctx.out_error is None):
172  
-                return_code = HTTP_500
  190
+            if ctx.out_error is None:
  191
+                ctx.transport.resp_code = HTTP_200
  192
+            else:
  193
+                ctx.transport.resp_code = HTTP_500
173 194
 
174 195
         self.get_out_string(ctx)
175  
-        if ctx.out_string is None:
176  
-            ctx.out_string = [""]
177 196
 
178 197
         # implementation hook
179 198
         self.event_manager.fire_event('wsgi_return', ctx)
180 199
 
181 200
         if ctx.descriptor and ctx.descriptor.mtom:
182  
-            # when there are more than one return type, the result is
  201
+            # when there is more than one return type, the result is
183 202
             # encapsulated inside a list. when there's just one, the result
184  
-            # is returned unencapsulated. the apply_mtom always expects the
185  
-            # objects to be inside an iterable, hence the following test.
  203
+            # is returned in a non-encapsulated form. the apply_mtom always
  204
+            # expects the objects to be inside an iterable, hence the following
  205
+            # test.
186 206
             out_type_info = ctx.descriptor.out_message._type_info
187 207
             if len(out_type_info) == 1:
188 208
                 out_object = [out_object]
@@ -193,8 +213,12 @@ def __handle_rpc(self, req_env, start_response):
193 213
                     out_object
194 214
                 )
195 215
 
196  
-        # initiate the response
  216
+        # We can't set the content-length if we want to support any kind of
  217
+        # python iterable as output. We can't iterate and count, that defeats
  218
+        # the whole point.
197 219
         del ctx.transport.resp_headers['Content-Length']
198  
-        start_response(return_code, ctx.transport.resp_headers.items())
  220
+
  221
+        # initiate the response
  222
+        start_response(ctx.transport.resp_code, ctx.transport.resp_headers.items())
199 223
 
200 224
         return ctx.out_string
29  src/rpclib/test/README
@@ -12,13 +12,23 @@ detect such corner cases is to have a great test suite.
12 12
 Requirements
13 13
 ------------
14 14
 
15  
-While the stock unittest package is normally enough to run Python tests, using
16  
-py.test from pytest package is just a more pleasant way to run them. Simply
17  
-easy_install pytest to get it. You can run the following command in the test
18  
-directory: ::
  15
+While simply executing test modules is normally enough to run Python tests,
  16
+using py.test from pytest package is just a more pleasant way to run them.
  17
+Simply easy_install pytest to get it. You can run the following command in the
  18
+test directory: ::
19 19
 
20 20
     py.test -v --tb=short
21 21
 
  22
+You can use the module name as an argument: ::
  23
+
  24
+    py.test -v --tb=short test_sqla.py
  25
+
  26
+You can also choose which test to run: ::
  27
+
  28
+    py.test -v --tb=short test_sqla.py -k test_same_table_inheritance
  29
+
  30
+See [pytest documentation](http://pytest.org/latest/) for more info.
  31
+
22 32
 Note that you need to do several other preparations to have the interop tests
23 33
 working. See the next section for the specifics.
24 34
 
@@ -28,6 +38,15 @@ Interoperability Tests
28 38
 
29 39
 The interoperability servers require twisted.web.
30 40
 
  41
+.Python
  42
+^^^^^^^
  43
+
  44
+Python interop tests currently use Rpclib's own clients and suds. The suds test
  45
+is the first thing we check and try not to break.
  46
+
  47
+Two tests that fail in the suds interop tests due to the lack of proper assert
  48
+statements, so they're false alarms.
  49
+
31 50
 .Net
32 51
 ^^^^
33 52
 
@@ -78,5 +97,3 @@ Here's the directory tree from a working setup:
78 97
     |       |-- README.txt
79 98
     |       `-- (...)
80 99
     `-- (...)
81  
-
82  
-
2  src/rpclib/test/interop/server/httprpc_pod_basic.py
@@ -41,7 +41,7 @@
41 41
         wsgi_application = WsgiApplication(httprpc_soap_application)
42 42
         server = make_server('0.0.0.0', 9757, validator(wsgi_application))
43 43
 
44  
-        logger.info('Starting interop server at %s:%s.' % ('0.0.0.0', 9756))
  44
+        logger.info('Starting interop server at %s:%s.' % ('0.0.0.0', 9757))
45 45
         logger.info('WSDL is at: /?wsdl')
46 46
         server.serve_forever()
47 47
 
86  src/rpclib/test/interop/test_httprpc.py
... ...
@@ -0,0 +1,86 @@
  1
+#!/usr/bin/env python
  2
+#
  3
+# rpclib - Copyright (C) Rpclib contributors.
  4
+#
  5
+# This library is free software; you can redistribute it and/or
  6
+# modify it under the terms of the GNU Lesser General Public
  7
+# License as published by the Free Software Foundation; either
  8
+# version 2.1 of the License, or (at your option) any later version.
  9
+#
  10
+# This library is distributed in the hope that it will be useful,
  11
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
  12
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13
+# Lesser General Public License for more details.
  14
+#
  15
+# You should have received a copy of the GNU Lesser General Public
  16
+# License along with this library; if not, write to the Free Software
  17
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
  18
+#
  19
+
  20
+import pytz
  21
+import unittest
  22
+import urllib
  23
+import urllib2
  24
+
  25
+from datetime import datetime
  26
+
  27
+class TestHttpRpc(unittest.TestCase):
  28
+    def test_404(self):
  29
+        url = 'http://localhost:9757/404'
  30
+        try:
  31
+            data = urllib2.urlopen(url).read()
  32
+        except urllib2.HTTPError, e:
  33
+            assert e.code == 404
  34
+
  35
+    def test_500(self):
  36
+        url = 'http://localhost:9757/python_exception'
  37
+        try:
  38
+            data = urllib2.urlopen(url).read()
  39
+        except urllib2.HTTPError, e:
  40
+            assert e.code == 500
  41
+
  42
+    def test_500_2(self):
  43
+        url = 'http://localhost:9757/soap_exception'
  44
+        try:
  45
+            data = urllib2.urlopen(url).read()
  46
+        except urllib2.HTTPError, e:
  47
+            assert e.code == 500
  48
+
  49
+    def test_echo_string(self):
  50
+        url = 'http://localhost:9757/echo_string?s=punk'
  51
+        data = urllib2.urlopen(url).read()
  52
+
  53
+        assert data == 'punk'
  54
+
  55
+    def test_echo_integer(self):
  56
+        url = 'http://localhost:9757/echo_integer?i=444'
  57
+        data = urllib2.urlopen(url).read()
  58
+
  59
+        assert data == '444'
  60
+
  61
+    def test_echo_datetime(self):
  62
+        dt = datetime.now().isoformat()
  63
+        params = urllib.urlencode({
  64
+            'dt': dt,
  65
+        })
  66
+
  67
+        print params
  68
+        url = 'http://localhost:9757/echo_datetime?%s' % str(params)
  69
+        data = urllib2.urlopen(url).read()
  70
+
  71
+        assert dt == data
  72
+
  73
+    def test_echo_datetime_tz(self):
  74
+        dt = datetime.now(pytz.utc).isoformat()
  75
+        params = urllib.urlencode({
  76
+            'dt': dt,
  77
+        })
  78
+
  79
+        print params
  80
+        url = 'http://localhost:9757/echo_datetime?%s' % str(params)
  81
+        data = urllib2.urlopen(url).read()
  82
+
  83
+        assert dt == data
  84
+
  85
+if __name__ == '__main__':
  86
+    unittest.main()
5  src/rpclib/test/interop/test_suds.py
@@ -62,10 +62,11 @@ def test_validation(self):
62 62
         non_nillable_class.s = None
63 63
 
64 64
         try:
65  
-            ret = self.client.service.non_nillable(non_nillable_class)
66  
-            raise Exception("must fail")
  65
+            self.client.service.non_nillable(non_nillable_class)
67 66
         except WebFault, e:
68 67
             pass
  68
+        else:
  69
+            raise Exception("must fail")
69 70
 
70 71
     def test_echo_integer_array(self):
71 72
         ia = self.client.factory.create('integerArray')
104  src/rpclib/test/protocol/test_http.py
... ...
@@ -0,0 +1,104 @@
  1
+
  2
+#!/usr/bin/env python
  3
+#
  4
+# rpclib - Copyright (C) Rpclib contributors.
  5
+#
  6
+# This library is free software; you can redistribute it and/or
  7
+# modify it under the terms of the GNU Lesser General Public
  8
+# License as published by the Free Software Foundation; either
  9
+# version 2.1 of the License, or (at your option) any later version.
  10
+#
  11
+# This library is distributed in the hope that it will be useful,
  12
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
  13
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14
+# Lesser General Public License for more details.
  15
+#
  16
+# You should have received a copy of the GNU Lesser General Public
  17
+# License along with this library; if not, write to the Free Software
  18
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
  19
+#
  20
+
  21
+import logging
  22
+logging.basicConfig(level=logging.DEBUG)
  23
+
  24
+import unittest
  25
+
  26
+from rpclib.application import Application
  27
+from rpclib.decorator import srpc
  28
+from rpclib.model.primitive import Integer
  29
+from rpclib.model.primitive import String
  30
+from rpclib.model.complex import ComplexModel
  31
+from rpclib.interface.wsdl import Wsdl11
  32
+from rpclib.protocol.http import HttpRpc
  33
+from rpclib.protocol.soap import Soap11
  34
+from rpclib.service import ServiceBase
  35
+
  36
+class Test(unittest.TestCase):
  37
+    '''Most of the service tests are performed through the interop tests.'''
  38
+
  39
+    def test_multiple_return(self):
  40
+        class SomeNotSoComplexModel(ComplexModel):
  41
+            s = String
  42
+
  43
+        class SomeService(ServiceBase):
  44
+            @srpc(_returns=[Integer, String])
  45
+            def some_call():
  46
+                return 1,'s'
  47
+
  48
+        app = Application([SomeService], 'tns', Wsdl11(), HttpRpc(), HttpRpc())
  49
+
  50
+        from rpclib.server.wsgi import WsgiMethodContext
  51
+
  52
+        ctx = WsgiMethodContext(app,{
  53
+            'QUERY_STRING': '',
  54
+            'PATH_INFO': '/some_call',
  55
+        }, 'some-content-type')
  56
+
  57
+        from rpclib.server import ServerBase
  58
+
  59
+        server = ServerBase(app)
  60
+        try:
  61
+            server.get_in_object(ctx)
  62
+            server.get_out_object(ctx)
  63
+            server.get_out_string(ctx)
  64
+        except ValueError:
  65
+            pass
  66
+        else:
  67
+            raise Exception("Must Fail")
  68
+
  69
+    def test_primitive_only(self):
  70
+        class SomeComplexModel(ComplexModel):
  71
+            i = Integer
  72
+            s = String
  73
+
  74
+        class SomeService(ServiceBase):
  75
+            @srpc(SomeComplexModel, _returns=SomeComplexModel)
  76
+            def some_call(scm):
  77
+                return SomeComplexModel(i=5,s='5x')
  78
+
  79
+        app = Application([SomeService], 'tns', Wsdl11(), HttpRpc(), HttpRpc())
  80
+
  81
+        from rpclib.server.wsgi import WsgiMethodContext
  82
+
  83
+        ctx = WsgiMethodContext(app,{
  84
+            'QUERY_STRING': '',
  85
+            'PATH_INFO': '/some_call',
  86
+        }, 'some-content-type')
  87
+
  88
+        from rpclib.server import ServerBase
  89
+
  90
+        server = ServerBase(app)
  91
+
  92
+        try:
  93
+            server.get_in_object(ctx)
  94
+            server.get_out_object(ctx)
  95
+            server.get_out_string(ctx)
  96
+        except ValueError:
  97
+            pass
  98
+        else:
  99
+            raise Exception("Must Fail")
  100
+
  101
+
  102
+
  103
+if __name__ == '__main__':
  104
+    unittest.main()
51  src/rpclib/test/test_service.py
@@ -17,11 +17,14 @@
17 17
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
18 18
 #
19 19
 
  20
+#
  21
+# Most of the service tests are performed through the interop tests.
  22
+#
20 23
 import datetime
21 24
 import unittest
22 25
 
23  
-import rpclib.interface.wsdl
24  
-import rpclib.protocol.soap
  26
+from rpclib.interface.wsdl import Wsdl11
  27
+from rpclib.protocol.soap import Soap11
25 28
 
26 29
 from lxml import etree
27 30
 
@@ -124,55 +127,49 @@ class MultipleReturnService(ServiceBase):
124 127
     def multi(self, s):
125 128
         return s, 'a', 'b'
126 129
 
127  
-class Test(unittest.TestCase):
128  
-    '''Most of the service tests are performed through the interop tests.'''
129  
-
130  
-    def _set_up(self):
131  
-        self.app = Application([TestService], rpclib.interface.wsdl.Wsdl11,
132  
-                                        rpclib.protocol.soap.Soap11, tns='tns')
  130
+class TestSingle(unittest.TestCase):
  131
+    def setUp(self):
  132
+        self.app = Application([TestService], 'tns', Wsdl11(), Soap11(), Soap11())
133 133
         self.srv = TestService()
  134
+
134 135
         self.app.interface.build_interface_document('URL')
135  
-        self._wsdl = self.app.interface.get_interface_document()
136  
-        self.wsdl = etree.fromstring(self._wsdl)
  136
+        self.wsdl_str = self.app.interface.get_interface_document()
  137
+        self.wsdl_doc = etree.fromstring(self.wsdl_str)
137 138
 
138 139
     def test_portypes(self):
139  
-        self._set_up()
140  
-
141  
-        porttype = self.wsdl.find('{http://schemas.xmlsoap.org/wsdl/}portType')
  140
+        porttype = self.wsdl_doc.find('{http://schemas.xmlsoap.org/wsdl/}portType')
142 141
         self.assertEquals(
143 142
             len(self.srv.public_methods), len(porttype.getchildren()))
144 143
 
145 144
     def test_override_param_names(self):
146  
-        self._set_up()
  145
+        # FIXME: This test must be rewritten.
147 146
 
148 147
         for n in ['self', 'import', 'return', 'from']:
149  
-            self.assertTrue(n in self._wsdl, '"%s" not in self._wsdl' % n)
  148
+            self.assertTrue(n in self.wsdl_str, '"%s" not in self.wsdl_str' % n)
  149
+
  150
+class TestMultiple(unittest.TestCase):
  151
+    def setUp(self):
  152
+        self.app = Application([MultipleReturnService], 'tns', Wsdl11(), Soap11(), Soap11())
  153
+        self.app.interface.build_interface_document('url')
150 154
 
151 155
     def test_multiple_return(self):
152  
-        app = Application([MultipleReturnService], rpclib.interface.wsdl.Wsdl11,
153  
-                                        rpclib.protocol.soap.Soap11, tns='tns')
154  
-        app.interface.build_interface_document('url')
155  
-        srv = MultipleReturnService()
156  
-        message = srv.public_methods.values()[0].out_message()
  156
+        message_class = MultipleReturnService.public_methods.values()[0].out_message
  157
+        message = message_class()
157 158
 
158 159
         self.assertEquals(len(message._type_info), 3)
159 160
 
160 161
         sent_xml = etree.Element('test')
161  
-        message.to_parent_element( ('a','b','c'), srv.get_tns(), sent_xml )
  162
+        self.app.out_protocol.to_parent_element(message_class, ('a','b','c'),
  163
+                                    MultipleReturnService.get_tns(), sent_xml)
162 164
         sent_xml = sent_xml[0]
163 165
 
164 166
         print etree.tostring(sent_xml, pretty_print=True)
165  
-        response_data = message.from_xml(sent_xml)
  167
+        response_data = self.app.out_protocol.from_element(message_class, sent_xml)
166 168
 
167 169
         self.assertEquals(len(response_data), 3)
168 170
         self.assertEqual(response_data[0], 'a')
169 171
         self.assertEqual(response_data[1], 'b')
170 172
         self.assertEqual(response_data[2], 'c')
171 173
 
172  
-    def test_multiple_ns(self):
173  
-        svc = Application([MultipleNamespaceService],rpclib.interface.wsdl.Wsdl11,
174  
-                                         rpclib.protocol.soap.Soap11,tns='tns')
175  
-        svc.interface.get_interface_document()
176  
-
177 174
 if __name__ == '__main__':
178 175
     unittest.main()
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.