##// END OF EJS Templates
twshell: do not install gtk2 reactor automatically
Ville M. Vainio -
Show More
@@ -1,280 +1,278 b''
1 1 import sys
2 2
3 from twisted.internet import gtk2reactor
4 gtk2reactor.install()
5
6 3 from twisted.internet import reactor, threads
7 4
8 """ change here to choose the plot shell with the MT option
9 which should cost extra """
10
11 5 from IPython.ipmaker import make_IPython
12 6 from IPython.iplib import InteractiveShell
13 7 from IPython.ipstruct import Struct
14 8 import Queue,thread,threading,signal
15 9 from signal import signal, SIGINT
16 10 from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no
17 11 import shellglobals
18 12
13 def install_gtk2():
14 """ Install gtk2 reactor, needs to be called bef """
15 from twisted.internet import gtk2reactor
16 gtk2reactor.install()
17
19 18
20 19 def hijack_reactor():
21 20 """Modifies Twisted's reactor with a dummy so user code does
22 21 not block IPython. This function returns the original
23 22 'twisted.internet.reactor' that has been hijacked.
24 23
25 24 NOTE: Make sure you call this *AFTER* you've installed
26 25 the reactor of your choice.
27 26 """
28 27 from twisted import internet
29 28 orig_reactor = internet.reactor
30 29
31 30 class DummyReactor(object):
32 31 def run(self):
33 32 pass
34 33 def __getattr__(self, name):
35 34 return getattr(orig_reactor, name)
36 35 def __setattr__(self, name, value):
37 36 return setattr(orig_reactor, name, value)
38 37
39 38 internet.reactor = DummyReactor()
40 39 return orig_reactor
41 40
42 41 class TwistedInteractiveShell(InteractiveShell):
43 42 """Simple multi-threaded shell."""
44 43
45 44 # Threading strategy taken from:
46 45 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian
47 46 # McErlean and John Finlay. Modified with corrections by Antoon Pardon,
48 47 # from the pygtk mailing list, to avoid lockups with system calls.
49 48
50 49 # class attribute to indicate whether the class supports threads or not.
51 50 # Subclasses with thread support should override this as needed.
52 51 isthreaded = True
53 52
54 53 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
55 54 user_ns=None,user_global_ns=None,banner2='',**kw):
56 55 """Similar to the normal InteractiveShell, but with threading control"""
57 56
58 57 InteractiveShell.__init__(self,name,usage,rc,user_ns,
59 58 user_global_ns,banner2)
60 59
61 60
62 61 # A queue to hold the code to be executed.
63 62 self.code_queue = Queue.Queue()
64 63
65 64 # Stuff to do at closing time
66 65 self._kill = None
67 66 on_kill = kw.get('on_kill', [])
68 67 # Check that all things to kill are callable:
69 68 for t in on_kill:
70 69 if not callable(t):
71 70 raise TypeError,'on_kill must be a list of callables'
72 71 self.on_kill = on_kill
73 72 # thread identity of the "worker thread" (that may execute code directly)
74 73 self.worker_ident = None
75 74 self.reactor_started = False
76 75 self.first_run = True
77 76
78 77 def runsource(self, source, filename="<input>", symbol="single"):
79 78 """Compile and run some source in the interpreter.
80 79
81 80 Modified version of code.py's runsource(), to handle threading issues.
82 81 See the original for full docstring details."""
83 82
84 83 # If Ctrl-C was typed, we reset the flag and return right away
85 84 if shellglobals.KBINT:
86 85 shellglobals.KBINT = False
87 86 return False
88 87
89 88 if self._kill:
90 89 # can't queue new code if we are being killed
91 90 return True
92 91
93 92 try:
94 93 code = self.compile(source, filename, symbol)
95 94 except (OverflowError, SyntaxError, ValueError):
96 95 # Case 1
97 96 self.showsyntaxerror(filename)
98 97 return False
99 98
100 99 if code is None:
101 100 # Case 2
102 101 return True
103 102
104 103 # shortcut - if we are in worker thread, or the worker thread is not running,
105 104 # execute directly (to allow recursion and prevent deadlock if code is run early
106 105 # in IPython construction)
107 106
108 107 if (not self.reactor_started or (self.worker_ident is None and not self.first_run)
109 108 or self.worker_ident == thread.get_ident()):
110 109 InteractiveShell.runcode(self,code)
111 110 return
112 111
113 112 # Case 3
114 113 # Store code in queue, so the execution thread can handle it.
115 114
116 115 self.first_run = False
117 116 completed_ev, received_ev = threading.Event(), threading.Event()
118 117
119 118 self.code_queue.put((code,completed_ev, received_ev))
120 119
121 120 reactor.callLater(0.0,self.runcode)
122 121 received_ev.wait(5)
123 122 if not received_ev.isSet():
124 123 # the mainloop is dead, start executing code directly
125 124 print "Warning: Timeout for mainloop thread exceeded"
126 125 print "switching to nonthreaded mode (until mainloop wakes up again)"
127 126 self.worker_ident = None
128 127 else:
129 shellglobals.CURRENT_COMPLETE_EV = completed_ev
130 128 completed_ev.wait()
131 129
132 130 return False
133 131
134 132 def runcode(self):
135 133 """Execute a code object.
136 134
137 135 Multithreaded wrapper around IPython's runcode()."""
138 136
139 137
140 138 # we are in worker thread, stash out the id for runsource()
141 139 self.worker_ident = thread.get_ident()
142 140
143 141 if self._kill:
144 142 print >>Term.cout, 'Closing threads...',
145 143 Term.cout.flush()
146 144 for tokill in self.on_kill:
147 145 tokill()
148 146 print >>Term.cout, 'Done.'
149 147 # allow kill() to return
150 148 self._kill.set()
151 149 return True
152 150
153 151 # Install SIGINT handler. We do it every time to ensure that if user
154 152 # code modifies it, we restore our own handling.
155 153 try:
156 154 pass
157 155 signal(SIGINT,shellglobals.sigint_handler)
158 156 except SystemError:
159 157 # This happens under Windows, which seems to have all sorts
160 158 # of problems with signal handling. Oh well...
161 159 pass
162 160
163 161 # Flush queue of pending code by calling the run methood of the parent
164 162 # class with all items which may be in the queue.
165 163 code_to_run = None
166 164 while 1:
167 165 try:
168 166 code_to_run, completed_ev, received_ev = self.code_queue.get_nowait()
169 167 except Queue.Empty:
170 168 break
171 169 received_ev.set()
172 170
173 171
174 172 # Exceptions need to be raised differently depending on which
175 173 # thread is active. This convoluted try/except is only there to
176 174 # protect against asynchronous exceptions, to ensure that a shellglobals.KBINT
177 175 # at the wrong time doesn't deadlock everything. The global
178 176 # CODE_TO_RUN is set to true/false as close as possible to the
179 177 # runcode() call, so that the KBINT handler is correctly informed.
180 178 try:
181 179 try:
182 180 shellglobals.CODE_RUN = True
183 181 InteractiveShell.runcode(self,code_to_run)
184 182 except KeyboardInterrupt:
185 183 print "Keyboard interrupted in mainloop"
186 184 while not self.code_queue.empty():
187 185 code = self.code_queue.get_nowait()
188 186 break
189 187 finally:
190 188 shellglobals.CODE_RUN = False
191 189 # allow runsource() return from wait
192 190 completed_ev.set()
193 191
194 192 # This MUST return true for gtk threading to work
195 193 return True
196 194
197 195 def kill(self):
198 196 """Kill the thread, returning when it has been shut down."""
199 197 self._kill = threading.Event()
200 198 reactor.callLater(0.0,self.runcode)
201 199 self._kill.wait()
202 200
203 201
204 202
205 203 class IPShellTwisted():
206 204 """Run a Twisted reactor while in an IPython session.
207 205
208 206 Python commands can be passed to the thread where they will be
209 207 executed. This is implemented by periodically checking for
210 208 passed code using a Twisted reactor callback.
211 209 """
212 210
213 211 TIMEOUT = 0.01 # Millisecond interval between reactor runs.
214 212
215 213 def __init__(self, argv=None, user_ns=None, debug=1,
216 214 shell_class=TwistedInteractiveShell):
217 215
218 216 from twisted.internet import reactor
219 217 self.reactor = hijack_reactor()
220 218
221 219 mainquit = self.reactor.stop
222 220
223 221 # Make sure IPython keeps going after reactor stop.
224 222 def reactorstop():
225 223 pass
226 224 self.reactor.stop = reactorstop
227 225 reactorrun_orig = self.reactor.run
228 226 self.quitting = False
229 227 def reactorrun():
230 228 while True and not self.quitting:
231 229 reactorrun_orig()
232 230 self.reactor.run = reactorrun
233 231
234 232 self.IP = make_IPython(argv, user_ns=user_ns, debug=debug,
235 233 shell_class=shell_class,
236 234 on_kill=[mainquit])
237 235
238 236 # threading.Thread.__init__(self)
239 237
240 238 def run(self):
241 239 self.IP.mainloop()
242 240 self.quitting = True
243 241 self.IP.kill()
244 242
245 243 def mainloop(self):
246 244 def mainLoopThreadDeath(r):
247 245 print "mainLoopThreadDeath: ", str(r)
248 246 def spawnMainloopThread():
249 247 d=threads.deferToThread(self.run)
250 248 d.addBoth(mainLoopThreadDeath)
251 249 reactor.callWhenRunning(spawnMainloopThread)
252 250 self.IP.reactor_started = True
253 251 self.reactor.run()
254 252 print "mainloop ending...."
255 253
256 254 exists = True
257 255
258 256
259 257 if __name__ == '__main__':
260 258 # Sample usage.
261 259
262 260 # Create the shell object. This steals twisted.internet.reactor
263 261 # for its own purposes, to make sure you've already installed a
264 262 # reactor of your choice.
265 263 shell = IPShellTwisted(
266 264 argv=[],
267 265 user_ns={'__name__': '__example__',
268 266 'hello': 'world',
269 267 },
270 268 )
271 269
272 270 # Run the mainloop. This runs the actual reactor.run() method.
273 271 # The twisted.internet.reactor object at this point is a dummy
274 272 # object that passes through to the actual reactor, but prevents
275 273 # run() from being called on it again.
276 274 shell.mainloop()
277 275
278 276 # You must exit IPython to terminate your program.
279 277 print 'Goodbye!'
280 278
General Comments 0
You need to be logged in to leave comments. Login now