/examples/jsonrpc/public/services/jsonrpc/__init__.py

1""" 2 Copyright (c) 2006 Jan-Klaas Kollhof 3 4 This file is part of jsonrpc. 5 6 jsonrpc is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published by 8 the Free Software Foundation; either version 2.1 of the License, or 9 (at your option) any later version. 10 11 This software 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 14 GNU Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with this software; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19""" 20 21fromthreadingimportEvent,Lock 22 23fromerrorsimport* 24 25fromsimplejsonimportJSONDecoder,JSONEncoder 26 27 28classJSONRPCEncoder(JSONEncoder): 29defdefault(self,obj): 30ifisinstance(obj,JSONRPCError): 31returnobj.__class__.__name__ 32else: 33returnJSONEncoder.default(self,obj) 34 35 36classTimeout(Exception): 37pass 38 39classResponseEvent: 40"""Event which is fired when the response is returned for a request. 41 42 For each request sent this event is created. 43 An application can wait for the event to create a blocking request. 44""" 45def__init__(self): 46self.__evt=Event() 47 48defwaiting(self): 49returnnotself.__evt.isSet() 50 51defwaitForResponse(self,timeOut=None): 52"""blocks until the response arrived or timeout is reached.""" 53self.__evt.wait(timeOut) 54ifself.waiting(): 55raiseTimeout() 56else: 57ifself.response["error"]: 58raiseException(self.response["error"]) 59else: 60returnself.response["result"] 61 62defhandleResponse(self,resp): 63self.response=resp 64self.__evt.set() 65 66 67classSimpleMessageHandler: 68def__init__(self,DecoderClass=JSONDecoder,EncoderClass=JSONRPCEncoder,messageDelimiter=""): 69self.decoder=DecoderClass() 70self.encoder=EncoderClass() 71self.partialData="" 72self.respEvents={} 73self.respLock=Lock() 74self.messageDelimiter=messageDelimiter 75 76defclose(self): 77pass 78 79defsend(self,data): 80pass 81 82defsendMessage(self,msg): 83self.send(self.encoder.encode(msg)+self.messageDelimiter) 84 85defhandlePartialData(self,data): 86data=self.partialData+data.replace("\r","").replace("\n","") 87msgs=[] 88whiledata!="": 89pos=data.find("{") 90if(pos>-1): 91data=data[pos:] 92try: 93(obj,pos)=self.decoder.raw_decode(data) 94data=data[pos:] 95msgs.append(obj) 96except: 97break 98else: 99break100self.partialData=data101102self.handleMessages(msgs)103104defsendNotify(self,name,args):105"""sends a notification object to the peer"""106self.sendMessage({"method":name,"params":args})107108defsendRequest(self,name,args):109"""sends a request to the peer"""110(respEvt,id)=self.newResponseEvent()111self.sendMessage({"id":id,"method":name,"params":args})112returnrespEvt113114defsendResponse(self,id,result,error):115"""sends a response to the peer"""116self.sendMessage({"result":result,"error":error,"id":id})117118defnewResponseEvent(self):119"""creates a response event and adds it to a waiting list120 When the reponse arrives it will be removed from the list. 121"""122respEvt=ResponseEvent()123self.respLock.acquire()124eid=id(respEvt)125self.respEvents[eid]=respEvt126self.respLock.release()127return(respEvt,eid)128129defhandleMessages(self,msgs):130formsginmsgs:131ifmsg.has_key("method")andmsg.has_key("params"):132ifmsg.has_key("id"):133ifmsg["id"]:134self.handleRequest(msg)135else:136self.handleNotification(msg)137else:138self.handleNotification(msg)139elifmsg.has_key("result")andmsg.has_key("error"):140self.handleResponse(msg)141else:#unknown object 142self.sendResponse(None,InvalidJSONMessage())143self.close()144145defhandleResponse(self,resp):146"""handles a response by fireing the response event for the response coming in"""147id=resp["id"]148evt=self.respEvents[id]149del(self.respEvents[id])150evt.handleResponse(resp)151152defhandleRequest(self,request):153pass154defhandleNotification(self,notification):155pass156157158importre159NameAllowedRegExp=re.compile("^[a-zA-Z]\w*$")160defnameAllowed(name):161"""checks if a name is allowed.162"""163ifNameAllowedRegExp.match(name):164return1165else:166return0167168defgetMethodByName(obj,name):169170"""searches for an object with the name given inside the object given.171"obj.child.meth" will return the meth obj.172"""173try:#to get a method by asking the service174obj=obj._getMethodByName(name)175except:176#assumed a childObject is ment 177#split the name from objName.childObjName... -> [objName, childObjName, ...]178#and get all objects up to the last in list with name checking from the service object179names=name.split(".")180fornameinnames:181ifnameAllowed(name):182obj=getattr(obj,name)183else:184raiseMethodNameNotAllowed()185186returnobj187188189190classSimpleServiceHandler(SimpleMessageHandler):191def__init__(self,service,DecoderClass=JSONDecoder,EncoderClass=JSONRPCEncoder,messageDelimiter=""):192self.service=service193SimpleMessageHandler.__init__(self,DecoderClass,EncoderClass,messageDelimiter)194try:195service._newConnection(self)196except:197pass198199defclose(self):200try:201self.service._closingConnection(self)202except:203pass204205defhandleRequest(self,req):206"""handles a request by calling the appropriete method the service exposes"""207name=req["method"]208params=req["params"]209id=req["id"]210obj=None211try:#to get a callable obj 212obj=getMethodByName(self.service,name)213exceptMethodNameNotAllowed,e:214self.sendResponse(id,None,e)215except:216self.sendResponse(id,None,MethodNotFound())217ifobj:218try:#to call the object with parameters219rslt=obj(*params)220self.sendResponse(id,rslt,None)221exceptTypeError:# wrong arguments222#todo what if the TypeError was not thrown directly by the callable obj223s=getTracebackStr()224self.sendResponse(id,None,InvalidMethodParameters())225except:#error inside the callable object226s=getTracebackStr()227self.sendResponse(id,None,s)228229defhandleNotification(self,req):230"""handles a notification request by calling the appropriete method the service exposes"""231name=req["method"]232params=req["params"]233try:#to get a callable obj 234obj=getMethodByName(self.service,name)235rslt=obj(*params)236except:237pass238239240241