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