roger has proposed merging lp:~roger-lp/ladon/trunk into lp:ladon. Requested reviews: Mikhus (mikhus)
For more details, see: https://code.launchpad.net/~roger-lp/ladon/trunk/+merge/131028 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). -- https://code.launchpad.net/~roger-lp/ladon/trunk/+merge/131028 Your team Ladon Developer is subscribed to branch lp:ladon.
=== modified file 'frameworks/python/src/ladon/interfaces/__init__.py' --- frameworks/python/src/ladon/interfaces/__init__.py 2012-05-04 14:43:58 +0000 +++ frameworks/python/src/ladon/interfaces/__init__.py 2012-10-23 15:04:45 +0000 @@ -71,3 +71,4 @@ import ladon.interfaces.soap import ladon.interfaces.soap11 import ladon.interfaces.jsonwsp +import ladon.interfaces.jsonrpc10 === modified file 'frameworks/python/src/ladon/interfaces/base.py' --- frameworks/python/src/ladon/interfaces/base.py 2012-01-06 12:44:37 +0000 +++ frameworks/python/src/ladon/interfaces/base.py 2012-10-23 15:04:45 +0000 @@ -81,6 +81,12 @@ def description_content_type(self): return self._service_descriptor._content_type + + def get_passback_params(self,req_dict): + return self._request_handler.get_passback_params(req_dict) + + def add_passback_params(self,res_dict,passback_dict): + return self._fault_handler.add_passback_params(res_dict,passback_dict) class ServiceDescriptor(object): @@ -108,6 +114,9 @@ def parse_request(self,req,sinfo,encoding): return {} + + def get_passback_params(self,req_dict): + return {} class BaseResponseHandler(object): @@ -127,6 +136,9 @@ def build_response(self,method_result,sinfo,encoding): return '' + + def add_passback_params(self,res_dict,passback_dict): + return res_dict class BaseFaultHandler(object): @@ -145,3 +157,6 @@ def build_fault_response(self,exc,sinfo,methodname,encoding): return '' + + def add_passback_params(self,res_dict,passback_dict): + return res_dict === added file 'frameworks/python/src/ladon/interfaces/jsonrpc10.py' --- frameworks/python/src/ladon/interfaces/jsonrpc10.py 1970-01-01 00:00:00 +0000 +++ frameworks/python/src/ladon/interfaces/jsonrpc10.py 2012-10-23 15:04:45 +0000 @@ -0,0 +1,209 @@ +# -*- coding: utf-8 -*- + +from ladon.interfaces.base import BaseInterface,ServiceDescriptor,BaseRequestHandler,BaseResponseHandler,BaseFaultHandler +from ladon.interfaces import expose +from ladon.compat import type_to_jsontype,pytype_support,PORTABLE_STRING +import json,sys,traceback +from ladon.exceptions.service import ServiceFault +from ladon.exceptions.base import LadonException + +def _add_passback_params(res_dict,passback_dict): + merged_dict = dict((k,v) for (k,v) in res_dict.items()) + for k,v in passback_dict.items(): + merged_dict[k] = v + return merged_dict + +class RequestPropFault(ServiceFault): + def __init__(self,prop, passback_dict): + self.prop = prop + self.passback_dict = passback_dict + super(RequestPropFault,self).__init__('service','Request doesn\'t have "%s" property.' % self.prop, None, 2) + + def __str__(self): + return self.faultstring + +class RequestParamFault(ServiceFault): + def __init__(self,param, passback_dict): + self.param = param + self.passback_dict = passback_dict + super(RequestParamFault,self).__init__('service','Request doesn\'t have "%s" parameter.' % self.param, None, 2) + + def __str__(self): + return self.faultstring + +class JSONRPCServiceDescriptor(ServiceDescriptor): + javascript_type_map = type_to_jsontype + version = '1.0' + _content_type = 'application/json' + + def generate(self,servicename,servicenumber,typemanager,methodlist,service_url,encoding): + type_dict = typemanager.type_dict + type_order = typemanager.type_order + + def map_type(typ): + if typ in JSONRPCServiceDescriptor.javascript_type_map: + return JSONRPCServiceDescriptor.javascript_type_map[typ] + else: + return typ.__name__ + + desc = { + 'servicename': servicename, + 'url': service_url, + 'type': 'jsonrpc/description', + 'version': self.version, + 'types': {}, + 'methods': {} + } + + types = desc['types'] + for typ in type_order: + if type(typ)==dict: + desc_type = {} + types[typ['name']] = desc_type + for k,v,props in typ['attributes']: + if type(v)==list: + desc_type_val = [map_type(v[0])] + else: + desc_type_val = map_type(v) + desc_type[k] = desc_type_val + + methods = desc['methods'] + for m in methodlist: + desc_mparams = {} + order = 1 + desc_method = {'params': desc_mparams, 'doc_lines': m._method_doc} + methods[m.name()] = desc_method + for arg in m.args(): + if [list,tuple].count(type(arg['type'])): + desc_param_type = [map_type(arg['type'][0])] + else: + desc_param_type = map_type(arg['type']) + desc_mparams[arg['name']] = { + "type": desc_param_type, + "def_order": order, + "optional": arg['optional'], + } + if 'doc' in arg: + desc_mparams[arg['name']]["doc_lines"] = arg['doc'] + else: + desc_mparams[arg['name']]["doc_lines"] = [] + order += 1 + + if [list,tuple].count(type(m._rtype)): + desc_rtype = [map_type(m._rtype[0])] + else: + desc_rtype = map_type(m._rtype) + desc_method['ret_info'] = { + 'type': desc_rtype, + 'doc_lines': m._rtype_doc + } + + if sys.version_info[0]>=3: + return json.dumps(desc) + return json.dumps(desc,encoding=encoding) + +class JSONRPCRequestHandler(BaseRequestHandler): + def parse_request(self,json_body,sinfo,encoding): + def parse_number(x): + return PORTABLE_STRING(x) + def parse_constant(x): + if x=='null': + return PORTABLE_STRING("None") + return PORTABLE_STRING(x) + req_dict = json.loads(PORTABLE_STRING(json_body,encoding), parse_int=parse_number, parse_float=parse_number, \ + parse_constant=parse_constant) + passback_dict = self.get_passback_params(req_dict) + if 'method' not in req_dict: + raise RequestPropFault('method',passback_dict) + if 'params' not in req_dict: + raise RequestPropFault('params',passback_dict) + if 'id' not in req_dict: + raise RequestPropFault('id',passback_dict) + minfo = sinfo.methods[req_dict['method']] + if (req_dict['params'] is None or len(req_dict['params']) == 0) and len(minfo.args()) > 0: + raise RequestParamFault(minfo.args()[0]['name'],passback_dict) + else: + for arg in minfo.args(): + isgiven = False + for param in req_dict['params']: + if param == arg['name']: + isgiven = True + if not isgiven: + raise RequestParamFault(arg['name'],passback_dict) + req_dict['args'] = req_dict['params'] + del req_dict['params'] + return req_dict + + def get_passback_params(self, req_dict): + if 'id' in req_dict: + return {'id': req_dict['id']} + else: + return {} + +class JSONRPCResponseHandler(BaseResponseHandler): + _content_type = 'application/json' + _stringify_res_dict = False + + def build_response(self,res_dict,sinfo,encoding): + res_dict['error'] = None + del res_dict['servicenumber'] + del res_dict['servicename'] + del res_dict['method'] + return json.dumps(res_dict,ensure_ascii=False).encode(encoding) + + def add_passback_params(self,res_dict,passback_dict): + return _add_passback_params(res_dict,passback_dict) + +class JSONRPCFaultHandler(BaseFaultHandler): + _content_type = 'application/json' + _stringify_res_dict = False + + def build_fault_response(self,service_exc,sinfo,methodname,encoding): + if service_exc.detail: + detail = service_exc.detail + else: + detail = traceback.format_exc() + detail = detail.replace('\r\n','\n').split('\n') + fault_dict = { + 'result': None, + 'error': { + 'code': service_exc.faultcode, + 'string': service_exc.faultstring, + 'detail': detail, + 'filename': service_exc.mod, + 'lineno': service_exc.lineno + }, + } + if hasattr(service_exc,'passback_dict'): + fault_dict = self.add_passback_params(fault_dict,service_exc.passback_dict) + return json.dumps(fault_dict,ensure_ascii=False).encode(encoding) + + def add_passback_params(self, res_dict, passback_dict): + return _add_passback_params(res_dict,passback_dict) + +@expose +class JSONRPCInterface(BaseInterface): + def __init__(self,sinfo,**kw): + def_kw = { + 'service_descriptor': JSONRPCServiceDescriptor, + 'request_handler': JSONRPCRequestHandler, + 'response_handler': JSONRPCResponseHandler, + 'fault_handler': JSONRPCFaultHandler} + def_kw.update(kw) + BaseInterface.__init__(self,sinfo,**def_kw) + + @staticmethod + def _interface_name(): + return 'jsonrpc10' + + @staticmethod + def _accept_basetype(typ): + return pytype_support.count(typ)>0 + + @staticmethod + def _accept_list(): + return True + + @staticmethod + def _accept_dict(): + return False \ No newline at end of file === modified file 'frameworks/python/src/ladon/server/dispatcher.py' --- frameworks/python/src/ladon/server/dispatcher.py 2012-09-05 12:39:33 +0000 +++ frameworks/python/src/ladon/server/dispatcher.py 2012-10-23 15:04:45 +0000 @@ -37,7 +37,7 @@ self.logging = logging - def call_method(self,method,req_dict,tc,export_dict,log_line): + def call_method(self,methodname,method,req_dict,tc,export_dict,log_line): """ call_method converts the res_dict delivered from an interface to the type of arguments expected by the service method. @@ -83,7 +83,7 @@ #service_module = imp.load_module(mname,file, pathname, description) service_class_instance = getattr(service_module,method.sinfo.servicename)() if self.logging & LOG_REQUEST_ACCESS: - log_line += ['Method:%s.%s' % (method.sinfo.servicename,req_dict['methodname'])] + log_line += ['Method:%s.%s' % (method.sinfo.servicename,methodname)] if self.logging & LOG_REQUEST_DICT: log_line += ['RequestDict:%s' % (str(req_dict))] if self.logging & LOG_EXECUTION_TIME: @@ -91,9 +91,9 @@ if method._has_keywords: kw = {'LADON_METHOD_TC':tc} kw.update(export_dict) - result = getattr(service_class_instance,req_dict['methodname'])(*args,**kw) + result = getattr(service_class_instance,methodname)(*args,**kw) else: - result = getattr(service_class_instance,req_dict['methodname'])(*args) + result = getattr(service_class_instance,methodname)(*args) if self.logging & LOG_EXECUTION_TIME: log_line.insert(0,'ExecutionTime:%s' % str(time.time()-start)) return result @@ -148,7 +148,8 @@ export_dict['response_attachments'] = AttachmentHandler() methodname,method = None,None req_dict = self.iface.parse_request(request_data,encoding=self.response_encoding) - methodname = req_dict['methodname'] + passback_dict = self.iface.get_passback_params(req_dict) + methodname = req_dict['methodname'] if 'methodname' in req_dict else req_dict['method'] method = self.sinst.method(methodname) if not method: raise UndefinedServiceMethod(self.iface._interface_name(),self.sinst.servicename,'Service method "%s" is not declared in service' % methodname) @@ -156,7 +157,7 @@ encoding=method._encoding, allow_unsafe_conversion=method._allow_unsafe_conversion, only_strings_to_unicode=(not self.iface.stringify_res_dict())) - result = self.call_method(method,req_dict,tc,export_dict,log_line) + result = self.call_method(methodname,method,req_dict,tc,export_dict,log_line) except Exception as e: if isinstance(e,ServiceFault): response = self.iface.build_fault_response(e,methodname,encoding=self.response_encoding) @@ -176,9 +177,10 @@ # so the service developer has full control over response headers and data. return result res_dict = self.result_to_dict(method,result,tc,export_dict['response_attachments'],log_line=log_line) + res_dict = self.iface.add_passback_params(res_dict, passback_dict) if 'mirror' in req_dict: res_dict['reflection'] = req_dict['mirror'] response = self.iface.build_response(res_dict,encoding=self.response_encoding) if self.logging: - debug('\t%s' % ('\t'.join(log_line))) + debug('\t%s' % ('\t'.join(log_line))) return response === modified file 'frameworks/python/src/ladon/types/typemanager.py' --- frameworks/python/src/ladon/types/typemanager.py 2012-01-28 14:40:18 +0000 +++ frameworks/python/src/ladon/types/typemanager.py 2012-10-23 15:04:45 +0000 @@ -9,7 +9,9 @@ this_cls_attrs = dir(cls) res = [] for attr in this_cls_attrs: - if base_attrs.count(attr) or (exclude_methods and inspect.ismethod(getattr(cls,attr))): + # attr == '__qualname__' + # Python 3.3 __qualname__ (PEP 3155 -- Qualified name for classes and functions) fix + if base_attrs.count(attr) or (exclude_methods and inspect.ismethod(getattr(cls,attr))) or attr == '__qualname__': continue res += [attr] return res === modified file 'frameworks/python/tests/servicerunner.py' --- frameworks/python/tests/servicerunner.py 2012-10-15 15:05:09 +0000 +++ frameworks/python/tests/servicerunner.py 2012-10-23 15:04:45 +0000 @@ -9,6 +9,7 @@ 'stringtests', 'typetests', 'attachmenttests', + 'jsonrpc10', 'collectiontest' ] === added file 'frameworks/python/tests/services/jsonrpc10.py' --- frameworks/python/tests/services/jsonrpc10.py 1970-01-01 00:00:00 +0000 +++ frameworks/python/tests/services/jsonrpc10.py 2012-10-23 15:04:45 +0000 @@ -0,0 +1,54 @@ +from ladon.ladonizer import ladonize +from ladon.compat import PORTABLE_BYTES,PORTABLE_STRING +import binascii +import sys + +class JsonPrc10Service(object): + @ladonize(rtype=PORTABLE_STRING) + def return_string(self): + if sys.version_info[0]>=3: + return 'Yo!!!' + return PORTABLE_STRING('Yo!!!','utf-8') + + @ladonize(rtype=int) + def return_int(self): + return 11 + + @ladonize(rtype=float) + def return_float(self): + return 11.11 + + @ladonize(rtype=bool) + def return_bool(self): + return True + + @ladonize(rtype=PORTABLE_BYTES) + def return_bytes(self): + if sys.version_info[0]>=3: + return PORTABLE_BYTES('Yo!!!','utf-8') + return 'Yo!!!' + + @ladonize(PORTABLE_STRING, rtype=PORTABLE_STRING) + def passback_string(self,arg): + return arg + + @ladonize(int,rtype=int) + def passback_int(self,arg): + return arg + + @ladonize(float,rtype=float) + def passback_float(self,arg): + return arg + + @ladonize(bool,rtype=bool) + def passback_bool(self,arg): + return arg + + @ladonize(PORTABLE_BYTES,rtype=PORTABLE_BYTES) + def passback_bytes(self,arg): + return arg + + @ladonize(int,float,PORTABLE_STRING,rtype=bool) + def params(self,arg0,arg1,arg2): + return True + \ No newline at end of file === added file 'frameworks/python/tests/testjsonrpc10.py' --- frameworks/python/tests/testjsonrpc10.py 1970-01-01 00:00:00 +0000 +++ frameworks/python/tests/testjsonrpc10.py 2012-10-23 15:04:45 +0000 @@ -0,0 +1,254 @@ +# -*- coding: utf-8 -*- + +import unittest +import servicerunner +import sys +import xml.dom.minidom as md +if sys.version_info[0]>=3: + from urllib.parse import urlparse,splitport + from http.client import HTTPConnection, HTTPSConnection +else: + from urllib import splitport + from urlparse import urlparse + from httplib import HTTPConnection, HTTPSConnection +import sys,json +from ladon.compat import PORTABLE_BYTES,PORTABLE_STRING,PORTABLE_STRING_TYPES +from testladon import HTTPRequestPoster +from testladon import str_to_portable_string +import binascii + +class JsonRpc10Tests(unittest.TestCase): + + def setUp(self): + self.post_helper = HTTPRequestPoster('http://localhost:2376/JsonPrc10Service') + + def test_get_string(self): + req = {'method':'return_string','params':None,'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + expected_result = str_to_portable_string('Yo!!!') + self.assertEqual(res['result'], expected_result) + + def test_get_int(self): + req = {'method':'return_int','params':None,'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + expected_result = 11 + self.assertEqual(res['result'], expected_result) + + def test_get_float(self): + req = {'method':'return_float','params':None,'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + expected_result = 11.11 + self.assertEqual(res['result'], expected_result) + + def test_get_bool(self): + req = {'method':'return_bool','params':None,'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + expected_result = True + self.assertEqual(res['result'], expected_result) + + def test_get_bytes(self): + req = {'method':'return_bytes','params':None,'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + expected_result = 'Yo!!!' + self.assertEqual(res['result'], expected_result) + + def test_passback_string(self): + val = 'Yo!!!' + req = {'method':'passback_string','params':{'arg':val},'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + expected_result = str_to_portable_string(val) + self.assertEqual(res['result'], expected_result) + + def test_passback_int(self): + val = 11 + req = {'method':'passback_int','params':{'arg':val},'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + expected_result = val + self.assertEqual(res['result'], expected_result) + + def test_passback_float(self): + val = 11.11 + req = {'method':'passback_float','params':{'arg':val},'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + expected_result = val + self.assertEqual(res['result'], expected_result) + + def test_passback_bool(self): + val = True + req = {'method':'passback_bool','params':{'arg':val},'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + expected_result = val + self.assertEqual(res['result'], expected_result) + + def test_passback_bytes(self): + val = 'Yo!!!' + req = {'method':'passback_bytes','params':{'arg':val},'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + self.assertEqual(res['result'], val) + + def test_validate_request_response_structure(self): + req = {} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + + self.assertIs(type(res['error']), dict) + self.assertTrue('id' is not res) + self.assertIs(res['result'], None) + self.assertTrue('"method"' in res['error']['string']) + + req = {'method':'passback_string'} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + self.assertIs(type(res['error']), dict) + self.assertTrue('id' is not res) + self.assertIs(res['result'], None) + self.assertTrue('"params"' in res['error']['string']) + + req = {'method':'passback_string','params':None} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + self.assertIs(type(res['error']), dict) + self.assertTrue('id' is not res) + self.assertIs(res['result'], None) + self.assertTrue('"id"' in res['error']['string']) + + req = {'method':'passback_string','params':None,'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + + self.assertIs(type(res['error']), dict) + self.assertEqual(res['id'], '0') + self.assertIs(res['result'], None) + + req = {'method':'passback_string','params':None,'id':'0'} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + + self.assertIs(type(res['error']), dict) + self.assertEqual(res['id'], '0') + self.assertIs(res['result'], None) + + req = {'method':'passback_string','params':{'arg': 'Yo!!!'},'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + + self.assertIs(res['error'], None) + self.assertEqual(res['id'], '0') + self.assertEqual(res['result'], 'Yo!!!') + + req = {'method':'params','params':{},'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + + self.assertIs(type(res['error']), dict) + self.assertEqual(res['id'], '0') + self.assertTrue('"arg0"' in res['error']['string']) + + req = {'method':'params','params':{'arg0':11},'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + + self.assertIs(type(res['error']), dict) + self.assertEqual(res['id'], '0') + self.assertTrue('"arg1"' in res['error']['string']) + + req = {'method':'params','params':{'arg0':11, 'arg1':11.11},'id':0} + jreq = json.dumps(req) + + status,reason,resdata = self.post_helper.post_request(jreq.encode('utf-8'),extra_path='jsonrpc10',encoding='utf-8') + + self.assertEqual(status, 200) + res = json.loads(PORTABLE_STRING(resdata,'utf-8')) + + self.assertIs(type(res['error']), dict) + self.assertEqual(res['id'], '0') + self.assertTrue('"arg2"' in res['error']['string']) + +if __name__ == '__main__': + import servicerunner + servicerunner + service_thread = servicerunner.serve_test_service(as_thread=True) + unittest.main(exit=False) + service_thread.server.shutdown() \ No newline at end of file
-- Mailing list: https://launchpad.net/~ladon-dev-team Post to : ladon-dev-team@lists.launchpad.net Unsubscribe : https://launchpad.net/~ladon-dev-team More help : https://help.launchpad.net/ListHelp