X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ec873c943d71f0d5f13e3398557071448cda6c23..a4027e74873007e3430af3bd77019bcab76f6c04:/wxPython/wx/lib/rpcMixin.py diff --git a/wxPython/wx/lib/rpcMixin.py b/wxPython/wx/lib/rpcMixin.py deleted file mode 100644 index 7a0c3420e3..0000000000 --- a/wxPython/wx/lib/rpcMixin.py +++ /dev/null @@ -1,422 +0,0 @@ -# -# This was modified from rpcMixin.py distributed with wxPython -# -#---------------------------------------------------------------------- -# Name: rpcMixin -# Version: 0.2.0 -# Purpose: provides xmlrpc server functionality for wxPython -# applications via a mixin class -# -# Requires: (1) Python with threading enabled. -# (2) xmlrpclib from PythonWare -# (http://www.pythonware.com/products/xmlrpc/) -# the code was developed and tested using version 0.9.8 -# -# Author: greg Landrum (Landrum@RationalDiscovery.com) -# -# Copyright: (c) 2000, 2001 by Greg Landrum and Rational Discovery LLC -# Licence: wxWindows license -#---------------------------------------------------------------------- -# 12/11/2003 - Jeff Grimmett (grimmtooth@softhome.net) -# -# o 2.5 compatability update. -# o xmlrpcserver not available. -# - -"""provides xmlrpc server functionality for wxPython applications via a mixin class - -**Some Notes:** - - 1) The xmlrpc server runs in a separate thread from the main GUI - application, communication between the two threads using a custom - event (see the Threads demo in the wxPython docs for more info). - - 2) Neither the server nor the client are particularly smart about - checking method names. So it's easy to shoot yourself in the foot - by calling improper methods. It would be pretty easy to add - either a list of allowed methods or a list of forbidden methods. - - 3) Authentication of xmlrpc clients is *not* performed. I think it - would be pretty easy to do this in a hacky way, but I haven't done - it yet. - - 4) See the bottom of this file for an example of using the class. - -**Obligatory disclaimer:** - This is my first crack at both using xmlrpc and multi-threaded - programming, so there could be huge horrible bugs or design - flaws. If you see one, I'd love to hear about them. - -""" - - -""" ChangeLog -23 May 2001: Version bumped to 0.2.0 - Numerous code and design changes - -21 Mar. 2001: Version bumped to 0.1.4 - Updated rpcMixin.OnExternal to support methods with further references - (i.e. now you can do rpcClient.foo.bar() and have it work) - This probably ain't super legal in xmlrpc land, but it works just fine here - and we need it. - -6 Mar. 2001: Version bumped to 0.1.3 - Documentation changes to make this compatible with happydoc - -21 Jan. 2001: Version bumped to 0.1.2 - OnExternal() method in the mixin class now uses getattr() to check if - a desired method is present. It should have been done this way in - the first place. -14 Dec. 2000: Version bumped to 0.1.1 - rearranged locking code and made other changes so that multiple - servers in one application are possible. - -""" - -import new -import SocketServer -import sys -import threading -import xmlrpclib -import xmlrpcserver - -import wx - -rpcPENDING = 0 -rpcDONE = 1 -rpcEXCEPT = 2 - -class RPCRequest: - """A wrapper to use for handling requests and their responses""" - status = rpcPENDING - result = None - -# here's the ID for external events -wxEVT_EXTERNAL_EVENT = wx.NewEventType() -EVT_EXTERNAL_EVENT = wx.PyEventBinder(wxEVT_EXTERNAL_EVENT, 0) - -class ExternalEvent(wx.PyEvent): - """The custom event class used to pass xmlrpc calls from - the server thread into the GUI thread - - """ - def __init__(self,method,args): - wx.PyEvent.__init__(self) - self.SetEventType(wxEVT_EXTERNAL_EVENT) - self.method = method - self.args = args - self.rpcStatus = RPCRequest() - self.rpcStatusLock = threading.Lock() - self.rpcCondVar = threading.Condition() - - def Destroy(self): - self.method=None - self.args=None - self.rpcStatus = None - self.rpcStatusLock = None - self.rpcondVar = None - -class Handler(xmlrpcserver.RequestHandler): - """The handler class that the xmlrpcserver actually calls - when a request comes in. - - """ - def log_message(self,*args): - """ causes the server to stop spewing messages every time a request comes in - - """ - pass - def call(self,method,params): - """When an xmlrpc request comes in, this is the method that - gets called. - - **Arguments** - - - method: name of the method to be called - - - params: arguments to that method - - """ - if method == '_rpcPing': - # we just acknowledge these without processing them - return 'ack' - - # construct the event - evt = ExternalEvent(method,params) - - # update the status variable - evt.rpcStatusLock.acquire() - evt.rpcStatus.status = rpcPENDING - evt.rpcStatusLock.release() - - evt.rpcCondVar.acquire() - # dispatch the event to the GUI - wx.PostEvent(self._app,evt) - - # wait for the GUI to finish - while evt.rpcStatus.status == rpcPENDING: - evt.rpcCondVar.wait() - evt.rpcCondVar.release() - evt.rpcStatusLock.acquire() - if evt.rpcStatus.status == rpcEXCEPT: - # The GUI threw an exception, release the status lock - # and re-raise the exception - evt.rpcStatusLock.release() - raise evt.rpcStatus.result[0],evt.rpcStatus.result[1] - else: - # everything went through without problems - s = evt.rpcStatus.result - - evt.rpcStatusLock.release() - evt.Destroy() - self._app = None - return s - -# this global Event is used to let the server thread -# know when it should quit -stopEvent = threading.Event() -stopEvent.clear() - -class _ServerThread(threading.Thread): - """ this is the Thread class which actually runs the server - - """ - def __init__(self,server,verbose=0): - self._xmlServ = server - threading.Thread.__init__(self,verbose=verbose) - - def stop(self): - stopEvent.set() - - def shouldStop(self): - return stopEvent.isSet() - - def run(self): - while not self.shouldStop(): - self._xmlServ.handle_request() - self._xmlServ = None - -class rpcMixin: - """A mixin class to provide xmlrpc server functionality to wxPython - frames/windows - - If you want to customize this, probably the best idea is to - override the OnExternal method, which is what's invoked when an - RPC is handled. - - """ - - # we'll try a range of ports for the server, this is the size of the - # range to be scanned - nPortsToTry=20 - if sys.platform == 'win32': - defPort = 800 - else: - defPort = 8023 - - def __init__(self,host='',port=-1,verbose=0,portScan=1): - """Constructor - - **Arguments** - - - host: (optional) the hostname for the server - - - port: (optional) the port the server will use - - - verbose: (optional) if set, the server thread will be launched - in verbose mode - - - portScan: (optional) if set, we'll scan across a number of ports - to find one which is avaiable - - """ - if port == -1: - port = self.defPort - self.verbose=verbose - self.Bind(EVT_EXTERNAL_EVENT,self.OnExternal) - if hasattr(self,'OnClose'): - self._origOnClose = self.OnClose - self.Disconnect(-1,-1,wx.EVT_CLOSE_WINDOW) - else: - self._origOnClose = None - self.OnClose = self.RPCOnClose - self.Bind(wx.EVT_CLOSE,self.RPCOnClose) - - tClass = new.classobj('Handler%d'%(port),(Handler,),{}) - tClass._app = self - if portScan: - self.rpcPort = -1 - for i in xrange(self.nPortsToTry): - try: - xmlServ = SocketServer.TCPServer((host,port+i),tClass) - except: - pass - else: - self.rpcPort = port+i - else: - self.rpcPort = port - try: - xmlServ = SocketServer.TCPServer((host,port),tClass) - except: - self.rpcPort = -1 - - if self.rpcPort == -1: - raise 'RPCMixinError','Cannot initialize server' - self.servThread = _ServerThread(xmlServ,verbose=self.verbose) - self.servThread.setName('XML-RPC Server') - self.servThread.start() - - def RPCOnClose(self,event): - """ callback for when the application is closed - - be sure to shutdown the server and the server thread before - leaving - - """ - # by setting the global stopEvent we inform the server thread - # that it's time to shut down. - stopEvent.set() - if event is not None: - # if we came in here from a user event (as opposed to an RPC event), - # then we'll need to kick the server one last time in order - # to get that thread to terminate. do so now - s1 = xmlrpclib.Server('http://localhost:%d'%(self.rpcPort)) - try: - s1._rpcPing() - except: - pass - - if self._origOnClose is not None: - self._origOnClose(event) - - def RPCQuit(self): - """ shuts down everything, including the rpc server - - """ - self.RPCOnClose(None) - def OnExternal(self,event): - """ this is the callback used to handle RPCs - - **Arguments** - - - event: an _ExternalEvent_ sent by the rpc server - - Exceptions are caught and returned in the global _rpcStatus - structure. This allows the xmlrpc server to report the - exception to the client without mucking up any of the delicate - thread stuff. - - """ - event.rpcStatusLock.acquire() - doQuit = 0 - try: - methsplit = event.method.split('.') - meth = self - for piece in methsplit: - meth = getattr(meth,piece) - except AttributeError,msg: - event.rpcStatus.result = 'No Such Method',msg - event.rpcStatus.status = rpcEXCEPT - else: - try: - res = apply(meth,event.args) - except: - import traceback - if self.verbose: traceback.print_exc() - event.rpcStatus.result = sys.exc_info()[:2] - event.rpcStatus.status = rpcEXCEPT - else: - if res is None: - # returning None across the xmlrpc interface is problematic - event.rpcStatus.result = [] - else: - event.rpcStatus.result = res - event.rpcStatus.status = rpcDONE - - event.rpcStatusLock.release() - - # broadcast (using the condition var) that we're done with the event - event.rpcCondVar.acquire() - event.rpcCondVar.notify() - event.rpcCondVar.release() - - -if __name__ == '__main__': - import time - if sys.platform == 'win32': - port = 800 - else: - port = 8023 - - class rpcFrame(wx.Frame,rpcMixin): - """A simple wxFrame with the rpcMixin functionality added - """ - def __init__(self,*args,**kwargs): - """ rpcHost or rpcPort keyword arguments will be passed along to - the xmlrpc server. - """ - mixinArgs = {} - if kwargs.has_key('rpcHost'): - mixinArgs['host'] = kwargs['rpcHost'] - del kwargs['rpcHost'] - if kwargs.has_key('rpcPort'): - mixinArgs['port'] = kwargs['rpcPort'] - del kwargs['rpcPort'] - if kwargs.has_key('rpcPortScan'): - mixinArgs['portScan'] = kwargs['rpcPortScan'] - del kwargs['rpcPortScan'] - - apply(wx.Frame.__init__,(self,)+args,kwargs) - apply(rpcMixin.__init__,(self,),mixinArgs) - - self.Bind(wx.EVT_CHAR,self.OnChar) - - def TestFunc(self,args): - """a demo method""" - return args - - def OnChar(self,event): - key = event.GetKeyCode() - if key == ord('q'): - self.OnQuit(event) - - def OnQuit(self,event): - self.OnClose(event) - - def OnClose(self,event): - self.Destroy() - - - - class MyApp(wx.App): - def OnInit(self): - self.frame = rpcFrame(None, -1, "wxPython RPCDemo", wx.DefaultPosition, - (300,300), rpcHost='localhost',rpcPort=port) - self.frame.Show(True) - return True - - - def testcon(port): - s1 = xmlrpclib.Server('http://localhost:%d'%(port)) - s1.SetTitle('Munged') - s1._rpcPing() - if doQuit: - s1.RPCQuit() - - doQuit = 1 - if len(sys.argv)>1 and sys.argv[1] == '-q': - doQuit = 0 - nT = threading.activeCount() - app = MyApp(0) - activePort = app.frame.rpcPort - t = threading.Thread(target=lambda x=activePort:testcon(x),verbose=0) - t.start() - - app.MainLoop() - # give the threads time to shut down - if threading.activeCount() > nT: - print 'waiting for all threads to terminate' - while threading.activeCount() > nT: - time.sleep(0.5) - -