]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/rpcMixin.py
44736e6bd8b59be715fa1d96e4dcc2c97e6a64e6
1 #----------------------------------------------------------------------
4 # Purpose: provides xmlrpc server functionality for wxPython
5 # applications via a mixin class
7 # Requires: (1) Python with threading enabled.
8 # (2) xmlrpclib from PythonWare
9 # (http://www.pythonware.com/products/xmlrpc/)
10 # the code was developed and tested using version 0.9.8
12 # Author: greg Landrum (Landrum@RationalDiscovery.com)
14 # Copyright: (c) 2000 by Greg Landrum and Rational Discovery LLC
15 # Licence: wxWindows license
16 #----------------------------------------------------------------------
21 1) The xmlrpc server runs in a separate thread from the main GUI
22 application, communication between the two threads using a custom
23 event (see the Threads demo in the wxPython docs for more info).
25 2) Neither the server nor the client are particularly smart about
26 checking method names. So it's easy to shoot yourself in the foot
27 by calling improper methods. It would be pretty easy to add
28 either a list of allowed methods or a list of forbidden methods.
30 3) Authentication of xmlrpc clients is *not* performed. I think it
31 would be pretty easy to do this in a hacky way, but I haven't done
34 4) The default port number is 800, it's a windows thing... at least
35 it seems like a windows thing to me. Since I'm not being smart
36 about port numbers, you can probably hork yourself arbitrarily by
37 firing up more than one xmlrpc-active frame at the same time, but
40 5) See the bottom of this file for an example of using the class.
42 Obligatory disclaimer:
43 This is my first crack at both using xmlrpc and multi-threaded
44 programming, so there could be huge horrible bugs or design
45 flaws. If you see one, I'd love to hear about them.
49 from wxPython
.wx
import *
58 """A wrapper to use for handling requests and their responses"""
62 # here's the ID for external events
63 wxEVT_EXTERNAL_EVENT
= 25015
64 class ExternalEvent(wxPyEvent
):
65 """The custom event class used to pass xmlrpc calls from
66 the server thread into the GUI thread
68 def __init__(self
,method
,args
):
69 wxPyEvent
.__init
__(self
)
70 self
.SetEventType(wxEVT_EXTERNAL_EVENT
)
73 self
.rpcStatus
= RPCRequest()
74 self
.rpcStatusLock
= Threading
.Lock()
75 self
.rpcCondVar
= Threading
.Condition()
77 def EVT_EXTERNAL_EVENT(win
,func
):
78 win
.Connect(-1,-1,wxEVT_EXTERNAL_EVENT
,func
)
80 class Handler(xmlrpcserver
.RequestHandler
):
81 """The handler class that the xmlrpcserver actually calls
82 when a request comes in.
84 def call(self
,method
,params
):
85 """When an xmlrpc request comes in, this is the method that
89 evt
= ExternalEvent(method
,params
)
91 # update the status variable
92 evt
.rpcStatusLock
.acquire()
93 evt
.rpcStatus
.status
= rpcPENDING
94 evt
.rpcStatusLock
.release()
96 # acquire the condition lock
97 evt
.rpcCondVar
.acquire()
98 # dispatch the event to the GUI
99 wxPostEvent(self
._app
,evt
)
100 # wait for the GUI to finish
101 while evt
.rpcStatus
.status
== rpcPENDING
:
102 evt
.rpcCondVar
.wait()
103 evt
.rpcCondVar
.release()
104 evt
.rpcStatusLock
.acquire()
105 if evt
.rpcStatus
.status
== rpcEXCEPT
:
106 # The GUI threw an exception, release the status lock
107 # and re-raise the exception
108 evt
.rpcStatusLock
.release()
109 raise evt
.rpcStatus
.result
[0],evt
.rpcStatus
.result
[1]
111 # everything went through without problems
112 s
= evt
.rpcStatus
.result
113 evt
.rpcStatusLock
.release()
117 """A mixin class to provide xmlrpc server functionality to wxPython
120 If you want to customize this, probably the best idea is to
121 override the OnExternal method, which is what's invoked when an
125 def __init__(self
,host
='',port
=800):
128 host: (optional) the hostname for the server
129 port: (optional) the port the server will use
131 EVT_EXTERNAL_EVENT(self
,self
.OnExternal
)
132 if hasattr(self
,'OnClose'):
133 self
._origOnClose
= self
.OnClose
135 self
._origOnClose
= None
136 EVT_CLOSE(self
,self
.OnClose
)
138 exec('class Handler%d(Handler): pass'%(port))
139 exec('tClass= Handler%d'%(port))
141 self
._xmlServ
= SocketServer
.TCPServer((host
,port
),tClass
)
142 self
.servThread
= Threading
.Thread(target
=self
._xmlServ
.serve_forever
)
143 self
.servThread
.setDaemon(1)
144 self
.servThread
.start()
146 def OnClose(self
,event
):
147 """ be sure to shutdown the server and the server thread before
151 self
.servThread
= None
152 if self
._origOnClose
is not None:
153 self
._origOnClose
(event
)
155 def OnExternal(self
,event
):
156 """ this is the callback used to handle RPCs
158 Exceptions are caught and returned in the global _rpcStatus
159 structure. This allows the xmlrpc server to report the
160 exception to the client without mucking up any of the delicate
163 event
.rpcStatusLock
.acquire()
165 res
= eval('apply(self.%s,event.args)'%event
.method
)
168 traceback
.print_exc()
169 event
.rpcStatus
.result
= sys
.exc_info()[:2]
170 event
.rpcStatus
.status
= rpcEXCEPT
173 event
.rpcStatus
.result
= []
175 event
.rpcStatus
.result
= res
176 event
.rpcStatus
.status
= rpcDONE
177 event
.rpcStatusLock
.release()
178 event
.rpcCondVar
.acquire()
179 event
.rpcCondVar
.notify()
180 event
.rpcCondVar
.release()
182 if __name__
== '__main__':
186 port
= int(sys
.argv
[1])
188 class rpcFrame(wxFrame
,rpcMixin
):
189 """A simple wxFrame with the rpcMixin functionality added
191 def __init__(self
,*args
,**kwargs
):
192 """ rpcHost or rpcPort keyword arguments will be passed along to
196 if kwargs
.has_key('rpcHost'):
197 mixinArgs
['host'] = kwargs
['rpcHost']
198 del kwargs
['rpcHost']
199 if kwargs
.has_key('rpcPort'):
200 mixinArgs
['port'] = kwargs
['rpcPort']
201 del kwargs
['rpcPort']
203 apply(wxFrame
.__init
__,(self
,)+args
,kwargs
)
204 apply(rpcMixin
.__init
__,(self
,),mixinArgs
)
206 EVT_CHAR(self
,self
.OnChar
)
208 def TestFunc(self
,args
):
212 def OnChar(self
,event
):
213 key
= event
.GetKeyCode()
217 def OnQuit(self
,event
):
220 def OnClose(self
,event
):
225 frame
= rpcFrame(NULL
, -1, "wxPython RPCDemo", wxDefaultPosition
, wxSize(300,300),rpcHost
='localhost',rpcPort
=port
)
229 #self.SetTopWindow(frame)
230 frame2
= rpcFrame(NULL
, -1, "wxPython RPCDemo2", wxDefaultPosition
, wxSize(300,300),rpcHost
='localhost',rpcPort
=port
+1)