##// END OF EJS Templates
merge mtexp to trunk - a more robust threaded shell implementation that works with macros, and does not hang IPython if worker thread dies
Ville M. Vainio -
r1087:5882f870 merge
parent child Browse files
Show More
@@ -1,343 +1,343 b''
1 1 """ Extension for bzr command tab completer. Supports comlpeting commands and options
2 2
3 3 Unlike the core IPython, you should note that this extension is under GPL, not BSD.
4 4
5 5 Based on "shell" bzr plugin by Aaron Bentley, license is below. The IPython additions
6 6 are at the bottom of the file, the rest is left untouched.
7 7
8 8 Must be loaded with ip.load('ipy_bzr')
9 9
10 10 """
11 a
11
12 12 # Copyright (C) 2004, 2005 Aaron Bentley
13 13 # <aaron@aaronbentley.com>
14 14 #
15 15 # This program is free software; you can redistribute it and/or modify
16 16 # it under the terms of the GNU General Public License as published by
17 17 # the Free Software Foundation; either version 2 of the License, or
18 18 # (at your option) any later version.
19 19 #
20 20 # This program is distributed in the hope that it will be useful,
21 21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 23 # GNU General Public License for more details.
24 24 #
25 25 # You should have received a copy of the GNU General Public License
26 26 # along with this program; if not, write to the Free Software
27 27 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 28
29 29 import cmd
30 30 from itertools import chain
31 31 import os
32 32 import shlex
33 33 import stat
34 34 import string
35 35 import sys
36 36
37 37 from bzrlib import osutils
38 38 from bzrlib.branch import Branch
39 39 from bzrlib.config import config_dir, ensure_config_dir_exists
40 40 from bzrlib.commands import get_cmd_object, get_all_cmds, get_alias
41 41 from bzrlib.errors import BzrError
42 42 from bzrlib.workingtree import WorkingTree
43 43 import bzrlib.plugin
44 44
45 45
46 46 SHELL_BLACKLIST = set(['rm', 'ls'])
47 47 COMPLETION_BLACKLIST = set(['shell'])
48 48
49 49
50 50 class BlackListedCommand(BzrError):
51 51 def __init__(self, command):
52 52 BzrError.__init__(self, "The command %s is blacklisted for shell use" %
53 53 command)
54 54
55 55
56 56 class CompletionContext(object):
57 57 def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
58 58 self.text = text
59 59 self.command = command
60 60 self.prev_opt = prev_opt
61 61 self.arg_pos = None
62 62
63 63 def get_completions(self):
64 64 try:
65 65 return self.get_completions_or_raise()
66 66 except Exception, e:
67 67 print e, type(e)
68 68 return []
69 69
70 70 def get_option_completions(self):
71 71 try:
72 72 command_obj = get_cmd_object(self.command)
73 73 except BzrError:
74 74 return []
75 75 opts = [o+" " for o in iter_opt_completions(command_obj)]
76 76 return list(filter_completions(opts, self.text))
77 77
78 78 def get_completions_or_raise(self):
79 79 if self.command is None:
80 80 if '/' in self.text:
81 81 iter = iter_executables(self.text)
82 82 else:
83 83 iter = (c+" " for c in iter_command_names() if
84 84 c not in COMPLETION_BLACKLIST)
85 85 return list(filter_completions(iter, self.text))
86 86 if self.prev_opt is None:
87 87 completions = self.get_option_completions()
88 88 if self.command == "cd":
89 89 iter = iter_dir_completions(self.text)
90 90 completions.extend(list(filter_completions(iter, self.text)))
91 91 else:
92 92 iter = iter_file_completions(self.text)
93 93 completions.extend(filter_completions(iter, self.text))
94 94 return completions
95 95
96 96
97 97 class PromptCmd(cmd.Cmd):
98 98
99 99 def __init__(self):
100 100 cmd.Cmd.__init__(self)
101 101 self.prompt = "bzr> "
102 102 try:
103 103 self.tree = WorkingTree.open_containing('.')[0]
104 104 except:
105 105 self.tree = None
106 106 self.set_title()
107 107 self.set_prompt()
108 108 self.identchars += '-'
109 109 ensure_config_dir_exists()
110 110 self.history_file = osutils.pathjoin(config_dir(), 'shell-history')
111 111 readline.set_completer_delims(string.whitespace)
112 112 if os.access(self.history_file, os.R_OK) and \
113 113 os.path.isfile(self.history_file):
114 114 readline.read_history_file(self.history_file)
115 115 self.cwd = os.getcwd()
116 116
117 117 def write_history(self):
118 118 readline.write_history_file(self.history_file)
119 119
120 120 def do_quit(self, args):
121 121 self.write_history()
122 122 raise StopIteration
123 123
124 124 def do_exit(self, args):
125 125 self.do_quit(args)
126 126
127 127 def do_EOF(self, args):
128 128 print
129 129 self.do_quit(args)
130 130
131 131 def postcmd(self, line, bar):
132 132 self.set_title()
133 133 self.set_prompt()
134 134
135 135 def set_prompt(self):
136 136 if self.tree is not None:
137 137 try:
138 138 prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
139 139 self.tree.relpath('.'))
140 140 prompt = " %s:%d/%s" % prompt_data
141 141 except:
142 142 prompt = ""
143 143 else:
144 144 prompt = ""
145 145 self.prompt = "bzr%s> " % prompt
146 146
147 147 def set_title(self, command=None):
148 148 try:
149 149 b = Branch.open_containing('.')[0]
150 150 version = "%s:%d" % (b.nick, b.revno())
151 151 except:
152 152 version = "[no version]"
153 153 if command is None:
154 154 command = ""
155 155 sys.stdout.write(terminal.term_title("bzr %s %s" % (command, version)))
156 156
157 157 def do_cd(self, line):
158 158 if line == "":
159 159 line = "~"
160 160 line = os.path.expanduser(line)
161 161 if os.path.isabs(line):
162 162 newcwd = line
163 163 else:
164 164 newcwd = self.cwd+'/'+line
165 165 newcwd = os.path.normpath(newcwd)
166 166 try:
167 167 os.chdir(newcwd)
168 168 self.cwd = newcwd
169 169 except Exception, e:
170 170 print e
171 171 try:
172 172 self.tree = WorkingTree.open_containing(".")[0]
173 173 except:
174 174 self.tree = None
175 175
176 176 def do_help(self, line):
177 177 self.default("help "+line)
178 178
179 179 def default(self, line):
180 180 args = shlex.split(line)
181 181 alias_args = get_alias(args[0])
182 182 if alias_args is not None:
183 183 args[0] = alias_args.pop(0)
184 184
185 185 commandname = args.pop(0)
186 186 for char in ('|', '<', '>'):
187 187 commandname = commandname.split(char)[0]
188 188 if commandname[-1] in ('|', '<', '>'):
189 189 commandname = commandname[:-1]
190 190 try:
191 191 if commandname in SHELL_BLACKLIST:
192 192 raise BlackListedCommand(commandname)
193 193 cmd_obj = get_cmd_object(commandname)
194 194 except (BlackListedCommand, BzrError):
195 195 return os.system(line)
196 196
197 197 try:
198 198 if too_complicated(line):
199 199 return os.system("bzr "+line)
200 200 else:
201 201 return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
202 202 except BzrError, e:
203 203 print e
204 204 except KeyboardInterrupt, e:
205 205 print "Interrupted"
206 206 except Exception, e:
207 207 # print "Unhandled error:\n%s" % errors.exception_str(e)
208 208 print "Unhandled error:\n%s" % (e)
209 209
210 210
211 211 def completenames(self, text, line, begidx, endidx):
212 212 return CompletionContext(text).get_completions()
213 213
214 214 def completedefault(self, text, line, begidx, endidx):
215 215 """Perform completion for native commands.
216 216
217 217 :param text: The text to complete
218 218 :type text: str
219 219 :param line: The entire line to complete
220 220 :type line: str
221 221 :param begidx: The start of the text in the line
222 222 :type begidx: int
223 223 :param endidx: The end of the text in the line
224 224 :type endidx: int
225 225 """
226 226 (cmd, args, foo) = self.parseline(line)
227 227 if cmd == "bzr":
228 228 cmd = None
229 229 return CompletionContext(text, command=cmd).get_completions()
230 230
231 231
232 232 def run_shell():
233 233 try:
234 234 prompt = PromptCmd()
235 235 try:
236 236 prompt.cmdloop()
237 237 finally:
238 238 prompt.write_history()
239 239 except StopIteration:
240 240 pass
241 241
242 242
243 243 def iter_opt_completions(command_obj):
244 244 for option_name, option in command_obj.options().items():
245 245 yield "--" + option_name
246 246 short_name = option.short_name()
247 247 if short_name:
248 248 yield "-" + short_name
249 249
250 250
251 251 def iter_file_completions(arg, only_dirs = False):
252 252 """Generate an iterator that iterates through filename completions.
253 253
254 254 :param arg: The filename fragment to match
255 255 :type arg: str
256 256 :param only_dirs: If true, match only directories
257 257 :type only_dirs: bool
258 258 """
259 259 cwd = os.getcwd()
260 260 if cwd != "/":
261 261 extras = [".", ".."]
262 262 else:
263 263 extras = []
264 264 (dir, file) = os.path.split(arg)
265 265 if dir != "":
266 266 listingdir = os.path.expanduser(dir)
267 267 else:
268 268 listingdir = cwd
269 269 for file in chain(os.listdir(listingdir), extras):
270 270 if dir != "":
271 271 userfile = dir+'/'+file
272 272 else:
273 273 userfile = file
274 274 if userfile.startswith(arg):
275 275 if os.path.isdir(listingdir+'/'+file):
276 276 userfile+='/'
277 277 yield userfile
278 278 elif not only_dirs:
279 279 yield userfile + ' '
280 280
281 281
282 282 def iter_dir_completions(arg):
283 283 """Generate an iterator that iterates through directory name completions.
284 284
285 285 :param arg: The directory name fragment to match
286 286 :type arg: str
287 287 """
288 288 return iter_file_completions(arg, True)
289 289
290 290
291 291 def iter_command_names(hidden=False):
292 292 for real_cmd_name, cmd_class in get_all_cmds():
293 293 if not hidden and cmd_class.hidden:
294 294 continue
295 295 for name in [real_cmd_name] + cmd_class.aliases:
296 296 # Don't complete on aliases that are prefixes of the canonical name
297 297 if name == real_cmd_name or not real_cmd_name.startswith(name):
298 298 yield name
299 299
300 300
301 301 def iter_executables(path):
302 302 dirname, partial = os.path.split(path)
303 303 for filename in os.listdir(dirname):
304 304 if not filename.startswith(partial):
305 305 continue
306 306 fullpath = os.path.join(dirname, filename)
307 307 mode=os.lstat(fullpath)[stat.ST_MODE]
308 308 if stat.S_ISREG(mode) and 0111 & mode:
309 309 yield fullpath + ' '
310 310
311 311
312 312 def filter_completions(iter, arg):
313 313 return (c for c in iter if c.startswith(arg))
314 314
315 315
316 316 def iter_munged_completions(iter, arg, text):
317 317 for completion in iter:
318 318 completion = str(completion)
319 319 if completion.startswith(arg):
320 320 yield completion[len(arg)-len(text):]+" "
321 321
322 322
323 323 def too_complicated(line):
324 324 for char in '|<>*?':
325 325 if char in line:
326 326 return True
327 327 return False
328 328
329 329
330 330 ### IPython mods start
331 331
332 332 def init_ipython(ip):
333 333 def bzr_completer(self,ev):
334 334 #print "bzr complete"
335 335 tup = ev.line.split(None,2)
336 336 if len(tup) > 2:
337 337 cmd = tup[1]
338 338 else:
339 339 cmd = None
340 340
341 341 return CompletionContext(ev.symbol, command = cmd).get_completions()
342 342 bzrlib.plugin.load_plugins()
343 343 ip.set_hook('complete_command', bzr_completer, str_key = 'bzr')
@@ -1,1216 +1,1218 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Shell classes.
3 3
4 4 All the matplotlib support code was co-developed with John Hunter,
5 5 matplotlib's author.
6 6
7 7 $Id: Shell.py 3024 2008-02-07 15:34:42Z darren.dale $"""
8 8
9 9 #*****************************************************************************
10 10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #*****************************************************************************
15 15
16 16 from IPython import Release
17 17 __author__ = '%s <%s>' % Release.authors['Fernando']
18 18 __license__ = Release.license
19 19
20 20 # Code begins
21 21 # Stdlib imports
22 22 import __builtin__
23 23 import __main__
24 24 import Queue
25 25 import inspect
26 26 import os
27 27 import sys
28 28 import thread
29 29 import threading
30 30 import time
31 31
32 32 from signal import signal, SIGINT
33 33
34 34 try:
35 35 import ctypes
36 36 HAS_CTYPES = True
37 37 except ImportError:
38 38 HAS_CTYPES = False
39 39
40 40 # IPython imports
41 41 import IPython
42 42 from IPython import ultraTB, ipapi
43 43 from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no
44 44 from IPython.iplib import InteractiveShell
45 45 from IPython.ipmaker import make_IPython
46 46 from IPython.Magic import Magic
47 47 from IPython.ipstruct import Struct
48 48
49 49 # Globals
50 50 # global flag to pass around information about Ctrl-C without exceptions
51 51 KBINT = False
52 52
53 53 # global flag to turn on/off Tk support.
54 54 USE_TK = False
55 55
56 56 # ID for the main thread, used for cross-thread exceptions
57 57 MAIN_THREAD_ID = thread.get_ident()
58 58
59 59 # Tag when runcode() is active, for exception handling
60 60 CODE_RUN = None
61 61
62 62 #-----------------------------------------------------------------------------
63 63 # This class is trivial now, but I want to have it in to publish a clean
64 64 # interface. Later when the internals are reorganized, code that uses this
65 65 # shouldn't have to change.
66 66
67 67 class IPShell:
68 68 """Create an IPython instance."""
69 69
70 70 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
71 71 debug=1,shell_class=InteractiveShell):
72 72 self.IP = make_IPython(argv,user_ns=user_ns,
73 73 user_global_ns=user_global_ns,
74 74 debug=debug,shell_class=shell_class)
75 75
76 76 def mainloop(self,sys_exit=0,banner=None):
77 77 self.IP.mainloop(banner)
78 78 if sys_exit:
79 79 sys.exit()
80 80
81 81 #-----------------------------------------------------------------------------
82 82 def kill_embedded(self,parameter_s=''):
83 83 """%kill_embedded : deactivate for good the current embedded IPython.
84 84
85 85 This function (after asking for confirmation) sets an internal flag so that
86 86 an embedded IPython will never activate again. This is useful to
87 87 permanently disable a shell that is being called inside a loop: once you've
88 88 figured out what you needed from it, you may then kill it and the program
89 89 will then continue to run without the interactive shell interfering again.
90 90 """
91 91
92 92 kill = ask_yes_no("Are you sure you want to kill this embedded instance "
93 93 "(y/n)? [y/N] ",'n')
94 94 if kill:
95 95 self.shell.embedded_active = False
96 96 print "This embedded IPython will not reactivate anymore once you exit."
97 97
98 98 class IPShellEmbed:
99 99 """Allow embedding an IPython shell into a running program.
100 100
101 101 Instances of this class are callable, with the __call__ method being an
102 102 alias to the embed() method of an InteractiveShell instance.
103 103
104 104 Usage (see also the example-embed.py file for a running example):
105 105
106 106 ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override])
107 107
108 108 - argv: list containing valid command-line options for IPython, as they
109 109 would appear in sys.argv[1:].
110 110
111 111 For example, the following command-line options:
112 112
113 113 $ ipython -prompt_in1 'Input <\\#>' -colors LightBG
114 114
115 115 would be passed in the argv list as:
116 116
117 117 ['-prompt_in1','Input <\\#>','-colors','LightBG']
118 118
119 119 - banner: string which gets printed every time the interpreter starts.
120 120
121 121 - exit_msg: string which gets printed every time the interpreter exits.
122 122
123 123 - rc_override: a dict or Struct of configuration options such as those
124 124 used by IPython. These options are read from your ~/.ipython/ipythonrc
125 125 file when the Shell object is created. Passing an explicit rc_override
126 126 dict with any options you want allows you to override those values at
127 127 creation time without having to modify the file. This way you can create
128 128 embeddable instances configured in any way you want without editing any
129 129 global files (thus keeping your interactive IPython configuration
130 130 unchanged).
131 131
132 132 Then the ipshell instance can be called anywhere inside your code:
133 133
134 134 ipshell(header='') -> Opens up an IPython shell.
135 135
136 136 - header: string printed by the IPython shell upon startup. This can let
137 137 you know where in your code you are when dropping into the shell. Note
138 138 that 'banner' gets prepended to all calls, so header is used for
139 139 location-specific information.
140 140
141 141 For more details, see the __call__ method below.
142 142
143 143 When the IPython shell is exited with Ctrl-D, normal program execution
144 144 resumes.
145 145
146 146 This functionality was inspired by a posting on comp.lang.python by cmkl
147 147 <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and
148 148 by the IDL stop/continue commands."""
149 149
150 150 def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None,
151 151 user_ns=None):
152 152 """Note that argv here is a string, NOT a list."""
153 153 self.set_banner(banner)
154 154 self.set_exit_msg(exit_msg)
155 155 self.set_dummy_mode(0)
156 156
157 157 # sys.displayhook is a global, we need to save the user's original
158 158 # Don't rely on __displayhook__, as the user may have changed that.
159 159 self.sys_displayhook_ori = sys.displayhook
160 160
161 161 # save readline completer status
162 162 try:
163 163 #print 'Save completer',sys.ipcompleter # dbg
164 164 self.sys_ipcompleter_ori = sys.ipcompleter
165 165 except:
166 166 pass # not nested with IPython
167 167
168 168 self.IP = make_IPython(argv,rc_override=rc_override,
169 169 embedded=True,
170 170 user_ns=user_ns)
171 171
172 172 ip = ipapi.IPApi(self.IP)
173 173 ip.expose_magic("kill_embedded",kill_embedded)
174 174
175 175 # copy our own displayhook also
176 176 self.sys_displayhook_embed = sys.displayhook
177 177 # and leave the system's display hook clean
178 178 sys.displayhook = self.sys_displayhook_ori
179 179 # don't use the ipython crash handler so that user exceptions aren't
180 180 # trapped
181 181 sys.excepthook = ultraTB.FormattedTB(color_scheme = self.IP.rc.colors,
182 182 mode = self.IP.rc.xmode,
183 183 call_pdb = self.IP.rc.pdb)
184 184 self.restore_system_completer()
185 185
186 186 def restore_system_completer(self):
187 187 """Restores the readline completer which was in place.
188 188
189 189 This allows embedded IPython within IPython not to disrupt the
190 190 parent's completion.
191 191 """
192 192
193 193 try:
194 194 self.IP.readline.set_completer(self.sys_ipcompleter_ori)
195 195 sys.ipcompleter = self.sys_ipcompleter_ori
196 196 except:
197 197 pass
198 198
199 199 def __call__(self,header='',local_ns=None,global_ns=None,dummy=None):
200 200 """Activate the interactive interpreter.
201 201
202 202 __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start
203 203 the interpreter shell with the given local and global namespaces, and
204 204 optionally print a header string at startup.
205 205
206 206 The shell can be globally activated/deactivated using the
207 207 set/get_dummy_mode methods. This allows you to turn off a shell used
208 208 for debugging globally.
209 209
210 210 However, *each* time you call the shell you can override the current
211 211 state of dummy_mode with the optional keyword parameter 'dummy'. For
212 212 example, if you set dummy mode on with IPShell.set_dummy_mode(1), you
213 213 can still have a specific call work by making it as IPShell(dummy=0).
214 214
215 215 The optional keyword parameter dummy controls whether the call
216 216 actually does anything. """
217 217
218 218 # If the user has turned it off, go away
219 219 if not self.IP.embedded_active:
220 220 return
221 221
222 222 # Normal exits from interactive mode set this flag, so the shell can't
223 223 # re-enter (it checks this variable at the start of interactive mode).
224 224 self.IP.exit_now = False
225 225
226 226 # Allow the dummy parameter to override the global __dummy_mode
227 227 if dummy or (dummy != 0 and self.__dummy_mode):
228 228 return
229 229
230 230 # Set global subsystems (display,completions) to our values
231 231 sys.displayhook = self.sys_displayhook_embed
232 232 if self.IP.has_readline:
233 233 self.IP.set_completer()
234 234
235 235 if self.banner and header:
236 236 format = '%s\n%s\n'
237 237 else:
238 238 format = '%s%s\n'
239 239 banner = format % (self.banner,header)
240 240
241 241 # Call the embedding code with a stack depth of 1 so it can skip over
242 242 # our call and get the original caller's namespaces.
243 243 self.IP.embed_mainloop(banner,local_ns,global_ns,stack_depth=1)
244 244
245 245 if self.exit_msg:
246 246 print self.exit_msg
247 247
248 248 # Restore global systems (display, completion)
249 249 sys.displayhook = self.sys_displayhook_ori
250 250 self.restore_system_completer()
251 251
252 252 def set_dummy_mode(self,dummy):
253 253 """Sets the embeddable shell's dummy mode parameter.
254 254
255 255 set_dummy_mode(dummy): dummy = 0 or 1.
256 256
257 257 This parameter is persistent and makes calls to the embeddable shell
258 258 silently return without performing any action. This allows you to
259 259 globally activate or deactivate a shell you're using with a single call.
260 260
261 261 If you need to manually"""
262 262
263 263 if dummy not in [0,1,False,True]:
264 264 raise ValueError,'dummy parameter must be boolean'
265 265 self.__dummy_mode = dummy
266 266
267 267 def get_dummy_mode(self):
268 268 """Return the current value of the dummy mode parameter.
269 269 """
270 270 return self.__dummy_mode
271 271
272 272 def set_banner(self,banner):
273 273 """Sets the global banner.
274 274
275 275 This banner gets prepended to every header printed when the shell
276 276 instance is called."""
277 277
278 278 self.banner = banner
279 279
280 280 def set_exit_msg(self,exit_msg):
281 281 """Sets the global exit_msg.
282 282
283 283 This exit message gets printed upon exiting every time the embedded
284 284 shell is called. It is None by default. """
285 285
286 286 self.exit_msg = exit_msg
287 287
288 288 #-----------------------------------------------------------------------------
289 289 if HAS_CTYPES:
290 290 # Add async exception support. Trick taken from:
291 291 # http://sebulba.wikispaces.com/recipe+thread2
292 292 def _async_raise(tid, exctype):
293 293 """raises the exception, performs cleanup if needed"""
294 294 if not inspect.isclass(exctype):
295 295 raise TypeError("Only types can be raised (not instances)")
296 296 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
297 297 ctypes.py_object(exctype))
298 298 if res == 0:
299 299 raise ValueError("invalid thread id")
300 300 elif res != 1:
301 301 # """if it returns a number greater than one, you're in trouble,
302 302 # and you should call it again with exc=NULL to revert the effect"""
303 303 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
304 304 raise SystemError("PyThreadState_SetAsyncExc failed")
305 305
306 306 def sigint_handler (signum,stack_frame):
307 307 """Sigint handler for threaded apps.
308 308
309 309 This is a horrible hack to pass information about SIGINT _without_
310 310 using exceptions, since I haven't been able to properly manage
311 311 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
312 312 done (or at least that's my understanding from a c.l.py thread where
313 313 this was discussed)."""
314 314
315 315 global KBINT
316 316
317 317 if CODE_RUN:
318 318 _async_raise(MAIN_THREAD_ID,KeyboardInterrupt)
319 319 else:
320 320 KBINT = True
321 321 print '\nKeyboardInterrupt - Press <Enter> to continue.',
322 322 Term.cout.flush()
323 323
324 324 else:
325 325 def sigint_handler (signum,stack_frame):
326 326 """Sigint handler for threaded apps.
327 327
328 328 This is a horrible hack to pass information about SIGINT _without_
329 329 using exceptions, since I haven't been able to properly manage
330 330 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
331 331 done (or at least that's my understanding from a c.l.py thread where
332 332 this was discussed)."""
333 333
334 334 global KBINT
335 335
336 336 print '\nKeyboardInterrupt - Press <Enter> to continue.',
337 337 Term.cout.flush()
338 338 # Set global flag so that runsource can know that Ctrl-C was hit
339 339 KBINT = True
340 340
341 341
342 342 class MTInteractiveShell(InteractiveShell):
343 343 """Simple multi-threaded shell."""
344 344
345 345 # Threading strategy taken from:
346 346 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian
347 347 # McErlean and John Finlay. Modified with corrections by Antoon Pardon,
348 348 # from the pygtk mailing list, to avoid lockups with system calls.
349 349
350 350 # class attribute to indicate whether the class supports threads or not.
351 351 # Subclasses with thread support should override this as needed.
352 352 isthreaded = True
353 353
354 354 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
355 355 user_ns=None,user_global_ns=None,banner2='',**kw):
356 356 """Similar to the normal InteractiveShell, but with threading control"""
357 357
358 358 InteractiveShell.__init__(self,name,usage,rc,user_ns,
359 359 user_global_ns,banner2)
360 360
361 # Locking control variable.
362 self.thread_ready = threading.Condition(threading.RLock())
363 361
364 # A queue to hold the code to be executed. A scalar variable is NOT
365 # enough, because uses like macros cause reentrancy.
362 # A queue to hold the code to be executed.
366 363 self.code_queue = Queue.Queue()
367 364
368 365 # Stuff to do at closing time
369 self._kill = False
370 on_kill = kw.get('on_kill')
371 if on_kill is None:
372 on_kill = []
366 self._kill = None
367 on_kill = kw.get('on_kill', [])
373 368 # Check that all things to kill are callable:
374 369 for t in on_kill:
375 370 if not callable(t):
376 371 raise TypeError,'on_kill must be a list of callables'
377 372 self.on_kill = on_kill
378 373 # thread identity of the "worker thread" (that may execute code directly)
379 374 self.worker_ident = None
375
380 376 def runsource(self, source, filename="<input>", symbol="single"):
381 377 """Compile and run some source in the interpreter.
382 378
383 379 Modified version of code.py's runsource(), to handle threading issues.
384 380 See the original for full docstring details."""
385
381
386 382 global KBINT
387 383
388 384 # If Ctrl-C was typed, we reset the flag and return right away
389 385 if KBINT:
390 386 KBINT = False
391 387 return False
388
389 if self._kill:
390 # can't queue new code if we are being killed
391 return True
392 392
393 393 try:
394 394 code = self.compile(source, filename, symbol)
395 395 except (OverflowError, SyntaxError, ValueError):
396 396 # Case 1
397 397 self.showsyntaxerror(filename)
398 398 return False
399 399
400 400 if code is None:
401 401 # Case 2
402 402 return True
403 403
404 # Case 3
405 # Store code in queue, so the execution thread can handle it.
406
407 # Note that with macros and other applications, we MAY re-enter this
408 # section, so we have to acquire the lock with non-blocking semantics,
409 # else we deadlock.
410
411 404 # shortcut - if we are in worker thread, or the worker thread is not running,
412 405 # execute directly (to allow recursion and prevent deadlock if code is run early
413 406 # in IPython construction)
414 407
415 408 if self.worker_ident is None or self.worker_ident == thread.get_ident():
416 409 InteractiveShell.runcode(self,code)
417 410 return
418
419 got_lock = self.thread_ready.acquire(blocking=False)
420 self.code_queue.put(code)
421 if got_lock:
422 self.thread_ready.wait() # Wait until processed in timeout interval
423 self.thread_ready.release()
424 411
412 # Case 3
413 # Store code in queue, so the execution thread can handle it.
414
415 completed_ev, received_ev = threading.Event(), threading.Event()
416
417 self.code_queue.put((code,completed_ev, received_ev))
418 # first make sure the message was received, with timeout
419 received_ev.wait(5)
420 if not received_ev.isSet():
421 # the mainloop is dead, start executing code directly
422 print "Warning: Timeout for mainloop thread exceeded"
423 print "switching to nonthreaded mode (until mainloop wakes up again)"
424 self.worker_ident = None
425 else:
426 completed_ev.wait()
425 427 return False
426 428
427 429 def runcode(self):
428 430 """Execute a code object.
429 431
430 432 Multithreaded wrapper around IPython's runcode()."""
431
433
432 434 global CODE_RUN
433 # lock thread-protected stuff
435
436 # we are in worker thread, stash out the id for runsource()
434 437 self.worker_ident = thread.get_ident()
435 got_lock = self.thread_ready.acquire()
436 438
437 439 if self._kill:
438 440 print >>Term.cout, 'Closing threads...',
439 441 Term.cout.flush()
440 442 for tokill in self.on_kill:
441 443 tokill()
442 444 print >>Term.cout, 'Done.'
445 # allow kill() to return
446 self._kill.set()
447 return True
443 448
444 449 # Install sigint handler. We do it every time to ensure that if user
445 450 # code modifies it, we restore our own handling.
446 451 try:
447 452 signal(SIGINT,sigint_handler)
448 453 except SystemError:
449 454 # This happens under Windows, which seems to have all sorts
450 455 # of problems with signal handling. Oh well...
451 456 pass
452 457
453 458 # Flush queue of pending code by calling the run methood of the parent
454 459 # class with all items which may be in the queue.
455 460 code_to_run = None
456 461 while 1:
457 462 try:
458 code_to_run = self.code_queue.get_nowait()
463 code_to_run, completed_ev, received_ev = self.code_queue.get_nowait()
459 464 except Queue.Empty:
460 465 break
466 received_ev.set()
467
461 468 # Exceptions need to be raised differently depending on which
462 469 # thread is active. This convoluted try/except is only there to
463 470 # protect against asynchronous exceptions, to ensure that a KBINT
464 471 # at the wrong time doesn't deadlock everything. The global
465 472 # CODE_TO_RUN is set to true/false as close as possible to the
466 473 # runcode() call, so that the KBINT handler is correctly informed.
467 474 try:
468 475 try:
469 476 CODE_RUN = True
470 477 InteractiveShell.runcode(self,code_to_run)
471 478 except KeyboardInterrupt:
472 479 print "Keyboard interrupted in mainloop"
473 480 while not self.code_queue.empty():
474 self.code_queue.get_nowait()
481 code, ev1,ev2 = self.code_queue.get_nowait()
482 ev1.set()
483 ev2.set()
475 484 break
476 485 finally:
477 if got_lock:
478 CODE_RUN = False
479
480 # We're done with thread-protected variables
481 if code_to_run is not None:
482 self.thread_ready.notify()
483 self.thread_ready.release()
484
485 # We're done...
486 CODE_RUN = False
486 CODE_RUN = False
487 # allow runsource() return from wait
488 completed_ev.set()
489
490
487 491 # This MUST return true for gtk threading to work
488 492 return True
489 493
490 494 def kill(self):
491 495 """Kill the thread, returning when it has been shut down."""
492 got_lock = self.thread_ready.acquire(False)
493 self._kill = True
494 if got_lock:
495 self.thread_ready.release()
496 self._kill = threading.Event()
497 self._kill.wait()
496 498
497 499 class MatplotlibShellBase:
498 500 """Mixin class to provide the necessary modifications to regular IPython
499 501 shell classes for matplotlib support.
500 502
501 503 Given Python's MRO, this should be used as the FIRST class in the
502 504 inheritance hierarchy, so that it overrides the relevant methods."""
503 505
504 506 def _matplotlib_config(self,name,user_ns):
505 507 """Return items needed to setup the user's shell with matplotlib"""
506 508
507 509 # Initialize matplotlib to interactive mode always
508 510 import matplotlib
509 511 from matplotlib import backends
510 512 matplotlib.interactive(True)
511 513
512 514 def use(arg):
513 515 """IPython wrapper for matplotlib's backend switcher.
514 516
515 517 In interactive use, we can not allow switching to a different
516 518 interactive backend, since thread conflicts will most likely crash
517 519 the python interpreter. This routine does a safety check first,
518 520 and refuses to perform a dangerous switch. It still allows
519 521 switching to non-interactive backends."""
520 522
521 523 if arg in backends.interactive_bk and arg != self.mpl_backend:
522 524 m=('invalid matplotlib backend switch.\n'
523 525 'This script attempted to switch to the interactive '
524 526 'backend: `%s`\n'
525 527 'Your current choice of interactive backend is: `%s`\n\n'
526 528 'Switching interactive matplotlib backends at runtime\n'
527 529 'would crash the python interpreter, '
528 530 'and IPython has blocked it.\n\n'
529 531 'You need to either change your choice of matplotlib backend\n'
530 532 'by editing your .matplotlibrc file, or run this script as a \n'
531 533 'standalone file from the command line, not using IPython.\n' %
532 534 (arg,self.mpl_backend) )
533 535 raise RuntimeError, m
534 536 else:
535 537 self.mpl_use(arg)
536 538 self.mpl_use._called = True
537 539
538 540 self.matplotlib = matplotlib
539 541 self.mpl_backend = matplotlib.rcParams['backend']
540 542
541 543 # we also need to block switching of interactive backends by use()
542 544 self.mpl_use = matplotlib.use
543 545 self.mpl_use._called = False
544 546 # overwrite the original matplotlib.use with our wrapper
545 547 matplotlib.use = use
546 548
547 549 # This must be imported last in the matplotlib series, after
548 550 # backend/interactivity choices have been made
549 551 import matplotlib.pylab as pylab
550 552 self.pylab = pylab
551 553
552 554 self.pylab.show._needmain = False
553 555 # We need to detect at runtime whether show() is called by the user.
554 556 # For this, we wrap it into a decorator which adds a 'called' flag.
555 557 self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive)
556 558
557 559 # Build a user namespace initialized with matplotlib/matlab features.
558 560 user_ns = IPython.ipapi.make_user_ns(user_ns)
559 561
560 562 exec ("import matplotlib\n"
561 563 "import matplotlib.pylab as pylab\n") in user_ns
562 564
563 565 # Build matplotlib info banner
564 566 b="""
565 567 Welcome to pylab, a matplotlib-based Python environment.
566 568 For more information, type 'help(pylab)'.
567 569 """
568 570 return user_ns,b
569 571
570 572 def mplot_exec(self,fname,*where,**kw):
571 573 """Execute a matplotlib script.
572 574
573 575 This is a call to execfile(), but wrapped in safeties to properly
574 576 handle interactive rendering and backend switching."""
575 577
576 578 #print '*** Matplotlib runner ***' # dbg
577 579 # turn off rendering until end of script
578 580 isInteractive = self.matplotlib.rcParams['interactive']
579 581 self.matplotlib.interactive(False)
580 582 self.safe_execfile(fname,*where,**kw)
581 583 self.matplotlib.interactive(isInteractive)
582 584 # make rendering call now, if the user tried to do it
583 585 if self.pylab.draw_if_interactive.called:
584 586 self.pylab.draw()
585 587 self.pylab.draw_if_interactive.called = False
586 588
587 589 # if a backend switch was performed, reverse it now
588 590 if self.mpl_use._called:
589 591 self.matplotlib.rcParams['backend'] = self.mpl_backend
590 592
591 593 def magic_run(self,parameter_s=''):
592 594 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
593 595
594 596 # Fix the docstring so users see the original as well
595 597 magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__,
596 598 "\n *** Modified %run for Matplotlib,"
597 599 " with proper interactive handling ***")
598 600
599 601 # Now we provide 2 versions of a matplotlib-aware IPython base shells, single
600 602 # and multithreaded. Note that these are meant for internal use, the IPShell*
601 603 # classes below are the ones meant for public consumption.
602 604
603 605 class MatplotlibShell(MatplotlibShellBase,InteractiveShell):
604 606 """Single-threaded shell with matplotlib support."""
605 607
606 608 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
607 609 user_ns=None,user_global_ns=None,**kw):
608 610 user_ns,b2 = self._matplotlib_config(name,user_ns)
609 611 InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
610 612 banner2=b2,**kw)
611 613
612 614 class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell):
613 615 """Multi-threaded shell with matplotlib support."""
614 616
615 617 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
616 618 user_ns=None,user_global_ns=None, **kw):
617 619 user_ns,b2 = self._matplotlib_config(name,user_ns)
618 620 MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
619 621 banner2=b2,**kw)
620 622
621 623 #-----------------------------------------------------------------------------
622 624 # Utility functions for the different GUI enabled IPShell* classes.
623 625
624 626 def get_tk():
625 627 """Tries to import Tkinter and returns a withdrawn Tkinter root
626 628 window. If Tkinter is already imported or not available, this
627 629 returns None. This function calls `hijack_tk` underneath.
628 630 """
629 631 if not USE_TK or sys.modules.has_key('Tkinter'):
630 632 return None
631 633 else:
632 634 try:
633 635 import Tkinter
634 636 except ImportError:
635 637 return None
636 638 else:
637 639 hijack_tk()
638 640 r = Tkinter.Tk()
639 641 r.withdraw()
640 642 return r
641 643
642 644 def hijack_tk():
643 645 """Modifies Tkinter's mainloop with a dummy so when a module calls
644 646 mainloop, it does not block.
645 647
646 648 """
647 649 def misc_mainloop(self, n=0):
648 650 pass
649 651 def tkinter_mainloop(n=0):
650 652 pass
651 653
652 654 import Tkinter
653 655 Tkinter.Misc.mainloop = misc_mainloop
654 656 Tkinter.mainloop = tkinter_mainloop
655 657
656 658 def update_tk(tk):
657 659 """Updates the Tkinter event loop. This is typically called from
658 660 the respective WX or GTK mainloops.
659 661 """
660 662 if tk:
661 663 tk.update()
662 664
663 665 def hijack_wx():
664 666 """Modifies wxPython's MainLoop with a dummy so user code does not
665 667 block IPython. The hijacked mainloop function is returned.
666 668 """
667 669 def dummy_mainloop(*args, **kw):
668 670 pass
669 671
670 672 try:
671 673 import wx
672 674 except ImportError:
673 675 # For very old versions of WX
674 676 import wxPython as wx
675 677
676 678 ver = wx.__version__
677 679 orig_mainloop = None
678 680 if ver[:3] >= '2.5':
679 681 import wx
680 682 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
681 683 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
682 684 else: raise AttributeError('Could not find wx core module')
683 685 orig_mainloop = core.PyApp_MainLoop
684 686 core.PyApp_MainLoop = dummy_mainloop
685 687 elif ver[:3] == '2.4':
686 688 orig_mainloop = wx.wxc.wxPyApp_MainLoop
687 689 wx.wxc.wxPyApp_MainLoop = dummy_mainloop
688 690 else:
689 691 warn("Unable to find either wxPython version 2.4 or >= 2.5.")
690 692 return orig_mainloop
691 693
692 694 def hijack_gtk():
693 695 """Modifies pyGTK's mainloop with a dummy so user code does not
694 696 block IPython. This function returns the original `gtk.mainloop`
695 697 function that has been hijacked.
696 698 """
697 699 def dummy_mainloop(*args, **kw):
698 700 pass
699 701 import gtk
700 702 if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main
701 703 else: orig_mainloop = gtk.mainloop
702 704 gtk.mainloop = dummy_mainloop
703 705 gtk.main = dummy_mainloop
704 706 return orig_mainloop
705 707
706 708 def hijack_qt():
707 709 """Modifies PyQt's mainloop with a dummy so user code does not
708 710 block IPython. This function returns the original
709 711 `qt.qApp.exec_loop` function that has been hijacked.
710 712 """
711 713 def dummy_mainloop(*args, **kw):
712 714 pass
713 715 import qt
714 716 orig_mainloop = qt.qApp.exec_loop
715 717 qt.qApp.exec_loop = dummy_mainloop
716 718 qt.QApplication.exec_loop = dummy_mainloop
717 719 return orig_mainloop
718 720
719 721 def hijack_qt4():
720 722 """Modifies PyQt4's mainloop with a dummy so user code does not
721 723 block IPython. This function returns the original
722 724 `QtGui.qApp.exec_` function that has been hijacked.
723 725 """
724 726 def dummy_mainloop(*args, **kw):
725 727 pass
726 728 from PyQt4 import QtGui, QtCore
727 729 orig_mainloop = QtGui.qApp.exec_
728 730 QtGui.qApp.exec_ = dummy_mainloop
729 731 QtGui.QApplication.exec_ = dummy_mainloop
730 732 QtCore.QCoreApplication.exec_ = dummy_mainloop
731 733 return orig_mainloop
732 734
733 735 #-----------------------------------------------------------------------------
734 736 # The IPShell* classes below are the ones meant to be run by external code as
735 737 # IPython instances. Note that unless a specific threading strategy is
736 738 # desired, the factory function start() below should be used instead (it
737 739 # selects the proper threaded class).
738 740
739 741 class IPThread(threading.Thread):
740 742 def run(self):
741 743 self.IP.mainloop(self._banner)
742 744 self.IP.kill()
743 745
744 746 class IPShellGTK(IPThread):
745 747 """Run a gtk mainloop() in a separate thread.
746 748
747 749 Python commands can be passed to the thread where they will be executed.
748 750 This is implemented by periodically checking for passed code using a
749 751 GTK timeout callback."""
750 752
751 753 TIMEOUT = 100 # Millisecond interval between timeouts.
752 754
753 755 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
754 756 debug=1,shell_class=MTInteractiveShell):
755 757
756 758 import gtk
757 759
758 760 self.gtk = gtk
759 761 self.gtk_mainloop = hijack_gtk()
760 762
761 763 # Allows us to use both Tk and GTK.
762 764 self.tk = get_tk()
763 765
764 766 if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit
765 767 else: mainquit = self.gtk.mainquit
766 768
767 769 self.IP = make_IPython(argv,user_ns=user_ns,
768 770 user_global_ns=user_global_ns,
769 771 debug=debug,
770 772 shell_class=shell_class,
771 773 on_kill=[mainquit])
772 774
773 775 # HACK: slot for banner in self; it will be passed to the mainloop
774 776 # method only and .run() needs it. The actual value will be set by
775 777 # .mainloop().
776 778 self._banner = None
777 779
778 780 threading.Thread.__init__(self)
779 781
780 782 def mainloop(self,sys_exit=0,banner=None):
781 783
782 784 self._banner = banner
783 785
784 786 if self.gtk.pygtk_version >= (2,4,0):
785 787 import gobject
786 788 gobject.idle_add(self.on_timer)
787 789 else:
788 790 self.gtk.idle_add(self.on_timer)
789 791
790 792 if sys.platform != 'win32':
791 793 try:
792 794 if self.gtk.gtk_version[0] >= 2:
793 795 self.gtk.gdk.threads_init()
794 796 except AttributeError:
795 797 pass
796 798 except RuntimeError:
797 799 error('Your pyGTK likely has not been compiled with '
798 800 'threading support.\n'
799 801 'The exception printout is below.\n'
800 802 'You can either rebuild pyGTK with threads, or '
801 803 'try using \n'
802 804 'matplotlib with a different backend (like Tk or WX).\n'
803 805 'Note that matplotlib will most likely not work in its '
804 806 'current state!')
805 807 self.IP.InteractiveTB()
806 808
807 809 self.start()
808 810 self.gtk.gdk.threads_enter()
809 811 self.gtk_mainloop()
810 812 self.gtk.gdk.threads_leave()
811 813 self.join()
812 814
813 815 def on_timer(self):
814 816 """Called when GTK is idle.
815 817
816 818 Must return True always, otherwise GTK stops calling it"""
817 819
818 820 update_tk(self.tk)
819 821 self.IP.runcode()
820 822 time.sleep(0.01)
821 823 return True
822 824
823 825
824 826 class IPShellWX(IPThread):
825 827 """Run a wx mainloop() in a separate thread.
826 828
827 829 Python commands can be passed to the thread where they will be executed.
828 830 This is implemented by periodically checking for passed code using a
829 831 GTK timeout callback."""
830 832
831 833 TIMEOUT = 100 # Millisecond interval between timeouts.
832 834
833 835 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
834 836 debug=1,shell_class=MTInteractiveShell):
835 837
836 838 self.IP = make_IPython(argv,user_ns=user_ns,
837 839 user_global_ns=user_global_ns,
838 840 debug=debug,
839 841 shell_class=shell_class,
840 842 on_kill=[self.wxexit])
841 843
842 844 wantedwxversion=self.IP.rc.wxversion
843 845 if wantedwxversion!="0":
844 846 try:
845 847 import wxversion
846 848 except ImportError:
847 849 error('The wxversion module is needed for WX version selection')
848 850 else:
849 851 try:
850 852 wxversion.select(wantedwxversion)
851 853 except:
852 854 self.IP.InteractiveTB()
853 855 error('Requested wxPython version %s could not be loaded' %
854 856 wantedwxversion)
855 857
856 858 import wx
857 859
858 860 threading.Thread.__init__(self)
859 861 self.wx = wx
860 862 self.wx_mainloop = hijack_wx()
861 863
862 864 # Allows us to use both Tk and GTK.
863 865 self.tk = get_tk()
864 866
865 867 # HACK: slot for banner in self; it will be passed to the mainloop
866 868 # method only and .run() needs it. The actual value will be set by
867 869 # .mainloop().
868 870 self._banner = None
869 871
870 872 self.app = None
871 873
872 874 def wxexit(self, *args):
873 875 if self.app is not None:
874 876 self.app.agent.timer.Stop()
875 877 self.app.ExitMainLoop()
876 878
877 879 def mainloop(self,sys_exit=0,banner=None):
878 880
879 881 self._banner = banner
880 882
881 883 self.start()
882 884
883 885 class TimerAgent(self.wx.MiniFrame):
884 886 wx = self.wx
885 887 IP = self.IP
886 888 tk = self.tk
887 889 def __init__(self, parent, interval):
888 890 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
889 891 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
890 892 size=(100, 100),style=style)
891 893 self.Show(False)
892 894 self.interval = interval
893 895 self.timerId = self.wx.NewId()
894 896
895 897 def StartWork(self):
896 898 self.timer = self.wx.Timer(self, self.timerId)
897 899 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
898 900 self.timer.Start(self.interval)
899 901
900 902 def OnTimer(self, event):
901 903 update_tk(self.tk)
902 904 self.IP.runcode()
903 905
904 906 class App(self.wx.App):
905 907 wx = self.wx
906 908 TIMEOUT = self.TIMEOUT
907 909 def OnInit(self):
908 910 'Create the main window and insert the custom frame'
909 911 self.agent = TimerAgent(None, self.TIMEOUT)
910 912 self.agent.Show(False)
911 913 self.agent.StartWork()
912 914 return True
913 915
914 916 self.app = App(redirect=False)
915 917 self.wx_mainloop(self.app)
916 918 self.join()
917 919
918 920
919 921 class IPShellQt(IPThread):
920 922 """Run a Qt event loop in a separate thread.
921 923
922 924 Python commands can be passed to the thread where they will be executed.
923 925 This is implemented by periodically checking for passed code using a
924 926 Qt timer / slot."""
925 927
926 928 TIMEOUT = 100 # Millisecond interval between timeouts.
927 929
928 930 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
929 931 debug=0, shell_class=MTInteractiveShell):
930 932
931 933 import qt
932 934
933 935 self.exec_loop = hijack_qt()
934 936
935 937 # Allows us to use both Tk and QT.
936 938 self.tk = get_tk()
937 939
938 940 self.IP = make_IPython(argv,
939 941 user_ns=user_ns,
940 942 user_global_ns=user_global_ns,
941 943 debug=debug,
942 944 shell_class=shell_class,
943 945 on_kill=[qt.qApp.exit])
944 946
945 947 # HACK: slot for banner in self; it will be passed to the mainloop
946 948 # method only and .run() needs it. The actual value will be set by
947 949 # .mainloop().
948 950 self._banner = None
949 951
950 952 threading.Thread.__init__(self)
951 953
952 954 def mainloop(self, sys_exit=0, banner=None):
953 955
954 956 import qt
955 957
956 958 self._banner = banner
957 959
958 960 if qt.QApplication.startingUp():
959 961 a = qt.QApplication(sys.argv)
960 962
961 963 self.timer = qt.QTimer()
962 964 qt.QObject.connect(self.timer,
963 965 qt.SIGNAL('timeout()'),
964 966 self.on_timer)
965 967
966 968 self.start()
967 969 self.timer.start(self.TIMEOUT, True)
968 970 while True:
969 971 if self.IP._kill: break
970 972 self.exec_loop()
971 973 self.join()
972 974
973 975 def on_timer(self):
974 976 update_tk(self.tk)
975 977 result = self.IP.runcode()
976 978 self.timer.start(self.TIMEOUT, True)
977 979 return result
978 980
979 981
980 982 class IPShellQt4(IPThread):
981 983 """Run a Qt event loop in a separate thread.
982 984
983 985 Python commands can be passed to the thread where they will be executed.
984 986 This is implemented by periodically checking for passed code using a
985 987 Qt timer / slot."""
986 988
987 989 TIMEOUT = 100 # Millisecond interval between timeouts.
988 990
989 991 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
990 992 debug=0, shell_class=MTInteractiveShell):
991 993
992 994 from PyQt4 import QtCore, QtGui
993 995
994 996 try:
995 997 # present in PyQt4-4.2.1 or later
996 998 QtCore.pyqtRemoveInputHook()
997 999 except AttributeError:
998 1000 pass
999 1001
1000 1002 if QtCore.PYQT_VERSION_STR == '4.3':
1001 1003 warn('''PyQt4 version 4.3 detected.
1002 1004 If you experience repeated threading warnings, please update PyQt4.
1003 1005 ''')
1004 1006
1005 1007 self.exec_ = hijack_qt4()
1006 1008
1007 1009 # Allows us to use both Tk and QT.
1008 1010 self.tk = get_tk()
1009 1011
1010 1012 self.IP = make_IPython(argv,
1011 1013 user_ns=user_ns,
1012 1014 user_global_ns=user_global_ns,
1013 1015 debug=debug,
1014 1016 shell_class=shell_class,
1015 1017 on_kill=[QtGui.qApp.exit])
1016 1018
1017 1019 # HACK: slot for banner in self; it will be passed to the mainloop
1018 1020 # method only and .run() needs it. The actual value will be set by
1019 1021 # .mainloop().
1020 1022 self._banner = None
1021 1023
1022 1024 threading.Thread.__init__(self)
1023 1025
1024 1026 def mainloop(self, sys_exit=0, banner=None):
1025 1027
1026 1028 from PyQt4 import QtCore, QtGui
1027 1029
1028 1030 self._banner = banner
1029 1031
1030 1032 if QtGui.QApplication.startingUp():
1031 1033 a = QtGui.QApplication(sys.argv)
1032 1034
1033 1035 self.timer = QtCore.QTimer()
1034 1036 QtCore.QObject.connect(self.timer,
1035 1037 QtCore.SIGNAL('timeout()'),
1036 1038 self.on_timer)
1037 1039
1038 1040 self.start()
1039 1041 self.timer.start(self.TIMEOUT)
1040 1042 while True:
1041 1043 if self.IP._kill: break
1042 1044 self.exec_()
1043 1045 self.join()
1044 1046
1045 1047 def on_timer(self):
1046 1048 update_tk(self.tk)
1047 1049 result = self.IP.runcode()
1048 1050 self.timer.start(self.TIMEOUT)
1049 1051 return result
1050 1052
1051 1053
1052 1054 # A set of matplotlib public IPython shell classes, for single-threaded (Tk*
1053 1055 # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use.
1054 1056 def _load_pylab(user_ns):
1055 1057 """Allow users to disable pulling all of pylab into the top-level
1056 1058 namespace.
1057 1059
1058 1060 This little utility must be called AFTER the actual ipython instance is
1059 1061 running, since only then will the options file have been fully parsed."""
1060 1062
1061 1063 ip = IPython.ipapi.get()
1062 1064 if ip.options.pylab_import_all:
1063 1065 ip.ex("from matplotlib.pylab import *")
1064 1066 ip.IP.user_config_ns.update(ip.user_ns)
1065 1067
1066 1068
1067 1069 class IPShellMatplotlib(IPShell):
1068 1070 """Subclass IPShell with MatplotlibShell as the internal shell.
1069 1071
1070 1072 Single-threaded class, meant for the Tk* and FLTK* backends.
1071 1073
1072 1074 Having this on a separate class simplifies the external driver code."""
1073 1075
1074 1076 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1075 1077 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
1076 1078 shell_class=MatplotlibShell)
1077 1079 _load_pylab(self.IP.user_ns)
1078 1080
1079 1081 class IPShellMatplotlibGTK(IPShellGTK):
1080 1082 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
1081 1083
1082 1084 Multi-threaded class, meant for the GTK* backends."""
1083 1085
1084 1086 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1085 1087 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
1086 1088 shell_class=MatplotlibMTShell)
1087 1089 _load_pylab(self.IP.user_ns)
1088 1090
1089 1091 class IPShellMatplotlibWX(IPShellWX):
1090 1092 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
1091 1093
1092 1094 Multi-threaded class, meant for the WX* backends."""
1093 1095
1094 1096 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1095 1097 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
1096 1098 shell_class=MatplotlibMTShell)
1097 1099 _load_pylab(self.IP.user_ns)
1098 1100
1099 1101 class IPShellMatplotlibQt(IPShellQt):
1100 1102 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
1101 1103
1102 1104 Multi-threaded class, meant for the Qt* backends."""
1103 1105
1104 1106 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1105 1107 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
1106 1108 shell_class=MatplotlibMTShell)
1107 1109 _load_pylab(self.IP.user_ns)
1108 1110
1109 1111 class IPShellMatplotlibQt4(IPShellQt4):
1110 1112 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
1111 1113
1112 1114 Multi-threaded class, meant for the Qt4* backends."""
1113 1115
1114 1116 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1115 1117 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
1116 1118 shell_class=MatplotlibMTShell)
1117 1119 _load_pylab(self.IP.user_ns)
1118 1120
1119 1121 #-----------------------------------------------------------------------------
1120 1122 # Factory functions to actually start the proper thread-aware shell
1121 1123
1122 1124 def _select_shell(argv):
1123 1125 """Select a shell from the given argv vector.
1124 1126
1125 1127 This function implements the threading selection policy, allowing runtime
1126 1128 control of the threading mode, both for general users and for matplotlib.
1127 1129
1128 1130 Return:
1129 1131 Shell class to be instantiated for runtime operation.
1130 1132 """
1131 1133
1132 1134 global USE_TK
1133 1135
1134 1136 mpl_shell = {'gthread' : IPShellMatplotlibGTK,
1135 1137 'wthread' : IPShellMatplotlibWX,
1136 1138 'qthread' : IPShellMatplotlibQt,
1137 1139 'q4thread' : IPShellMatplotlibQt4,
1138 1140 'tkthread' : IPShellMatplotlib, # Tk is built-in
1139 1141 }
1140 1142
1141 1143 th_shell = {'gthread' : IPShellGTK,
1142 1144 'wthread' : IPShellWX,
1143 1145 'qthread' : IPShellQt,
1144 1146 'q4thread' : IPShellQt4,
1145 1147 'tkthread' : IPShell, # Tk is built-in
1146 1148 }
1147 1149
1148 1150 backends = {'gthread' : 'GTKAgg',
1149 1151 'wthread' : 'WXAgg',
1150 1152 'qthread' : 'QtAgg',
1151 1153 'q4thread' :'Qt4Agg',
1152 1154 'tkthread' :'TkAgg',
1153 1155 }
1154 1156
1155 1157 all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread',
1156 1158 'tkthread'])
1157 1159 user_opts = set([s.replace('-','') for s in argv[:3]])
1158 1160 special_opts = user_opts & all_opts
1159 1161
1160 1162 if 'tk' in special_opts:
1161 1163 USE_TK = True
1162 1164 special_opts.remove('tk')
1163 1165
1164 1166 if 'pylab' in special_opts:
1165 1167
1166 1168 try:
1167 1169 import matplotlib
1168 1170 except ImportError:
1169 1171 error('matplotlib could NOT be imported! Starting normal IPython.')
1170 1172 return IPShell
1171 1173
1172 1174 special_opts.remove('pylab')
1173 1175 # If there's any option left, it means the user wants to force the
1174 1176 # threading backend, else it's auto-selected from the rc file
1175 1177 if special_opts:
1176 1178 th_mode = special_opts.pop()
1177 1179 matplotlib.rcParams['backend'] = backends[th_mode]
1178 1180 else:
1179 1181 backend = matplotlib.rcParams['backend']
1180 1182 if backend.startswith('GTK'):
1181 1183 th_mode = 'gthread'
1182 1184 elif backend.startswith('WX'):
1183 1185 th_mode = 'wthread'
1184 1186 elif backend.startswith('Qt4'):
1185 1187 th_mode = 'q4thread'
1186 1188 elif backend.startswith('Qt'):
1187 1189 th_mode = 'qthread'
1188 1190 else:
1189 1191 # Any other backend, use plain Tk
1190 1192 th_mode = 'tkthread'
1191 1193
1192 1194 return mpl_shell[th_mode]
1193 1195 else:
1194 1196 # No pylab requested, just plain threads
1195 1197 try:
1196 1198 th_mode = special_opts.pop()
1197 1199 except KeyError:
1198 1200 th_mode = 'tkthread'
1199 1201 return th_shell[th_mode]
1200 1202
1201 1203
1202 1204 # This is the one which should be called by external code.
1203 1205 def start(user_ns = None):
1204 1206 """Return a running shell instance, dealing with threading options.
1205 1207
1206 1208 This is a factory function which will instantiate the proper IPython shell
1207 1209 based on the user's threading choice. Such a selector is needed because
1208 1210 different GUI toolkits require different thread handling details."""
1209 1211
1210 1212 shell = _select_shell(sys.argv)
1211 1213 return shell(user_ns = user_ns)
1212 1214
1213 1215 # Some aliases for backwards compatibility
1214 1216 IPythonShell = IPShell
1215 1217 IPythonShellEmbed = IPShellEmbed
1216 1218 #************************ End of file <Shell.py> ***************************
General Comments 0
You need to be logged in to leave comments. Login now