Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 95 | ||||
Proposed branch: | lp:~roger-lp/ladon/trunk | ||||
Merge into: | lp:ladon | ||||
Diff against target: |
883 lines (+520/-148) 8 files modified
frameworks/python/src/ladon/interfaces/__init__.py (+1/-0) frameworks/python/src/ladon/interfaces/base.py (+0/-147) frameworks/python/src/ladon/interfaces/jsonrpc10.py (+205/-0) frameworks/python/src/ladon/server/dispatcher.py (+2/-0) frameworks/python/src/ladon/types/typemanager.py (+3/-1) frameworks/python/tests/servicerunner.py (+1/-0) frameworks/python/tests/services/jsonrpc10.py (+54/-0) frameworks/python/tests/testjsonrpc10.py (+254/-0) |
||||
To merge this branch: | bzr merge lp:~roger-lp/ladon/trunk | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
jsgaarde | code | Approve | |
Mikhus | Pending | ||
Review via email: mp+131028@code.launchpad.net |
This proposal supersedes a proposal from 2012-10-23.
Commit message
Description of the change
Contains implementation of JSON-RPC 1.0 specification for request and response, notifications hasn't implemented yet. Also contains fix for python 3.3 __qualname__ (PEP 3155 -- Qualified name for classes and functions).
Mikhus (mikhus) wrote : Posted in a previous version of this proposal | # |
roger (roger-lp) wrote : Posted in a previous version of this proposal | # |
.bzrignore was removed
jsgaarde (jakob-simon-gaarde) wrote : | # |
Please explain what these do and why they are nessecary:
add_passback_
roger (roger-lp) wrote : | # |
I have decided to implement these two methods because I have faced with needs of interaction with request parameters which should be returned to client without changes (pass back parameters), it means that such parameters are used as markers, like "id" parameter in json-rpc requests and responses.
get_passback_
add_passback_
roger (roger-lp) wrote : | # |
One more clarification, these methods are needed for implementation request/response id mechanism as described in json-rpc 1.0 specification.
jsgaarde (jakob-simon-gaarde) wrote : | # |
OK, I faced the same problem with jsonwsp implementing the mirror/reflection mechanism (built it into dispatcher as a general rule for all service protocols). To be honest - it should probably have been done something like your solution.
I'll finish my review tonight :-)
jsgaarde (jakob-simon-gaarde) wrote : | # |
Only one more question. What is the effect of not having check for '__qualname__' in the TypeManager?
/ Jakob
jsgaarde (jakob-simon-gaarde) wrote : | # |
Changes needed:
1. Here is something I don't do everyday, I'm going to ask you to to un-nice your solution (but to my defense it's for simplicity)
I have thought about the passback methods in interfaces.base.* - I think your solution is nice and everything, but I don't think we should extend the interfaces.
So I suggest a smaller not as nice solution for now:
http://
I know you meen to make the nicest solution I'm just worried about extending the interfaces.
2. please remove the methodname argument in call_method() of the dispatcher it is redundant cause the methodname _MUST_ recide in req_dict[
That also means you have to change your jsonrpc10 interface class so the method name doesn't end up in req_dict["method"].
Best Regards Jakob
roger (roger-lp) wrote : | # |
If there are not check for '__qualname__' when I run ladon application using python 3.3 I get following exception for any exposed with ladonize method:
Traceback (most recent call last):
File "/home/
self.
File "/home/
__import_
File "/home/
class AttachmentTestS
File "/home/
@ladonize(
File "/home/
ladon_
File "/home/
method = self.services[
File "/home/
method = LadonMethodInfo
File "/home/
self.
File "/home/
self.
File "/home/
raise NeedToDefinePar
class: UploadFileRespo
jsgaarde (jakob-simon-gaarde) wrote : | # |
OK, so this is a fix Python 3.3+? good :-)
> If there are not check for '__qualname__' when I run ladon application using
> python 3.3 I get following exception for any exposed with ladonize method:
>
> Traceback (most recent call last):
> File "/home/
> pplication.py", line 318, in __call__
> self.import_
> File "/home/
> pplication.py", line 274, in import_services
> __import__(service)
> File "/home/
> tests.py", line 23, in <module>
> class AttachmentTestS
> File "/home/
> tests.py", line 30, in AttachmentTestS
> @ladonize(
> File "/home/
> orator.py", line 88, in decorator
> ladon_method_info =
> global_
> File "/home/
> lection.py", line 133, in add_service_method
> method =
> self.services[
> File "/home/
> lection.py", line 325, in add_method
> method = LadonMethodInfo
> File "/home/
> lection.py", line 507, in __init__
> self._multipart
> sinfo.typemanag
> File "/home/
> ager.py", line 158, in analyze_param
> self.analyze_
> File "/home/
> ager.py", line 116, in analyze_class
> raise NeedToDefinePar
> be defined as types, lists.\\nclass: %s\\nattr: %s" %
> (cls.__
> class attributes on LadonTypes must be defined as types, lists.
> class: UploadFileRespo
roger (roger-lp) wrote : | # |
Yup :)
> OK, so this is a fix Python 3.3+? good :-)
>
> > If there are not check for '__qualname__' when I run ladon application using
> > python 3.3 I get following exception for any exposed with ladonize method:
> >
> > Traceback (most recent call last):
> > File
> "/home/
> > pplication.py", line 318, in __call__
> > self.import_
> > File
> "/home/
> > pplication.py", line 274, in import_services
> > __import__(service)
> > File
> "/home/
> > tests.py", line 23, in <module>
> > class AttachmentTestS
> > File
> "/home/
> > tests.py", line 30, in AttachmentTestS
> > @ladonize(
> > File
> "/home/
> > orator.py", line 88, in decorator
> > ladon_method_info =
> > global_
> > File
> "/home/
> > lection.py", line 133, in add_service_method
> > method =
> > self.services[
> > File
> "/home/
> > lection.py", line 325, in add_method
> > method = LadonMethodInfo
> > File
> "/home/
> > lection.py", line 507, in __init__
> > self._multipart
> > sinfo.typemanag
> > File
> "/home/
> > ager.py", line 158, in analyze_param
> > self.analyze_
> > File
> "/home/
> > ager.py", line 116, in analyze_class
> > raise NeedToDefinePar
> must
> > be defined as types, lists.\\nclass: %s\\nattr: %s" %
> > (cls.__
> > class attributes on LadonTypes must be defined as types, lists.
> > class: UploadFileRespo
roger (roger-lp) wrote : | # |
Remarks were applied.
Regards Roger
> Changes needed:
>
> 1. Here is something I don't do everyday, I'm going to ask you to to un-nice
> your solution (but to my defense it's for simplicity)
>
> I have thought about the passback methods in interfaces.base.* - I think your
> solution is nice and everything, but I don't think we should extend the
> interfaces.
> So I suggest a smaller not as nice solution for now:
>
> http://
>
> I know you meen to make the nicest solution I'm just worried about extending
> the interfaces.
> and these are exactly the classes we want to attract other people to use for
> contributing new interfaces.
>
> 2. please remove the methodname argument in call_method() of the dispatcher it
> is redundant cause the methodname _MUST_ recide in req_dict[
> have to stick to some kind of format in req_dict and res_dict as they are very
> central in Ladon's design.
> That also means you have to change your jsonrpc10 interface class so the
> method name doesn't end up in req_dict["method"].
>
> Best Regards Jakob
jsgaarde (jakob-simon-gaarde) wrote : | # |
Merged - thanks Roger!
Preview Diff
1 | === modified file 'frameworks/python/src/ladon/interfaces/__init__.py' | |||
2 | --- frameworks/python/src/ladon/interfaces/__init__.py 2012-05-04 14:43:58 +0000 | |||
3 | +++ frameworks/python/src/ladon/interfaces/__init__.py 2012-10-26 11:52:28 +0000 | |||
4 | @@ -71,3 +71,4 @@ | |||
5 | 71 | import ladon.interfaces.soap | 71 | import ladon.interfaces.soap |
6 | 72 | import ladon.interfaces.soap11 | 72 | import ladon.interfaces.soap11 |
7 | 73 | import ladon.interfaces.jsonwsp | 73 | import ladon.interfaces.jsonwsp |
8 | 74 | import ladon.interfaces.jsonrpc10 | ||
9 | 74 | 75 | ||
10 | === added file 'frameworks/python/src/ladon/interfaces/base.py' | |||
11 | --- frameworks/python/src/ladon/interfaces/base.py 1970-01-01 00:00:00 +0000 | |||
12 | +++ frameworks/python/src/ladon/interfaces/base.py 2012-10-26 11:52:28 +0000 | |||
13 | @@ -0,0 +1,147 @@ | |||
14 | 1 | # -*- coding: utf-8 -*- | ||
15 | 2 | |||
16 | 3 | import inspect | ||
17 | 4 | |||
18 | 5 | class BaseInterface(object): | ||
19 | 6 | """All interface implementations must descend from BaseInterface. The interface | ||
20 | 7 | implementation can be thought of as an aggregator that ties the three basic functions | ||
21 | 8 | of a web service protocol together: | ||
22 | 9 | |||
23 | 10 | 1. ServiceDescriptor - A generator class that can provide a description for the service. | ||
24 | 11 | ie. WSDL for soap. | ||
25 | 12 | 2. BaseRequestHandler - A handler that can parse the raw request objects from the client and | ||
26 | 13 | convert them into a so-called req_dict (Request dictionary) | ||
27 | 14 | 3. BaseResponseHandler - A handler that can process the res_dict (Result Dictionary) of a | ||
28 | 15 | service call and build a response object that fit's the protocol being implemented. | ||
29 | 16 | 4. BaseFaultHandler - A handler that can convert a ladon.exceptions.service.ServiceFault object | ||
30 | 17 | to a fault response that fits the interface. | ||
31 | 18 | |||
32 | 19 | """ | ||
33 | 20 | def __init__(self,sinfo,**kw): | ||
34 | 21 | self._sinfo = sinfo | ||
35 | 22 | sd = ServiceDescriptor() | ||
36 | 23 | if 'service_descriptor' in kw: | ||
37 | 24 | sd = kw['service_descriptor'] | ||
38 | 25 | if inspect.getmro(sd).count(ServiceDescriptor): | ||
39 | 26 | self._service_descriptor = sd() | ||
40 | 27 | if 'request_handler' in kw: | ||
41 | 28 | req_handler = kw['request_handler'] | ||
42 | 29 | if inspect.getmro(req_handler).count(BaseRequestHandler): | ||
43 | 30 | self._request_handler = req_handler() | ||
44 | 31 | if 'response_handler' in kw: | ||
45 | 32 | resp_handler = kw['response_handler'] | ||
46 | 33 | if inspect.getmro(resp_handler).count(BaseResponseHandler): | ||
47 | 34 | self._response_handler = resp_handler() | ||
48 | 35 | if 'fault_handler' in kw: | ||
49 | 36 | fault_handler = kw['fault_handler'] | ||
50 | 37 | if inspect.getmro(fault_handler).count(BaseFaultHandler): | ||
51 | 38 | self._fault_handler = fault_handler() | ||
52 | 39 | |||
53 | 40 | @staticmethod | ||
54 | 41 | def _interface_name(typ): | ||
55 | 42 | return None | ||
56 | 43 | |||
57 | 44 | @staticmethod | ||
58 | 45 | def _accept_basetype(typ): | ||
59 | 46 | return False | ||
60 | 47 | |||
61 | 48 | @staticmethod | ||
62 | 49 | def _accept_list(): | ||
63 | 50 | return False | ||
64 | 51 | |||
65 | 52 | @staticmethod | ||
66 | 53 | def _accept_dict(): | ||
67 | 54 | return False | ||
68 | 55 | |||
69 | 56 | def service_info(self): | ||
70 | 57 | return self._sinfo | ||
71 | 58 | |||
72 | 59 | def parse_request(self,soap_body,encoding='UTF-8'): | ||
73 | 60 | return self._request_handler.parse_request(soap_body,self._sinfo,encoding) | ||
74 | 61 | |||
75 | 62 | def build_response(self,res_dict,encoding='UTF-8'): | ||
76 | 63 | return self._response_handler.build_response(res_dict,self._sinfo,encoding) | ||
77 | 64 | |||
78 | 65 | def build_fault_response(self,exc,methodname=None,encoding='UTF-8'): | ||
79 | 66 | return self._fault_handler.build_fault_response(exc,self._sinfo,methodname,encoding) | ||
80 | 67 | |||
81 | 68 | def stringify_res_dict(self): | ||
82 | 69 | return self._response_handler._stringify_res_dict | ||
83 | 70 | |||
84 | 71 | def response_content_type(self): | ||
85 | 72 | return self._response_handler._content_type | ||
86 | 73 | |||
87 | 74 | def description(self,service_url,encoding='UTF-8'): | ||
88 | 75 | return self._service_descriptor.generate( | ||
89 | 76 | self._sinfo.servicename, | ||
90 | 77 | self._sinfo.servicenumber, | ||
91 | 78 | self._sinfo.typemanager, | ||
92 | 79 | self._sinfo.method_list(), | ||
93 | 80 | service_url,encoding) | ||
94 | 81 | |||
95 | 82 | def description_content_type(self): | ||
96 | 83 | return self._service_descriptor._content_type | ||
97 | 84 | |||
98 | 85 | class ServiceDescriptor(object): | ||
99 | 86 | |||
100 | 87 | _content_type = 'text/plain' | ||
101 | 88 | |||
102 | 89 | def generate(self,servicename,servicenumber,typemanager,methodlist,encoding): | ||
103 | 90 | """ | ||
104 | 91 | Implement the method that can generate a service description file for the | ||
105 | 92 | type of interface you are developing. Thus if you were developing a new | ||
106 | 93 | and better SOAP interface and a new and better service descriptor for it, | ||
107 | 94 | you will be implementing a WSDL generator in this method. | ||
108 | 95 | |||
109 | 96 | @param servicename This is a string containing the service class name | ||
110 | 97 | @param servicenumber An integer that reflects the order in which the service classes are parsed (useful for creating namespaces) | ||
111 | 98 | @param typemanager An instance of ladon.ladonizer.collection.TypeManager which manages the types used by the service | ||
112 | 99 | @param methodlist The list of methods exposed by the service | ||
113 | 100 | @param encoding The client-side controlled encoding to use in the descriptor | ||
114 | 101 | |||
115 | 102 | @rtype String representation of the service descriptor (ie. wsdl-formatted if it were SOAP) | ||
116 | 103 | """ | ||
117 | 104 | return '' | ||
118 | 105 | |||
119 | 106 | |||
120 | 107 | class BaseRequestHandler(object): | ||
121 | 108 | |||
122 | 109 | def parse_request(self,req,sinfo,encoding): | ||
123 | 110 | return {} | ||
124 | 111 | |||
125 | 112 | |||
126 | 113 | class BaseResponseHandler(object): | ||
127 | 114 | |||
128 | 115 | """ | ||
129 | 116 | This is the base class of all response handlers. To implement a new response handler | ||
130 | 117 | inherit it and overload the build_response() method so it implements the protocol you are | ||
131 | 118 | extending Ladon with. | ||
132 | 119 | |||
133 | 120 | set _stringify_res_dict to True if you want the Ladon dispatcher to convert all result | ||
134 | 121 | values to unicode regardless of their defined types during ladonization. This can be useful | ||
135 | 122 | in some interfaces. ie. SOAP where all values are presented the same way inside XML tags no | ||
136 | 123 | matter if it is a string or a number (example: <name>string</name><age>37</age>) | ||
137 | 124 | """ | ||
138 | 125 | _content_type = 'text/plain' | ||
139 | 126 | _stringify_res_dict = False | ||
140 | 127 | |||
141 | 128 | def build_response(self,method_result,sinfo,encoding): | ||
142 | 129 | return '' | ||
143 | 130 | |||
144 | 131 | class BaseFaultHandler(object): | ||
145 | 132 | |||
146 | 133 | """ | ||
147 | 134 | This is the base class of all fault handlers. To implement a new fault handler | ||
148 | 135 | inherit it and overload the build_fault_response() method so it implements the | ||
149 | 136 | protocol you are extending Ladon with. | ||
150 | 137 | |||
151 | 138 | set _stringify_res_dict to True if you want the Ladon dispatcher to convert all result | ||
152 | 139 | values to unicode regardless of their defined types during ladonization. This can be useful | ||
153 | 140 | in some interfaces. ie. SOAP where all values are presented the same way inside XML tags no | ||
154 | 141 | matter if it is a string or a number (example: <name>string</name><age>37</age>) | ||
155 | 142 | """ | ||
156 | 143 | _content_type = 'text/plain' | ||
157 | 144 | _stringify_res_dict = False | ||
158 | 145 | |||
159 | 146 | def build_fault_response(self,exc,sinfo,methodname,encoding): | ||
160 | 147 | return '' | ||
161 | 0 | 148 | ||
162 | === removed file 'frameworks/python/src/ladon/interfaces/base.py' | |||
163 | --- frameworks/python/src/ladon/interfaces/base.py 2012-01-06 12:44:37 +0000 | |||
164 | +++ frameworks/python/src/ladon/interfaces/base.py 1970-01-01 00:00:00 +0000 | |||
165 | @@ -1,147 +0,0 @@ | |||
166 | 1 | # -*- coding: utf-8 -*- | ||
167 | 2 | |||
168 | 3 | import inspect | ||
169 | 4 | |||
170 | 5 | class BaseInterface(object): | ||
171 | 6 | """All interface implementations must descend from BaseInterface. The interface | ||
172 | 7 | implementation can be thought of as an aggregator that ties the three basic functions | ||
173 | 8 | of a web service protocol together: | ||
174 | 9 | |||
175 | 10 | 1. ServiceDescriptor - A generator class that can provide a description for the service. | ||
176 | 11 | ie. WSDL for soap. | ||
177 | 12 | 2. BaseRequestHandler - A handler that can parse the raw request objects from the client and | ||
178 | 13 | convert them into a so-called req_dict (Request dictionary) | ||
179 | 14 | 3. BaseResponseHandler - A handler that can process the res_dict (Result Dictionary) of a | ||
180 | 15 | service call and build a response object that fit's the protocol being implemented. | ||
181 | 16 | 4. BaseFaultHandler - A handler that can convert a ladon.exceptions.service.ServiceFault object | ||
182 | 17 | to a fault response that fits the interface. | ||
183 | 18 | |||
184 | 19 | """ | ||
185 | 20 | def __init__(self,sinfo,**kw): | ||
186 | 21 | self._sinfo = sinfo | ||
187 | 22 | sd = ServiceDescriptor() | ||
188 | 23 | if 'service_descriptor' in kw: | ||
189 | 24 | sd = kw['service_descriptor'] | ||
190 | 25 | if inspect.getmro(sd).count(ServiceDescriptor): | ||
191 | 26 | self._service_descriptor = sd() | ||
192 | 27 | if 'request_handler' in kw: | ||
193 | 28 | req_handler = kw['request_handler'] | ||
194 | 29 | if inspect.getmro(req_handler).count(BaseRequestHandler): | ||
195 | 30 | self._request_handler = req_handler() | ||
196 | 31 | if 'response_handler' in kw: | ||
197 | 32 | resp_handler = kw['response_handler'] | ||
198 | 33 | if inspect.getmro(resp_handler).count(BaseResponseHandler): | ||
199 | 34 | self._response_handler = resp_handler() | ||
200 | 35 | if 'fault_handler' in kw: | ||
201 | 36 | fault_handler = kw['fault_handler'] | ||
202 | 37 | if inspect.getmro(fault_handler).count(BaseFaultHandler): | ||
203 | 38 | self._fault_handler = fault_handler() | ||
204 | 39 | |||
205 | 40 | @staticmethod | ||
206 | 41 | def _interface_name(typ): | ||
207 | 42 | return None | ||
208 | 43 | |||
209 | 44 | @staticmethod | ||
210 | 45 | def _accept_basetype(typ): | ||
211 | 46 | return False | ||
212 | 47 | |||
213 | 48 | @staticmethod | ||
214 | 49 | def _accept_list(): | ||
215 | 50 | return False | ||
216 | 51 | |||
217 | 52 | @staticmethod | ||
218 | 53 | def _accept_dict(): | ||
219 | 54 | return False | ||
220 | 55 | |||
221 | 56 | def service_info(self): | ||
222 | 57 | return self._sinfo | ||
223 | 58 | |||
224 | 59 | def parse_request(self,soap_body,encoding='UTF-8'): | ||
225 | 60 | return self._request_handler.parse_request(soap_body,self._sinfo,encoding) | ||
226 | 61 | |||
227 | 62 | def build_response(self,res_dict,encoding='UTF-8'): | ||
228 | 63 | return self._response_handler.build_response(res_dict,self._sinfo,encoding) | ||
229 | 64 | |||
230 | 65 | def build_fault_response(self,exc,methodname=None,encoding='UTF-8'): | ||
231 | 66 | return self._fault_handler.build_fault_response(exc,self._sinfo,methodname,encoding) | ||
232 | 67 | |||
233 | 68 | def stringify_res_dict(self): | ||
234 | 69 | return self._response_handler._stringify_res_dict | ||
235 | 70 | |||
236 | 71 | def response_content_type(self): | ||
237 | 72 | return self._response_handler._content_type | ||
238 | 73 | |||
239 | 74 | def description(self,service_url,encoding='UTF-8'): | ||
240 | 75 | return self._service_descriptor.generate( | ||
241 | 76 | self._sinfo.servicename, | ||
242 | 77 | self._sinfo.servicenumber, | ||
243 | 78 | self._sinfo.typemanager, | ||
244 | 79 | self._sinfo.method_list(), | ||
245 | 80 | service_url,encoding) | ||
246 | 81 | |||
247 | 82 | def description_content_type(self): | ||
248 | 83 | return self._service_descriptor._content_type | ||
249 | 84 | |||
250 | 85 | class ServiceDescriptor(object): | ||
251 | 86 | |||
252 | 87 | _content_type = 'text/plain' | ||
253 | 88 | |||
254 | 89 | def generate(self,servicename,servicenumber,typemanager,methodlist,encoding): | ||
255 | 90 | """ | ||
256 | 91 | Implement the method that can generate a service description file for the | ||
257 | 92 | type of interface you are developing. Thus if you were developing a new | ||
258 | 93 | and better SOAP interface and a new and better service descriptor for it, | ||
259 | 94 | you will be implementing a WSDL generator in this method. | ||
260 | 95 | |||
261 | 96 | @param servicename This is a string containing the service class name | ||
262 | 97 | @param servicenumber An integer that reflects the order in which the service classes are parsed (useful for creating namespaces) | ||
263 | 98 | @param typemanager An instance of ladon.ladonizer.collection.TypeManager which manages the types used by the service | ||
264 | 99 | @param methodlist The list of methods exposed by the service | ||
265 | 100 | @param encoding The client-side controlled encoding to use in the descriptor | ||
266 | 101 | |||
267 | 102 | @rtype String representation of the service descriptor (ie. wsdl-formatted if it were SOAP) | ||
268 | 103 | """ | ||
269 | 104 | return '' | ||
270 | 105 | |||
271 | 106 | |||
272 | 107 | class BaseRequestHandler(object): | ||
273 | 108 | |||
274 | 109 | def parse_request(self,req,sinfo,encoding): | ||
275 | 110 | return {} | ||
276 | 111 | |||
277 | 112 | |||
278 | 113 | class BaseResponseHandler(object): | ||
279 | 114 | |||
280 | 115 | """ | ||
281 | 116 | This is the base class of all response handlers. To implement a new response handler | ||
282 | 117 | inherit it and overload the build_response() method so it implements the protocol you are | ||
283 | 118 | extending Ladon with. | ||
284 | 119 | |||
285 | 120 | set _stringify_res_dict to True if you want the Ladon dispatcher to convert all result | ||
286 | 121 | values to unicode regardless of their defined types during ladonization. This can be useful | ||
287 | 122 | in some interfaces. ie. SOAP where all values are presented the same way inside XML tags no | ||
288 | 123 | matter if it is a string or a number (example: <name>string</name><age>37</age>) | ||
289 | 124 | """ | ||
290 | 125 | _content_type = 'text/plain' | ||
291 | 126 | _stringify_res_dict = False | ||
292 | 127 | |||
293 | 128 | def build_response(self,method_result,sinfo,encoding): | ||
294 | 129 | return '' | ||
295 | 130 | |||
296 | 131 | class BaseFaultHandler(object): | ||
297 | 132 | |||
298 | 133 | """ | ||
299 | 134 | This is the base class of all fault handlers. To implement a new fault handler | ||
300 | 135 | inherit it and overload the build_fault_response() method so it implements the | ||
301 | 136 | protocol you are extending Ladon with. | ||
302 | 137 | |||
303 | 138 | set _stringify_res_dict to True if you want the Ladon dispatcher to convert all result | ||
304 | 139 | values to unicode regardless of their defined types during ladonization. This can be useful | ||
305 | 140 | in some interfaces. ie. SOAP where all values are presented the same way inside XML tags no | ||
306 | 141 | matter if it is a string or a number (example: <name>string</name><age>37</age>) | ||
307 | 142 | """ | ||
308 | 143 | _content_type = 'text/plain' | ||
309 | 144 | _stringify_res_dict = False | ||
310 | 145 | |||
311 | 146 | def build_fault_response(self,exc,sinfo,methodname,encoding): | ||
312 | 147 | return '' | ||
313 | 148 | 0 | ||
314 | === added file 'frameworks/python/src/ladon/interfaces/jsonrpc10.py' | |||
315 | --- frameworks/python/src/ladon/interfaces/jsonrpc10.py 1970-01-01 00:00:00 +0000 | |||
316 | +++ frameworks/python/src/ladon/interfaces/jsonrpc10.py 2012-10-26 11:52:28 +0000 | |||
317 | @@ -0,0 +1,205 @@ | |||
318 | 1 | # -*- coding: utf-8 -*- | ||
319 | 2 | |||
320 | 3 | from ladon.interfaces.base import BaseInterface,ServiceDescriptor,BaseRequestHandler,BaseResponseHandler,BaseFaultHandler | ||
321 | 4 | from ladon.interfaces import expose | ||
322 | 5 | from ladon.compat import type_to_jsontype,pytype_support,PORTABLE_STRING | ||
323 | 6 | import json,sys,traceback | ||
324 | 7 | from ladon.exceptions.service import ServiceFault | ||
325 | 8 | from ladon.exceptions.base import LadonException | ||
326 | 9 | |||
327 | 10 | def _add_passback_params(res_dict,passback_dict): | ||
328 | 11 | merged_dict = dict((k,v) for (k,v) in res_dict.items()) | ||
329 | 12 | for k,v in passback_dict.items(): | ||
330 | 13 | merged_dict[k] = v | ||
331 | 14 | return merged_dict | ||
332 | 15 | |||
333 | 16 | class RequestPropFault(ServiceFault): | ||
334 | 17 | def __init__(self,prop, passback_dict): | ||
335 | 18 | self.prop = prop | ||
336 | 19 | self.passback_dict = passback_dict | ||
337 | 20 | super(RequestPropFault,self).__init__('service','Request doesn\'t have "%s" property.' % self.prop, None, 2) | ||
338 | 21 | |||
339 | 22 | def __str__(self): | ||
340 | 23 | return self.faultstring | ||
341 | 24 | |||
342 | 25 | class RequestParamFault(ServiceFault): | ||
343 | 26 | def __init__(self,param, passback_dict): | ||
344 | 27 | self.param = param | ||
345 | 28 | self.passback_dict = passback_dict | ||
346 | 29 | super(RequestParamFault,self).__init__('service','Request doesn\'t have "%s" parameter.' % self.param, None, 2) | ||
347 | 30 | |||
348 | 31 | def __str__(self): | ||
349 | 32 | return self.faultstring | ||
350 | 33 | |||
351 | 34 | class JSONRPCServiceDescriptor(ServiceDescriptor): | ||
352 | 35 | javascript_type_map = type_to_jsontype | ||
353 | 36 | version = '1.0' | ||
354 | 37 | _content_type = 'application/json' | ||
355 | 38 | |||
356 | 39 | def generate(self,servicename,servicenumber,typemanager,methodlist,service_url,encoding): | ||
357 | 40 | type_dict = typemanager.type_dict | ||
358 | 41 | type_order = typemanager.type_order | ||
359 | 42 | |||
360 | 43 | def map_type(typ): | ||
361 | 44 | if typ in JSONRPCServiceDescriptor.javascript_type_map: | ||
362 | 45 | return JSONRPCServiceDescriptor.javascript_type_map[typ] | ||
363 | 46 | else: | ||
364 | 47 | return typ.__name__ | ||
365 | 48 | |||
366 | 49 | desc = { | ||
367 | 50 | 'servicename': servicename, | ||
368 | 51 | 'url': service_url, | ||
369 | 52 | 'type': 'jsonrpc/description', | ||
370 | 53 | 'version': self.version, | ||
371 | 54 | 'types': {}, | ||
372 | 55 | 'methods': {} | ||
373 | 56 | } | ||
374 | 57 | |||
375 | 58 | types = desc['types'] | ||
376 | 59 | for typ in type_order: | ||
377 | 60 | if type(typ)==dict: | ||
378 | 61 | desc_type = {} | ||
379 | 62 | types[typ['name']] = desc_type | ||
380 | 63 | for k,v,props in typ['attributes']: | ||
381 | 64 | if type(v)==list: | ||
382 | 65 | desc_type_val = [map_type(v[0])] | ||
383 | 66 | else: | ||
384 | 67 | desc_type_val = map_type(v) | ||
385 | 68 | desc_type[k] = desc_type_val | ||
386 | 69 | |||
387 | 70 | methods = desc['methods'] | ||
388 | 71 | for m in methodlist: | ||
389 | 72 | desc_mparams = {} | ||
390 | 73 | order = 1 | ||
391 | 74 | desc_method = {'params': desc_mparams, 'doc_lines': m._method_doc} | ||
392 | 75 | methods[m.name()] = desc_method | ||
393 | 76 | for arg in m.args(): | ||
394 | 77 | if [list,tuple].count(type(arg['type'])): | ||
395 | 78 | desc_param_type = [map_type(arg['type'][0])] | ||
396 | 79 | else: | ||
397 | 80 | desc_param_type = map_type(arg['type']) | ||
398 | 81 | desc_mparams[arg['name']] = { | ||
399 | 82 | "type": desc_param_type, | ||
400 | 83 | "def_order": order, | ||
401 | 84 | "optional": arg['optional'], | ||
402 | 85 | } | ||
403 | 86 | if 'doc' in arg: | ||
404 | 87 | desc_mparams[arg['name']]["doc_lines"] = arg['doc'] | ||
405 | 88 | else: | ||
406 | 89 | desc_mparams[arg['name']]["doc_lines"] = [] | ||
407 | 90 | order += 1 | ||
408 | 91 | |||
409 | 92 | if [list,tuple].count(type(m._rtype)): | ||
410 | 93 | desc_rtype = [map_type(m._rtype[0])] | ||
411 | 94 | else: | ||
412 | 95 | desc_rtype = map_type(m._rtype) | ||
413 | 96 | desc_method['ret_info'] = { | ||
414 | 97 | 'type': desc_rtype, | ||
415 | 98 | 'doc_lines': m._rtype_doc | ||
416 | 99 | } | ||
417 | 100 | |||
418 | 101 | if sys.version_info[0]>=3: | ||
419 | 102 | return json.dumps(desc) | ||
420 | 103 | return json.dumps(desc,encoding=encoding) | ||
421 | 104 | |||
422 | 105 | class JSONRPCRequestHandler(BaseRequestHandler): | ||
423 | 106 | def parse_request(self,json_body,sinfo,encoding): | ||
424 | 107 | def parse_number(x): | ||
425 | 108 | return PORTABLE_STRING(x) | ||
426 | 109 | def parse_constant(x): | ||
427 | 110 | if x=='null': | ||
428 | 111 | return PORTABLE_STRING("None") | ||
429 | 112 | return PORTABLE_STRING(x) | ||
430 | 113 | req_dict = json.loads(PORTABLE_STRING(json_body,encoding), parse_int=parse_number, parse_float=parse_number, \ | ||
431 | 114 | parse_constant=parse_constant) | ||
432 | 115 | passback_dict = self.get_passback_params(req_dict) | ||
433 | 116 | if 'method' not in req_dict: | ||
434 | 117 | raise RequestPropFault('method',passback_dict) | ||
435 | 118 | if 'params' not in req_dict: | ||
436 | 119 | raise RequestPropFault('params',passback_dict) | ||
437 | 120 | if 'id' not in req_dict: | ||
438 | 121 | raise RequestPropFault('id',passback_dict) | ||
439 | 122 | minfo = sinfo.methods[req_dict['method']] | ||
440 | 123 | if (req_dict['params'] is None or len(req_dict['params']) == 0) and len(minfo.args()) > 0: | ||
441 | 124 | raise RequestParamFault(minfo.args()[0]['name'],passback_dict) | ||
442 | 125 | else: | ||
443 | 126 | for arg in minfo.args(): | ||
444 | 127 | isgiven = False | ||
445 | 128 | for param in req_dict['params']: | ||
446 | 129 | if param == arg['name']: | ||
447 | 130 | isgiven = True | ||
448 | 131 | if not isgiven: | ||
449 | 132 | raise RequestParamFault(arg['name'],passback_dict) | ||
450 | 133 | req_dict['args'] = req_dict['params'] | ||
451 | 134 | req_dict['methodname'] = req_dict['method'] | ||
452 | 135 | del req_dict['params'] | ||
453 | 136 | del req_dict['method'] | ||
454 | 137 | return req_dict | ||
455 | 138 | |||
456 | 139 | def get_passback_params(self, req_dict): | ||
457 | 140 | if 'id' in req_dict: | ||
458 | 141 | return {'id': req_dict['id']} | ||
459 | 142 | else: | ||
460 | 143 | return {} | ||
461 | 144 | |||
462 | 145 | class JSONRPCResponseHandler(BaseResponseHandler): | ||
463 | 146 | _content_type = 'application/json' | ||
464 | 147 | _stringify_res_dict = False | ||
465 | 148 | |||
466 | 149 | def build_response(self,res_dict,sinfo,encoding): | ||
467 | 150 | res_dict['error'] = None | ||
468 | 151 | del res_dict['servicenumber'] | ||
469 | 152 | del res_dict['servicename'] | ||
470 | 153 | del res_dict['method'] | ||
471 | 154 | return json.dumps(res_dict,ensure_ascii=False).encode(encoding) | ||
472 | 155 | |||
473 | 156 | class JSONRPCFaultHandler(BaseFaultHandler): | ||
474 | 157 | _content_type = 'application/json' | ||
475 | 158 | _stringify_res_dict = False | ||
476 | 159 | |||
477 | 160 | def build_fault_response(self,service_exc,sinfo,methodname,encoding): | ||
478 | 161 | if service_exc.detail: | ||
479 | 162 | detail = service_exc.detail | ||
480 | 163 | else: | ||
481 | 164 | detail = traceback.format_exc() | ||
482 | 165 | detail = detail.replace('\r\n','\n').split('\n') | ||
483 | 166 | fault_dict = { | ||
484 | 167 | 'result': None, | ||
485 | 168 | 'error': { | ||
486 | 169 | 'code': service_exc.faultcode, | ||
487 | 170 | 'string': service_exc.faultstring, | ||
488 | 171 | 'detail': detail, | ||
489 | 172 | 'filename': service_exc.mod, | ||
490 | 173 | 'lineno': service_exc.lineno | ||
491 | 174 | }, | ||
492 | 175 | } | ||
493 | 176 | if hasattr(service_exc,'passback_dict'): | ||
494 | 177 | fault_dict = _add_passback_params(fault_dict,service_exc.passback_dict) | ||
495 | 178 | return json.dumps(fault_dict,ensure_ascii=False).encode(encoding) | ||
496 | 179 | |||
497 | 180 | @expose | ||
498 | 181 | class JSONRPCInterface(BaseInterface): | ||
499 | 182 | def __init__(self,sinfo,**kw): | ||
500 | 183 | def_kw = { | ||
501 | 184 | 'service_descriptor': JSONRPCServiceDescriptor, | ||
502 | 185 | 'request_handler': JSONRPCRequestHandler, | ||
503 | 186 | 'response_handler': JSONRPCResponseHandler, | ||
504 | 187 | 'fault_handler': JSONRPCFaultHandler} | ||
505 | 188 | def_kw.update(kw) | ||
506 | 189 | BaseInterface.__init__(self,sinfo,**def_kw) | ||
507 | 190 | |||
508 | 191 | @staticmethod | ||
509 | 192 | def _interface_name(): | ||
510 | 193 | return 'jsonrpc10' | ||
511 | 194 | |||
512 | 195 | @staticmethod | ||
513 | 196 | def _accept_basetype(typ): | ||
514 | 197 | return pytype_support.count(typ)>0 | ||
515 | 198 | |||
516 | 199 | @staticmethod | ||
517 | 200 | def _accept_list(): | ||
518 | 201 | return True | ||
519 | 202 | |||
520 | 203 | @staticmethod | ||
521 | 204 | def _accept_dict(): | ||
522 | 205 | return False | ||
523 | 0 | \ No newline at end of file | 206 | \ No newline at end of file |
524 | 1 | 207 | ||
525 | === modified file 'frameworks/python/src/ladon/server/dispatcher.py' | |||
526 | --- frameworks/python/src/ladon/server/dispatcher.py 2012-09-05 12:39:33 +0000 | |||
527 | +++ frameworks/python/src/ladon/server/dispatcher.py 2012-10-26 11:52:28 +0000 | |||
528 | @@ -178,6 +178,8 @@ | |||
529 | 178 | res_dict = self.result_to_dict(method,result,tc,export_dict['response_attachments'],log_line=log_line) | 178 | res_dict = self.result_to_dict(method,result,tc,export_dict['response_attachments'],log_line=log_line) |
530 | 179 | if 'mirror' in req_dict: | 179 | if 'mirror' in req_dict: |
531 | 180 | res_dict['reflection'] = req_dict['mirror'] | 180 | res_dict['reflection'] = req_dict['mirror'] |
532 | 181 | if 'id' in req_dict: | ||
533 | 182 | res_dict['id'] = req_dict['id'] | ||
534 | 181 | response = self.iface.build_response(res_dict,encoding=self.response_encoding) | 183 | response = self.iface.build_response(res_dict,encoding=self.response_encoding) |
535 | 182 | if self.logging: | 184 | if self.logging: |
536 | 183 | debug('\t%s' % ('\t'.join(log_line))) | 185 | debug('\t%s' % ('\t'.join(log_line))) |
537 | 184 | 186 | ||
538 | === modified file 'frameworks/python/src/ladon/types/typemanager.py' | |||
539 | --- frameworks/python/src/ladon/types/typemanager.py 2012-01-28 14:40:18 +0000 | |||
540 | +++ frameworks/python/src/ladon/types/typemanager.py 2012-10-26 11:52:28 +0000 | |||
541 | @@ -9,7 +9,9 @@ | |||
542 | 9 | this_cls_attrs = dir(cls) | 9 | this_cls_attrs = dir(cls) |
543 | 10 | res = [] | 10 | res = [] |
544 | 11 | for attr in this_cls_attrs: | 11 | for attr in this_cls_attrs: |
546 | 12 | if base_attrs.count(attr) or (exclude_methods and inspect.ismethod(getattr(cls,attr))): | 12 | # attr == '__qualname__' |
547 | 13 | # Python 3.3 __qualname__ (PEP 3155 -- Qualified name for classes and functions) fix | ||
548 | 14 | if base_attrs.count(attr) or (exclude_methods and inspect.ismethod(getattr(cls,attr))) or attr == '__qualname__': | ||
549 | 13 | continue | 15 | continue |
550 | 14 | res += [attr] | 16 | res += [attr] |
551 | 15 | return res | 17 | return res |
552 | 16 | 18 | ||
553 | === modified file 'frameworks/python/tests/servicerunner.py' | |||
554 | --- frameworks/python/tests/servicerunner.py 2012-10-15 15:05:09 +0000 | |||
555 | +++ frameworks/python/tests/servicerunner.py 2012-10-26 11:52:28 +0000 | |||
556 | @@ -9,6 +9,7 @@ | |||
557 | 9 | 'stringtests', | 9 | 'stringtests', |
558 | 10 | 'typetests', | 10 | 'typetests', |
559 | 11 | 'attachmenttests', | 11 | 'attachmenttests', |
560 | 12 | 'jsonrpc10', | ||
561 | 12 | 'collectiontest' | 13 | 'collectiontest' |
562 | 13 | ] | 14 | ] |
563 | 14 | 15 | ||
564 | 15 | 16 | ||
565 | === added file 'frameworks/python/tests/services/jsonrpc10.py' | |||
566 | --- frameworks/python/tests/services/jsonrpc10.py 1970-01-01 00:00:00 +0000 | |||
567 | +++ frameworks/python/tests/services/jsonrpc10.py 2012-10-26 11:52:28 +0000 | |||
568 | @@ -0,0 +1,54 @@ | |||
569 | 1 | from ladon.ladonizer import ladonize | ||
570 | 2 | from ladon.compat import PORTABLE_BYTES,PORTABLE_STRING | ||
571 | 3 | import binascii | ||
572 | 4 | import sys | ||
573 | 5 | |||
574 | 6 | class JsonPrc10Service(object): | ||
575 | 7 | @ladonize(rtype=PORTABLE_STRING) | ||
576 | 8 | def return_string(self): | ||
577 | 9 | if sys.version_info[0]>=3: | ||
578 | 10 | return 'Yo!!!' | ||
579 | 11 | return PORTABLE_STRING('Yo!!!','utf-8') | ||
580 | 12 | |||
581 | 13 | @ladonize(rtype=int) | ||
582 | 14 | def return_int(self): | ||
583 | 15 | return 11 | ||
584 | 16 | |||
585 | 17 | @ladonize(rtype=float) | ||
586 | 18 | def return_float(self): | ||
587 | 19 | return 11.11 | ||
588 | 20 | |||
589 | 21 | @ladonize(rtype=bool) | ||
590 | 22 | def return_bool(self): | ||
591 | 23 | return True | ||
592 | 24 | |||
593 | 25 | @ladonize(rtype=PORTABLE_BYTES) | ||
594 | 26 | def return_bytes(self): | ||
595 | 27 | if sys.version_info[0]>=3: | ||
596 | 28 | return PORTABLE_BYTES('Yo!!!','utf-8') | ||
597 | 29 | return 'Yo!!!' | ||
598 | 30 | |||
599 | 31 | @ladonize(PORTABLE_STRING, rtype=PORTABLE_STRING) | ||
600 | 32 | def passback_string(self,arg): | ||
601 | 33 | return arg | ||
602 | 34 | |||
603 | 35 | @ladonize(int,rtype=int) | ||
604 | 36 | def passback_int(self,arg): | ||
605 | 37 | return arg | ||
606 | 38 | |||
607 | 39 | @ladonize(float,rtype=float) | ||
608 | 40 | def passback_float(self,arg): | ||
609 | 41 | return arg | ||
610 | 42 | |||
611 | 43 | @ladonize(bool,rtype=bool) | ||
612 | 44 | def passback_bool(self,arg): | ||
613 | 45 | return arg | ||
614 | 46 | |||
615 | 47 | @ladonize(PORTABLE_BYTES,rtype=PORTABLE_BYTES) | ||
616 | 48 | def passback_bytes(self,arg): | ||
617 | 49 | return arg | ||
618 | 50 | |||
619 | 51 | @ladonize(int,float,PORTABLE_STRING,rtype=bool) | ||
620 | 52 | def params(self,arg0,arg1,arg2): | ||
621 | 53 | return True | ||
622 | 54 | |||
623 | 0 | \ No newline at end of file | 55 | \ No newline at end of file |
624 | 1 | 56 | ||
625 | === added file 'frameworks/python/tests/testjsonrpc10.py' | |||
626 | --- frameworks/python/tests/testjsonrpc10.py 1970-01-01 00:00:00 +0000 | |||
627 | +++ frameworks/python/tests/testjsonrpc10.py 2012-10-26 11:52:28 +0000 | |||
628 | @@ -0,0 +1,254 @@ | |||
629 | 1 | # -*- coding: utf-8 -*- | ||
630 | 2 | |||
631 | 3 | import unittest | ||
632 | 4 | import servicerunner | ||
633 | 5 | import sys | ||
634 | 6 | import xml.dom.minidom as md | ||
635 | 7 | if sys.version_info[0]>=3: | ||
636 | 8 | from urllib.parse import urlparse,splitport | ||
637 | 9 | from http.client import HTTPConnection, HTTPSConnection | ||
638 | 10 | else: | ||
639 | 11 | from urllib import splitport | ||
640 | 12 | from urlparse import urlparse | ||
641 | 13 | from httplib import HTTPConnection, HTTPSConnection | ||
642 | 14 | import sys,json | ||
643 | 15 | from ladon.compat import PORTABLE_BYTES,PORTABLE_STRING,PORTABLE_STRING_TYPES | ||
644 | 16 | from testladon import HTTPRequestPoster | ||
645 | 17 | from testladon import str_to_portable_string | ||
646 | 18 | import binascii | ||
647 | 19 | |||
648 | 20 | class JsonRpc10Tests(unittest.TestCase): | ||
649 | 21 | |||
650 | 22 | def setUp(self): | ||
651 | 23 | self.post_helper = HTTPRequestPoster('http://localhost:2376/JsonPrc10Service') | ||
652 | 24 | |||
653 | 25 | def test_get_string(self): | ||
654 | 26 | req = {'method':'return_string','params':None,'id':0} | ||
655 | 27 | jreq = json.dumps(req) | ||
656 | 28 | |||
657 | 29 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
658 | 30 | |||
659 | 31 | self.assertEqual(status, 200) | ||
660 | 32 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
661 | 33 | expected_result = str_to_portable_string('Yo!!!') | ||
662 | 34 | self.assertEqual(res['result'], expected_result) | ||
663 | 35 | |||
664 | 36 | def test_get_int(self): | ||
665 | 37 | req = {'method':'return_int','params':None,'id':0} | ||
666 | 38 | jreq = json.dumps(req) | ||
667 | 39 | |||
668 | 40 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
669 | 41 | |||
670 | 42 | self.assertEqual(status, 200) | ||
671 | 43 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
672 | 44 | expected_result = 11 | ||
673 | 45 | self.assertEqual(res['result'], expected_result) | ||
674 | 46 | |||
675 | 47 | def test_get_float(self): | ||
676 | 48 | req = {'method':'return_float','params':None,'id':0} | ||
677 | 49 | jreq = json.dumps(req) | ||
678 | 50 | |||
679 | 51 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
680 | 52 | |||
681 | 53 | self.assertEqual(status, 200) | ||
682 | 54 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
683 | 55 | expected_result = 11.11 | ||
684 | 56 | self.assertEqual(res['result'], expected_result) | ||
685 | 57 | |||
686 | 58 | def test_get_bool(self): | ||
687 | 59 | req = {'method':'return_bool','params':None,'id':0} | ||
688 | 60 | jreq = json.dumps(req) | ||
689 | 61 | |||
690 | 62 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
691 | 63 | |||
692 | 64 | self.assertEqual(status, 200) | ||
693 | 65 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
694 | 66 | expected_result = True | ||
695 | 67 | self.assertEqual(res['result'], expected_result) | ||
696 | 68 | |||
697 | 69 | def test_get_bytes(self): | ||
698 | 70 | req = {'method':'return_bytes','params':None,'id':0} | ||
699 | 71 | jreq = json.dumps(req) | ||
700 | 72 | |||
701 | 73 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
702 | 74 | |||
703 | 75 | self.assertEqual(status, 200) | ||
704 | 76 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
705 | 77 | expected_result = 'Yo!!!' | ||
706 | 78 | self.assertEqual(res['result'], expected_result) | ||
707 | 79 | |||
708 | 80 | def test_passback_string(self): | ||
709 | 81 | val = 'Yo!!!' | ||
710 | 82 | req = {'method':'passback_string','params':{'arg':val},'id':0} | ||
711 | 83 | jreq = json.dumps(req) | ||
712 | 84 | |||
713 | 85 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
714 | 86 | |||
715 | 87 | self.assertEqual(status, 200) | ||
716 | 88 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
717 | 89 | expected_result = str_to_portable_string(val) | ||
718 | 90 | self.assertEqual(res['result'], expected_result) | ||
719 | 91 | |||
720 | 92 | def test_passback_int(self): | ||
721 | 93 | val = 11 | ||
722 | 94 | req = {'method':'passback_int','params':{'arg':val},'id':0} | ||
723 | 95 | jreq = json.dumps(req) | ||
724 | 96 | |||
725 | 97 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
726 | 98 | |||
727 | 99 | self.assertEqual(status, 200) | ||
728 | 100 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
729 | 101 | expected_result = val | ||
730 | 102 | self.assertEqual(res['result'], expected_result) | ||
731 | 103 | |||
732 | 104 | def test_passback_float(self): | ||
733 | 105 | val = 11.11 | ||
734 | 106 | req = {'method':'passback_float','params':{'arg':val},'id':0} | ||
735 | 107 | jreq = json.dumps(req) | ||
736 | 108 | |||
737 | 109 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
738 | 110 | |||
739 | 111 | self.assertEqual(status, 200) | ||
740 | 112 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
741 | 113 | expected_result = val | ||
742 | 114 | self.assertEqual(res['result'], expected_result) | ||
743 | 115 | |||
744 | 116 | def test_passback_bool(self): | ||
745 | 117 | val = True | ||
746 | 118 | req = {'method':'passback_bool','params':{'arg':val},'id':0} | ||
747 | 119 | jreq = json.dumps(req) | ||
748 | 120 | |||
749 | 121 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
750 | 122 | |||
751 | 123 | self.assertEqual(status, 200) | ||
752 | 124 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
753 | 125 | expected_result = val | ||
754 | 126 | self.assertEqual(res['result'], expected_result) | ||
755 | 127 | |||
756 | 128 | def test_passback_bytes(self): | ||
757 | 129 | val = 'Yo!!!' | ||
758 | 130 | req = {'method':'passback_bytes','params':{'arg':val},'id':0} | ||
759 | 131 | jreq = json.dumps(req) | ||
760 | 132 | |||
761 | 133 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
762 | 134 | |||
763 | 135 | self.assertEqual(status, 200) | ||
764 | 136 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
765 | 137 | self.assertEqual(res['result'], val) | ||
766 | 138 | |||
767 | 139 | def test_validate_request_response_structure(self): | ||
768 | 140 | req = {} | ||
769 | 141 | jreq = json.dumps(req) | ||
770 | 142 | |||
771 | 143 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
772 | 144 | |||
773 | 145 | self.assertEqual(status, 200) | ||
774 | 146 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
775 | 147 | |||
776 | 148 | self.assertIs(type(res['error']), dict) | ||
777 | 149 | self.assertTrue('id' is not res) | ||
778 | 150 | self.assertIs(res['result'], None) | ||
779 | 151 | self.assertTrue('"method"' in res['error']['string']) | ||
780 | 152 | |||
781 | 153 | req = {'method':'passback_string'} | ||
782 | 154 | jreq = json.dumps(req) | ||
783 | 155 | |||
784 | 156 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
785 | 157 | |||
786 | 158 | self.assertEqual(status, 200) | ||
787 | 159 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
788 | 160 | self.assertIs(type(res['error']), dict) | ||
789 | 161 | self.assertTrue('id' is not res) | ||
790 | 162 | self.assertIs(res['result'], None) | ||
791 | 163 | self.assertTrue('"params"' in res['error']['string']) | ||
792 | 164 | |||
793 | 165 | req = {'method':'passback_string','params':None} | ||
794 | 166 | jreq = json.dumps(req) | ||
795 | 167 | |||
796 | 168 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
797 | 169 | |||
798 | 170 | self.assertEqual(status, 200) | ||
799 | 171 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
800 | 172 | self.assertIs(type(res['error']), dict) | ||
801 | 173 | self.assertTrue('id' is not res) | ||
802 | 174 | self.assertIs(res['result'], None) | ||
803 | 175 | self.assertTrue('"id"' in res['error']['string']) | ||
804 | 176 | |||
805 | 177 | req = {'method':'passback_string','params':None,'id':0} | ||
806 | 178 | jreq = json.dumps(req) | ||
807 | 179 | |||
808 | 180 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
809 | 181 | |||
810 | 182 | self.assertEqual(status, 200) | ||
811 | 183 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
812 | 184 | |||
813 | 185 | self.assertIs(type(res['error']), dict) | ||
814 | 186 | self.assertEqual(res['id'], '0') | ||
815 | 187 | self.assertIs(res['result'], None) | ||
816 | 188 | |||
817 | 189 | req = {'method':'passback_string','params':None,'id':'0'} | ||
818 | 190 | jreq = json.dumps(req) | ||
819 | 191 | |||
820 | 192 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
821 | 193 | |||
822 | 194 | self.assertEqual(status, 200) | ||
823 | 195 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
824 | 196 | |||
825 | 197 | self.assertIs(type(res['error']), dict) | ||
826 | 198 | self.assertEqual(res['id'], '0') | ||
827 | 199 | self.assertIs(res['result'], None) | ||
828 | 200 | |||
829 | 201 | req = {'method':'passback_string','params':{'arg': 'Yo!!!'},'id':0} | ||
830 | 202 | jreq = json.dumps(req) | ||
831 | 203 | |||
832 | 204 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
833 | 205 | |||
834 | 206 | self.assertEqual(status, 200) | ||
835 | 207 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
836 | 208 | |||
837 | 209 | self.assertIs(res['error'], None) | ||
838 | 210 | self.assertEqual(res['id'], '0') | ||
839 | 211 | self.assertEqual(res['result'], 'Yo!!!') | ||
840 | 212 | |||
841 | 213 | req = {'method':'params','params':{},'id':0} | ||
842 | 214 | jreq = json.dumps(req) | ||
843 | 215 | |||
844 | 216 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
845 | 217 | |||
846 | 218 | self.assertEqual(status, 200) | ||
847 | 219 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
848 | 220 | |||
849 | 221 | self.assertIs(type(res['error']), dict) | ||
850 | 222 | self.assertEqual(res['id'], '0') | ||
851 | 223 | self.assertTrue('"arg0"' in res['error']['string']) | ||
852 | 224 | |||
853 | 225 | req = {'method':'params','params':{'arg0':11},'id':0} | ||
854 | 226 | jreq = json.dumps(req) | ||
855 | 227 | |||
856 | 228 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
857 | 229 | |||
858 | 230 | self.assertEqual(status, 200) | ||
859 | 231 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
860 | 232 | |||
861 | 233 | self.assertIs(type(res['error']), dict) | ||
862 | 234 | self.assertEqual(res['id'], '0') | ||
863 | 235 | self.assertTrue('"arg1"' in res['error']['string']) | ||
864 | 236 | |||
865 | 237 | req = {'method':'params','params':{'arg0':11, 'arg1':11.11},'id':0} | ||
866 | 238 | jreq = json.dumps(req) | ||
867 | 239 | |||
868 | 240 | status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') | ||
869 | 241 | |||
870 | 242 | self.assertEqual(status, 200) | ||
871 | 243 | res = json.loads(PORTABLE_STRING(resdata,'utf-8')) | ||
872 | 244 | |||
873 | 245 | self.assertIs(type(res['error']), dict) | ||
874 | 246 | self.assertEqual(res['id'], '0') | ||
875 | 247 | self.assertTrue('"arg2"' in res['error']['string']) | ||
876 | 248 | |||
877 | 249 | if __name__ == '__main__': | ||
878 | 250 | import servicerunner | ||
879 | 251 | servicerunner | ||
880 | 252 | service_thread = servicerunner.serve_test_service(as_thread=True) | ||
881 | 253 | unittest.main(exit=False) | ||
882 | 254 | service_thread.server.shutdown() | ||
883 | 0 | \ No newline at end of file | 255 | \ No newline at end of file |
Please, remove .bzrignore from merge