##// END OF EJS Templates
updating my code to incorporate upstream changes; resolved a merge conflict in IPython/lib/display.py
Greg Caporaso -
r8798:c1e52e56 merge
parent child Browse files
Show More
@@ -0,0 +1,14 b''
1 # encoding: utf-8
2 """Terminal-based IPython entry point.
3 """
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2012, IPython Development Team.
6 #
7 # Distributed under the terms of the Modified BSD License.
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
11
12 from IPython.frontend.terminal.ipapp import launch_new_instance
13
14 launch_new_instance()
@@ -0,0 +1,73 b''
1 import os.path
2
3 import nose.tools as nt
4
5 import IPython.testing.tools as tt
6 from IPython.utils.syspathcontext import prepended_to_syspath
7 from IPython.utils.tempdir import TemporaryDirectory
8
9 ext1_content = """
10 def load_ipython_extension(ip):
11 print("Running ext1 load")
12
13 def unload_ipython_extension(ip):
14 print("Running ext1 unload")
15 """
16
17 ext2_content = """
18 def load_ipython_extension(ip):
19 print("Running ext2 load")
20 """
21
22 def test_extension_loading():
23 em = get_ipython().extension_manager
24 with TemporaryDirectory() as td:
25 ext1 = os.path.join(td, 'ext1.py')
26 with open(ext1, 'w') as f:
27 f.write(ext1_content)
28
29 ext2 = os.path.join(td, 'ext2.py')
30 with open(ext2, 'w') as f:
31 f.write(ext2_content)
32
33 with prepended_to_syspath(td):
34 assert 'ext1' not in em.loaded
35 assert 'ext2' not in em.loaded
36
37 # Load extension
38 with tt.AssertPrints("Running ext1 load"):
39 assert em.load_extension('ext1') is None
40 assert 'ext1' in em.loaded
41
42 # Should refuse to load it again
43 with tt.AssertNotPrints("Running ext1 load"):
44 assert em.load_extension('ext1') == 'already loaded'
45
46 # Reload
47 with tt.AssertPrints("Running ext1 unload"):
48 with tt.AssertPrints("Running ext1 load", suppress=False):
49 em.reload_extension('ext1')
50
51 # Unload
52 with tt.AssertPrints("Running ext1 unload"):
53 assert em.unload_extension('ext1') is None
54
55 # Can't unload again
56 with tt.AssertNotPrints("Running ext1 unload"):
57 assert em.unload_extension('ext1') == 'not loaded'
58 assert em.unload_extension('ext2') == 'not loaded'
59
60 # Load extension 2
61 with tt.AssertPrints("Running ext2 load"):
62 assert em.load_extension('ext2') is None
63
64 # Can't unload this
65 assert em.unload_extension('ext2') == 'no unload function'
66
67 # But can reload it
68 with tt.AssertPrints("Running ext2 load"):
69 em.reload_extension('ext2')
70
71 def test_non_extension():
72 em = get_ipython().extension_manager
73 nt.assert_equal(em.load_extension('sys'), "no load function")
@@ -1,529 +1,557 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Pdb debugger class.
3 Pdb debugger class.
4
4
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 the command line completion of other programs which include this isn't
6 the command line completion of other programs which include this isn't
7 damaged.
7 damaged.
8
8
9 In the future, this class will be expanded with improvements over the standard
9 In the future, this class will be expanded with improvements over the standard
10 pdb.
10 pdb.
11
11
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 changes. Licensing should therefore be under the standard Python terms. For
13 changes. Licensing should therefore be under the standard Python terms. For
14 details on the PSF (Python Software Foundation) standard license, see:
14 details on the PSF (Python Software Foundation) standard license, see:
15
15
16 http://www.python.org/2.2.3/license.html"""
16 http://www.python.org/2.2.3/license.html"""
17
17
18 #*****************************************************************************
18 #*****************************************************************************
19 #
19 #
20 # This file is licensed under the PSF license.
20 # This file is licensed under the PSF license.
21 #
21 #
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 #
24 #
25 #
25 #
26 #*****************************************************************************
26 #*****************************************************************************
27 from __future__ import print_function
27 from __future__ import print_function
28
28
29 import bdb
29 import bdb
30 import linecache
30 import linecache
31 import sys
31 import sys
32
32
33 from IPython.utils import PyColorize, ulinecache
33 from IPython.utils import PyColorize, ulinecache
34 from IPython.core import ipapi
34 from IPython.core import ipapi
35 from IPython.utils import coloransi, io, openpy, py3compat
35 from IPython.utils import coloransi, io, openpy, py3compat
36 from IPython.core.excolors import exception_colors
36 from IPython.core.excolors import exception_colors
37
37
38 # See if we can use pydb.
38 # See if we can use pydb.
39 has_pydb = False
39 has_pydb = False
40 prompt = 'ipdb> '
40 prompt = 'ipdb> '
41 #We have to check this directly from sys.argv, config struct not yet available
41 #We have to check this directly from sys.argv, config struct not yet available
42 if '--pydb' in sys.argv:
42 if '--pydb' in sys.argv:
43 try:
43 try:
44 import pydb
44 import pydb
45 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
45 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
46 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
46 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
47 # better protect against it.
47 # better protect against it.
48 has_pydb = True
48 has_pydb = True
49 except ImportError:
49 except ImportError:
50 print("Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available")
50 print("Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available")
51
51
52 if has_pydb:
52 if has_pydb:
53 from pydb import Pdb as OldPdb
53 from pydb import Pdb as OldPdb
54 #print "Using pydb for %run -d and post-mortem" #dbg
54 #print "Using pydb for %run -d and post-mortem" #dbg
55 prompt = 'ipydb> '
55 prompt = 'ipydb> '
56 else:
56 else:
57 from pdb import Pdb as OldPdb
57 from pdb import Pdb as OldPdb
58
58
59 # Allow the set_trace code to operate outside of an ipython instance, even if
59 # Allow the set_trace code to operate outside of an ipython instance, even if
60 # it does so with some limitations. The rest of this support is implemented in
60 # it does so with some limitations. The rest of this support is implemented in
61 # the Tracer constructor.
61 # the Tracer constructor.
62 def BdbQuit_excepthook(et,ev,tb):
62 def BdbQuit_excepthook(et,ev,tb):
63 if et==bdb.BdbQuit:
63 if et==bdb.BdbQuit:
64 print('Exiting Debugger.')
64 print('Exiting Debugger.')
65 else:
65 else:
66 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
66 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
67
67
68 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
68 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
69 print('Exiting Debugger.')
69 print('Exiting Debugger.')
70
70
71
71
72 class Tracer(object):
72 class Tracer(object):
73 """Class for local debugging, similar to pdb.set_trace.
73 """Class for local debugging, similar to pdb.set_trace.
74
74
75 Instances of this class, when called, behave like pdb.set_trace, but
75 Instances of this class, when called, behave like pdb.set_trace, but
76 providing IPython's enhanced capabilities.
76 providing IPython's enhanced capabilities.
77
77
78 This is implemented as a class which must be initialized in your own code
78 This is implemented as a class which must be initialized in your own code
79 and not as a standalone function because we need to detect at runtime
79 and not as a standalone function because we need to detect at runtime
80 whether IPython is already active or not. That detection is done in the
80 whether IPython is already active or not. That detection is done in the
81 constructor, ensuring that this code plays nicely with a running IPython,
81 constructor, ensuring that this code plays nicely with a running IPython,
82 while functioning acceptably (though with limitations) if outside of it.
82 while functioning acceptably (though with limitations) if outside of it.
83 """
83 """
84
84
85 def __init__(self,colors=None):
85 def __init__(self,colors=None):
86 """Create a local debugger instance.
86 """Create a local debugger instance.
87
87
88 :Parameters:
88 :Parameters:
89
89
90 - `colors` (None): a string containing the name of the color scheme to
90 - `colors` (None): a string containing the name of the color scheme to
91 use, it must be one of IPython's valid color schemes. If not given, the
91 use, it must be one of IPython's valid color schemes. If not given, the
92 function will default to the current IPython scheme when running inside
92 function will default to the current IPython scheme when running inside
93 IPython, and to 'NoColor' otherwise.
93 IPython, and to 'NoColor' otherwise.
94
94
95 Usage example:
95 Usage example:
96
96
97 from IPython.core.debugger import Tracer; debug_here = Tracer()
97 from IPython.core.debugger import Tracer; debug_here = Tracer()
98
98
99 ... later in your code
99 ... later in your code
100 debug_here() # -> will open up the debugger at that point.
100 debug_here() # -> will open up the debugger at that point.
101
101
102 Once the debugger activates, you can use all of its regular commands to
102 Once the debugger activates, you can use all of its regular commands to
103 step through code, set breakpoints, etc. See the pdb documentation
103 step through code, set breakpoints, etc. See the pdb documentation
104 from the Python standard library for usage details.
104 from the Python standard library for usage details.
105 """
105 """
106
106
107 try:
107 try:
108 ip = get_ipython()
108 ip = get_ipython()
109 except NameError:
109 except NameError:
110 # Outside of ipython, we set our own exception hook manually
110 # Outside of ipython, we set our own exception hook manually
111 BdbQuit_excepthook.excepthook_ori = sys.excepthook
111 BdbQuit_excepthook.excepthook_ori = sys.excepthook
112 sys.excepthook = BdbQuit_excepthook
112 sys.excepthook = BdbQuit_excepthook
113 def_colors = 'NoColor'
113 def_colors = 'NoColor'
114 try:
114 try:
115 # Limited tab completion support
115 # Limited tab completion support
116 import readline
116 import readline
117 readline.parse_and_bind('tab: complete')
117 readline.parse_and_bind('tab: complete')
118 except ImportError:
118 except ImportError:
119 pass
119 pass
120 else:
120 else:
121 # In ipython, we use its custom exception handler mechanism
121 # In ipython, we use its custom exception handler mechanism
122 def_colors = ip.colors
122 def_colors = ip.colors
123 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
123 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
124
124
125 if colors is None:
125 if colors is None:
126 colors = def_colors
126 colors = def_colors
127
127
128 # The stdlib debugger internally uses a modified repr from the `repr`
128 # The stdlib debugger internally uses a modified repr from the `repr`
129 # module, that limits the length of printed strings to a hardcoded
129 # module, that limits the length of printed strings to a hardcoded
130 # limit of 30 characters. That much trimming is too aggressive, let's
130 # limit of 30 characters. That much trimming is too aggressive, let's
131 # at least raise that limit to 80 chars, which should be enough for
131 # at least raise that limit to 80 chars, which should be enough for
132 # most interactive uses.
132 # most interactive uses.
133 try:
133 try:
134 from repr import aRepr
134 from repr import aRepr
135 aRepr.maxstring = 80
135 aRepr.maxstring = 80
136 except:
136 except:
137 # This is only a user-facing convenience, so any error we encounter
137 # This is only a user-facing convenience, so any error we encounter
138 # here can be warned about but can be otherwise ignored. These
138 # here can be warned about but can be otherwise ignored. These
139 # printouts will tell us about problems if this API changes
139 # printouts will tell us about problems if this API changes
140 import traceback
140 import traceback
141 traceback.print_exc()
141 traceback.print_exc()
142
142
143 self.debugger = Pdb(colors)
143 self.debugger = Pdb(colors)
144
144
145 def __call__(self):
145 def __call__(self):
146 """Starts an interactive debugger at the point where called.
146 """Starts an interactive debugger at the point where called.
147
147
148 This is similar to the pdb.set_trace() function from the std lib, but
148 This is similar to the pdb.set_trace() function from the std lib, but
149 using IPython's enhanced debugger."""
149 using IPython's enhanced debugger."""
150
150
151 self.debugger.set_trace(sys._getframe().f_back)
151 self.debugger.set_trace(sys._getframe().f_back)
152
152
153
153
154 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
154 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
155 """Make new_fn have old_fn's doc string. This is particularly useful
155 """Make new_fn have old_fn's doc string. This is particularly useful
156 for the do_... commands that hook into the help system.
156 for the do_... commands that hook into the help system.
157 Adapted from from a comp.lang.python posting
157 Adapted from from a comp.lang.python posting
158 by Duncan Booth."""
158 by Duncan Booth."""
159 def wrapper(*args, **kw):
159 def wrapper(*args, **kw):
160 return new_fn(*args, **kw)
160 return new_fn(*args, **kw)
161 if old_fn.__doc__:
161 if old_fn.__doc__:
162 wrapper.__doc__ = old_fn.__doc__ + additional_text
162 wrapper.__doc__ = old_fn.__doc__ + additional_text
163 return wrapper
163 return wrapper
164
164
165
165
166 def _file_lines(fname):
166 def _file_lines(fname):
167 """Return the contents of a named file as a list of lines.
167 """Return the contents of a named file as a list of lines.
168
168
169 This function never raises an IOError exception: if the file can't be
169 This function never raises an IOError exception: if the file can't be
170 read, it simply returns an empty list."""
170 read, it simply returns an empty list."""
171
171
172 try:
172 try:
173 outfile = open(fname)
173 outfile = open(fname)
174 except IOError:
174 except IOError:
175 return []
175 return []
176 else:
176 else:
177 out = outfile.readlines()
177 out = outfile.readlines()
178 outfile.close()
178 outfile.close()
179 return out
179 return out
180
180
181
181
182 class Pdb(OldPdb):
182 class Pdb(OldPdb):
183 """Modified Pdb class, does not load readline."""
183 """Modified Pdb class, does not load readline."""
184
184
185 def __init__(self,color_scheme='NoColor',completekey=None,
185 def __init__(self,color_scheme='NoColor',completekey=None,
186 stdin=None, stdout=None):
186 stdin=None, stdout=None):
187
187
188 # Parent constructor:
188 # Parent constructor:
189 if has_pydb and completekey is None:
189 if has_pydb and completekey is None:
190 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
190 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
191 else:
191 else:
192 OldPdb.__init__(self,completekey,stdin,stdout)
192 OldPdb.__init__(self,completekey,stdin,stdout)
193
193
194 self.prompt = prompt # The default prompt is '(Pdb)'
194 self.prompt = prompt # The default prompt is '(Pdb)'
195
195
196 # IPython changes...
196 # IPython changes...
197 self.is_pydb = has_pydb
197 self.is_pydb = has_pydb
198
198
199 self.shell = ipapi.get()
199 self.shell = ipapi.get()
200
200
201 if self.is_pydb:
201 if self.is_pydb:
202
202
203 # interactiveshell.py's ipalias seems to want pdb's checkline
203 # interactiveshell.py's ipalias seems to want pdb's checkline
204 # which located in pydb.fn
204 # which located in pydb.fn
205 import pydb.fns
205 import pydb.fns
206 self.checkline = lambda filename, lineno: \
206 self.checkline = lambda filename, lineno: \
207 pydb.fns.checkline(self, filename, lineno)
207 pydb.fns.checkline(self, filename, lineno)
208
208
209 self.curframe = None
209 self.curframe = None
210 self.do_restart = self.new_do_restart
210 self.do_restart = self.new_do_restart
211
211
212 self.old_all_completions = self.shell.Completer.all_completions
212 self.old_all_completions = self.shell.Completer.all_completions
213 self.shell.Completer.all_completions=self.all_completions
213 self.shell.Completer.all_completions=self.all_completions
214
214
215 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
215 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
216 OldPdb.do_list)
216 OldPdb.do_list)
217 self.do_l = self.do_list
217 self.do_l = self.do_list
218 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
218 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
219 OldPdb.do_frame)
219 OldPdb.do_frame)
220
220
221 self.aliases = {}
221 self.aliases = {}
222
222
223 # Create color table: we copy the default one from the traceback
223 # Create color table: we copy the default one from the traceback
224 # module and add a few attributes needed for debugging
224 # module and add a few attributes needed for debugging
225 self.color_scheme_table = exception_colors()
225 self.color_scheme_table = exception_colors()
226
226
227 # shorthands
227 # shorthands
228 C = coloransi.TermColors
228 C = coloransi.TermColors
229 cst = self.color_scheme_table
229 cst = self.color_scheme_table
230
230
231 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
231 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
232 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
232 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
233
233
234 cst['Linux'].colors.breakpoint_enabled = C.LightRed
234 cst['Linux'].colors.breakpoint_enabled = C.LightRed
235 cst['Linux'].colors.breakpoint_disabled = C.Red
235 cst['Linux'].colors.breakpoint_disabled = C.Red
236
236
237 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
237 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
238 cst['LightBG'].colors.breakpoint_disabled = C.Red
238 cst['LightBG'].colors.breakpoint_disabled = C.Red
239
239
240 self.set_colors(color_scheme)
240 self.set_colors(color_scheme)
241
241
242 # Add a python parser so we can syntax highlight source while
242 # Add a python parser so we can syntax highlight source while
243 # debugging.
243 # debugging.
244 self.parser = PyColorize.Parser()
244 self.parser = PyColorize.Parser()
245
245
246 def set_colors(self, scheme):
246 def set_colors(self, scheme):
247 """Shorthand access to the color table scheme selector method."""
247 """Shorthand access to the color table scheme selector method."""
248 self.color_scheme_table.set_active_scheme(scheme)
248 self.color_scheme_table.set_active_scheme(scheme)
249
249
250 def interaction(self, frame, traceback):
250 def interaction(self, frame, traceback):
251 self.shell.set_completer_frame(frame)
251 self.shell.set_completer_frame(frame)
252 OldPdb.interaction(self, frame, traceback)
252 OldPdb.interaction(self, frame, traceback)
253
253
254 def new_do_up(self, arg):
254 def new_do_up(self, arg):
255 OldPdb.do_up(self, arg)
255 OldPdb.do_up(self, arg)
256 self.shell.set_completer_frame(self.curframe)
256 self.shell.set_completer_frame(self.curframe)
257 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
257 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
258
258
259 def new_do_down(self, arg):
259 def new_do_down(self, arg):
260 OldPdb.do_down(self, arg)
260 OldPdb.do_down(self, arg)
261 self.shell.set_completer_frame(self.curframe)
261 self.shell.set_completer_frame(self.curframe)
262
262
263 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
263 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
264
264
265 def new_do_frame(self, arg):
265 def new_do_frame(self, arg):
266 OldPdb.do_frame(self, arg)
266 OldPdb.do_frame(self, arg)
267 self.shell.set_completer_frame(self.curframe)
267 self.shell.set_completer_frame(self.curframe)
268
268
269 def new_do_quit(self, arg):
269 def new_do_quit(self, arg):
270
270
271 if hasattr(self, 'old_all_completions'):
271 if hasattr(self, 'old_all_completions'):
272 self.shell.Completer.all_completions=self.old_all_completions
272 self.shell.Completer.all_completions=self.old_all_completions
273
273
274
274
275 return OldPdb.do_quit(self, arg)
275 return OldPdb.do_quit(self, arg)
276
276
277 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
277 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
278
278
279 def new_do_restart(self, arg):
279 def new_do_restart(self, arg):
280 """Restart command. In the context of ipython this is exactly the same
280 """Restart command. In the context of ipython this is exactly the same
281 thing as 'quit'."""
281 thing as 'quit'."""
282 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
282 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
283 return self.do_quit(arg)
283 return self.do_quit(arg)
284
284
285 def postloop(self):
285 def postloop(self):
286 self.shell.set_completer_frame(None)
286 self.shell.set_completer_frame(None)
287
287
288 def print_stack_trace(self):
288 def print_stack_trace(self):
289 try:
289 try:
290 for frame_lineno in self.stack:
290 for frame_lineno in self.stack:
291 self.print_stack_entry(frame_lineno, context = 5)
291 self.print_stack_entry(frame_lineno, context = 5)
292 except KeyboardInterrupt:
292 except KeyboardInterrupt:
293 pass
293 pass
294
294
295 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
295 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
296 context = 3):
296 context = 3):
297 #frame, lineno = frame_lineno
297 #frame, lineno = frame_lineno
298 print(self.format_stack_entry(frame_lineno, '', context), file=io.stdout)
298 print(self.format_stack_entry(frame_lineno, '', context), file=io.stdout)
299
299
300 # vds: >>
300 # vds: >>
301 frame, lineno = frame_lineno
301 frame, lineno = frame_lineno
302 filename = frame.f_code.co_filename
302 filename = frame.f_code.co_filename
303 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
303 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
304 # vds: <<
304 # vds: <<
305
305
306 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
306 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
307 import repr
307 import repr
308
308
309 ret = []
309 ret = []
310
310
311 Colors = self.color_scheme_table.active_colors
311 Colors = self.color_scheme_table.active_colors
312 ColorsNormal = Colors.Normal
312 ColorsNormal = Colors.Normal
313 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
313 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
314 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
314 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
315 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
315 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
316 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
316 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
317 ColorsNormal)
317 ColorsNormal)
318
318
319 frame, lineno = frame_lineno
319 frame, lineno = frame_lineno
320
320
321 return_value = ''
321 return_value = ''
322 if '__return__' in frame.f_locals:
322 if '__return__' in frame.f_locals:
323 rv = frame.f_locals['__return__']
323 rv = frame.f_locals['__return__']
324 #return_value += '->'
324 #return_value += '->'
325 return_value += repr.repr(rv) + '\n'
325 return_value += repr.repr(rv) + '\n'
326 ret.append(return_value)
326 ret.append(return_value)
327
327
328 #s = filename + '(' + `lineno` + ')'
328 #s = filename + '(' + `lineno` + ')'
329 filename = self.canonic(frame.f_code.co_filename)
329 filename = self.canonic(frame.f_code.co_filename)
330 link = tpl_link % py3compat.cast_unicode(filename)
330 link = tpl_link % py3compat.cast_unicode(filename)
331
331
332 if frame.f_code.co_name:
332 if frame.f_code.co_name:
333 func = frame.f_code.co_name
333 func = frame.f_code.co_name
334 else:
334 else:
335 func = "<lambda>"
335 func = "<lambda>"
336
336
337 call = ''
337 call = ''
338 if func != '?':
338 if func != '?':
339 if '__args__' in frame.f_locals:
339 if '__args__' in frame.f_locals:
340 args = repr.repr(frame.f_locals['__args__'])
340 args = repr.repr(frame.f_locals['__args__'])
341 else:
341 else:
342 args = '()'
342 args = '()'
343 call = tpl_call % (func, args)
343 call = tpl_call % (func, args)
344
344
345 # The level info should be generated in the same format pdb uses, to
345 # The level info should be generated in the same format pdb uses, to
346 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
346 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
347 if frame is self.curframe:
347 if frame is self.curframe:
348 ret.append('> ')
348 ret.append('> ')
349 else:
349 else:
350 ret.append(' ')
350 ret.append(' ')
351 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
351 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
352
352
353 start = lineno - 1 - context//2
353 start = lineno - 1 - context//2
354 lines = ulinecache.getlines(filename)
354 lines = ulinecache.getlines(filename)
355 start = max(start, 0)
355 start = max(start, 0)
356 start = min(start, len(lines) - context)
356 start = min(start, len(lines) - context)
357 lines = lines[start : start + context]
357 lines = lines[start : start + context]
358
358
359 for i,line in enumerate(lines):
359 for i,line in enumerate(lines):
360 show_arrow = (start + 1 + i == lineno)
360 show_arrow = (start + 1 + i == lineno)
361 linetpl = (frame is self.curframe or show_arrow) \
361 linetpl = (frame is self.curframe or show_arrow) \
362 and tpl_line_em \
362 and tpl_line_em \
363 or tpl_line
363 or tpl_line
364 ret.append(self.__format_line(linetpl, filename,
364 ret.append(self.__format_line(linetpl, filename,
365 start + 1 + i, line,
365 start + 1 + i, line,
366 arrow = show_arrow) )
366 arrow = show_arrow) )
367 return ''.join(ret)
367 return ''.join(ret)
368
368
369 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
369 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
370 bp_mark = ""
370 bp_mark = ""
371 bp_mark_color = ""
371 bp_mark_color = ""
372
372
373 scheme = self.color_scheme_table.active_scheme_name
373 scheme = self.color_scheme_table.active_scheme_name
374 new_line, err = self.parser.format2(line, 'str', scheme)
374 new_line, err = self.parser.format2(line, 'str', scheme)
375 if not err: line = new_line
375 if not err: line = new_line
376
376
377 bp = None
377 bp = None
378 if lineno in self.get_file_breaks(filename):
378 if lineno in self.get_file_breaks(filename):
379 bps = self.get_breaks(filename, lineno)
379 bps = self.get_breaks(filename, lineno)
380 bp = bps[-1]
380 bp = bps[-1]
381
381
382 if bp:
382 if bp:
383 Colors = self.color_scheme_table.active_colors
383 Colors = self.color_scheme_table.active_colors
384 bp_mark = str(bp.number)
384 bp_mark = str(bp.number)
385 bp_mark_color = Colors.breakpoint_enabled
385 bp_mark_color = Colors.breakpoint_enabled
386 if not bp.enabled:
386 if not bp.enabled:
387 bp_mark_color = Colors.breakpoint_disabled
387 bp_mark_color = Colors.breakpoint_disabled
388
388
389 numbers_width = 7
389 numbers_width = 7
390 if arrow:
390 if arrow:
391 # This is the line with the error
391 # This is the line with the error
392 pad = numbers_width - len(str(lineno)) - len(bp_mark)
392 pad = numbers_width - len(str(lineno)) - len(bp_mark)
393 if pad >= 3:
393 if pad >= 3:
394 marker = '-'*(pad-3) + '-> '
394 marker = '-'*(pad-3) + '-> '
395 elif pad == 2:
395 elif pad == 2:
396 marker = '> '
396 marker = '> '
397 elif pad == 1:
397 elif pad == 1:
398 marker = '>'
398 marker = '>'
399 else:
399 else:
400 marker = ''
400 marker = ''
401 num = '%s%s' % (marker, str(lineno))
401 num = '%s%s' % (marker, str(lineno))
402 line = tpl_line % (bp_mark_color + bp_mark, num, line)
402 line = tpl_line % (bp_mark_color + bp_mark, num, line)
403 else:
403 else:
404 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
404 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
405 line = tpl_line % (bp_mark_color + bp_mark, num, line)
405 line = tpl_line % (bp_mark_color + bp_mark, num, line)
406
406
407 return line
407 return line
408
408
409 def list_command_pydb(self, arg):
409 def list_command_pydb(self, arg):
410 """List command to use if we have a newer pydb installed"""
410 """List command to use if we have a newer pydb installed"""
411 filename, first, last = OldPdb.parse_list_cmd(self, arg)
411 filename, first, last = OldPdb.parse_list_cmd(self, arg)
412 if filename is not None:
412 if filename is not None:
413 self.print_list_lines(filename, first, last)
413 self.print_list_lines(filename, first, last)
414
414
415 def print_list_lines(self, filename, first, last):
415 def print_list_lines(self, filename, first, last):
416 """The printing (as opposed to the parsing part of a 'list'
416 """The printing (as opposed to the parsing part of a 'list'
417 command."""
417 command."""
418 try:
418 try:
419 Colors = self.color_scheme_table.active_colors
419 Colors = self.color_scheme_table.active_colors
420 ColorsNormal = Colors.Normal
420 ColorsNormal = Colors.Normal
421 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
421 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
422 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
422 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
423 src = []
423 src = []
424 if filename == "<string>" and hasattr(self, "_exec_filename"):
424 if filename == "<string>" and hasattr(self, "_exec_filename"):
425 filename = self._exec_filename
425 filename = self._exec_filename
426
426
427 for lineno in range(first, last+1):
427 for lineno in range(first, last+1):
428 line = ulinecache.getline(filename, lineno)
428 line = ulinecache.getline(filename, lineno)
429 if not line:
429 if not line:
430 break
430 break
431
431
432 if lineno == self.curframe.f_lineno:
432 if lineno == self.curframe.f_lineno:
433 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
433 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
434 else:
434 else:
435 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
435 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
436
436
437 src.append(line)
437 src.append(line)
438 self.lineno = lineno
438 self.lineno = lineno
439
439
440 print(''.join(src), file=io.stdout)
440 print(''.join(src), file=io.stdout)
441
441
442 except KeyboardInterrupt:
442 except KeyboardInterrupt:
443 pass
443 pass
444
444
445 def do_list(self, arg):
445 def do_list(self, arg):
446 self.lastcmd = 'list'
446 self.lastcmd = 'list'
447 last = None
447 last = None
448 if arg:
448 if arg:
449 try:
449 try:
450 x = eval(arg, {}, {})
450 x = eval(arg, {}, {})
451 if type(x) == type(()):
451 if type(x) == type(()):
452 first, last = x
452 first, last = x
453 first = int(first)
453 first = int(first)
454 last = int(last)
454 last = int(last)
455 if last < first:
455 if last < first:
456 # Assume it's a count
456 # Assume it's a count
457 last = first + last
457 last = first + last
458 else:
458 else:
459 first = max(1, int(x) - 5)
459 first = max(1, int(x) - 5)
460 except:
460 except:
461 print('*** Error in argument:', repr(arg))
461 print('*** Error in argument:', repr(arg))
462 return
462 return
463 elif self.lineno is None:
463 elif self.lineno is None:
464 first = max(1, self.curframe.f_lineno - 5)
464 first = max(1, self.curframe.f_lineno - 5)
465 else:
465 else:
466 first = self.lineno + 1
466 first = self.lineno + 1
467 if last is None:
467 if last is None:
468 last = first + 10
468 last = first + 10
469 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
469 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
470
470
471 # vds: >>
471 # vds: >>
472 lineno = first
472 lineno = first
473 filename = self.curframe.f_code.co_filename
473 filename = self.curframe.f_code.co_filename
474 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
474 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
475 # vds: <<
475 # vds: <<
476
476
477 do_l = do_list
477 do_l = do_list
478
478
479 def do_pdef(self, arg):
479 def do_pdef(self, arg):
480 """The debugger interface to magic_pdef"""
480 """Print the call signature for any callable object.
481
482 The debugger interface to %pdef"""
481 namespaces = [('Locals', self.curframe.f_locals),
483 namespaces = [('Locals', self.curframe.f_locals),
482 ('Globals', self.curframe.f_globals)]
484 ('Globals', self.curframe.f_globals)]
483 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
485 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
484
486
485 def do_pdoc(self, arg):
487 def do_pdoc(self, arg):
486 """The debugger interface to magic_pdoc"""
488 """Print the docstring for an object.
489
490 The debugger interface to %pdoc."""
487 namespaces = [('Locals', self.curframe.f_locals),
491 namespaces = [('Locals', self.curframe.f_locals),
488 ('Globals', self.curframe.f_globals)]
492 ('Globals', self.curframe.f_globals)]
489 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
493 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
490
494
495 def do_pfile(self, arg):
496 """Print (or run through pager) the file where an object is defined.
497
498 The debugger interface to %pfile.
499 """
500 namespaces = [('Locals', self.curframe.f_locals),
501 ('Globals', self.curframe.f_globals)]
502 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
503
491 def do_pinfo(self, arg):
504 def do_pinfo(self, arg):
492 """The debugger equivalant of ?obj"""
505 """Provide detailed information about an object.
506
507 The debugger interface to %pinfo, i.e., obj?."""
508 namespaces = [('Locals', self.curframe.f_locals),
509 ('Globals', self.curframe.f_globals)]
510 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
511
512 def do_pinfo2(self, arg):
513 """Provide extra detailed information about an object.
514
515 The debugger interface to %pinfo2, i.e., obj??."""
516 namespaces = [('Locals', self.curframe.f_locals),
517 ('Globals', self.curframe.f_globals)]
518 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
519
520 def do_psource(self, arg):
521 """Print (or run through pager) the source code for an object."""
493 namespaces = [('Locals', self.curframe.f_locals),
522 namespaces = [('Locals', self.curframe.f_locals),
494 ('Globals', self.curframe.f_globals)]
523 ('Globals', self.curframe.f_globals)]
495 self.shell.find_line_magic('pinfo')("pinfo %s" % arg,
524 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
496 namespaces=namespaces)
497
525
498 def checkline(self, filename, lineno):
526 def checkline(self, filename, lineno):
499 """Check whether specified line seems to be executable.
527 """Check whether specified line seems to be executable.
500
528
501 Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
529 Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
502 line or EOF). Warning: testing is not comprehensive.
530 line or EOF). Warning: testing is not comprehensive.
503 """
531 """
504 #######################################################################
532 #######################################################################
505 # XXX Hack! Use python-2.5 compatible code for this call, because with
533 # XXX Hack! Use python-2.5 compatible code for this call, because with
506 # all of our changes, we've drifted from the pdb api in 2.6. For now,
534 # all of our changes, we've drifted from the pdb api in 2.6. For now,
507 # changing:
535 # changing:
508 #
536 #
509 #line = linecache.getline(filename, lineno, self.curframe.f_globals)
537 #line = linecache.getline(filename, lineno, self.curframe.f_globals)
510 # to:
538 # to:
511 #
539 #
512 line = linecache.getline(filename, lineno)
540 line = linecache.getline(filename, lineno)
513 #
541 #
514 # does the trick. But in reality, we need to fix this by reconciling
542 # does the trick. But in reality, we need to fix this by reconciling
515 # our updates with the new Pdb APIs in Python 2.6.
543 # our updates with the new Pdb APIs in Python 2.6.
516 #
544 #
517 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
545 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
518 #######################################################################
546 #######################################################################
519
547
520 if not line:
548 if not line:
521 print('End of file', file=self.stdout)
549 print('End of file', file=self.stdout)
522 return 0
550 return 0
523 line = line.strip()
551 line = line.strip()
524 # Don't allow setting breakpoint at a blank line
552 # Don't allow setting breakpoint at a blank line
525 if (not line or (line[0] == '#') or
553 if (not line or (line[0] == '#') or
526 (line[:3] == '"""') or line[:3] == "'''"):
554 (line[:3] == '"""') or line[:3] == "'''"):
527 print('*** Blank or comment', file=self.stdout)
555 print('*** Blank or comment', file=self.stdout)
528 return 0
556 return 0
529 return lineno
557 return lineno
@@ -1,157 +1,184 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """A class for managing IPython extensions.
2 """A class for managing IPython extensions.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2010-2011 The IPython Development Team
10 # Copyright (C) 2010-2011 The IPython Development Team
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 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 import os
20 import os
21 from shutil import copyfile
21 from shutil import copyfile
22 import sys
22 import sys
23 from urllib import urlretrieve
23 from urllib import urlretrieve
24 from urlparse import urlparse
24 from urlparse import urlparse
25
25
26 from IPython.core.error import UsageError
26 from IPython.config.configurable import Configurable
27 from IPython.config.configurable import Configurable
27 from IPython.utils.traitlets import Instance
28 from IPython.utils.traitlets import Instance
29 from IPython.utils.py3compat import PY3
30 if PY3:
31 from imp import reload
28
32
29 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
30 # Main class
34 # Main class
31 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
32
36
33 class ExtensionManager(Configurable):
37 class ExtensionManager(Configurable):
34 """A class to manage IPython extensions.
38 """A class to manage IPython extensions.
35
39
36 An IPython extension is an importable Python module that has
40 An IPython extension is an importable Python module that has
37 a function with the signature::
41 a function with the signature::
38
42
39 def load_ipython_extension(ipython):
43 def load_ipython_extension(ipython):
40 # Do things with ipython
44 # Do things with ipython
41
45
42 This function is called after your extension is imported and the
46 This function is called after your extension is imported and the
43 currently active :class:`InteractiveShell` instance is passed as
47 currently active :class:`InteractiveShell` instance is passed as
44 the only argument. You can do anything you want with IPython at
48 the only argument. You can do anything you want with IPython at
45 that point, including defining new magic and aliases, adding new
49 that point, including defining new magic and aliases, adding new
46 components, etc.
50 components, etc.
47
51
48 The :func:`load_ipython_extension` will be called again is you
52 You can also optionaly define an :func:`unload_ipython_extension(ipython)`
49 load or reload the extension again. It is up to the extension
53 function, which will be called if the user unloads or reloads the extension.
50 author to add code to manage that.
54 The extension manager will only call :func:`load_ipython_extension` again
55 if the extension is reloaded.
51
56
52 You can put your extension modules anywhere you want, as long as
57 You can put your extension modules anywhere you want, as long as
53 they can be imported by Python's standard import mechanism. However,
58 they can be imported by Python's standard import mechanism. However,
54 to make it easy to write extensions, you can also put your extensions
59 to make it easy to write extensions, you can also put your extensions
55 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
60 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
56 is added to ``sys.path`` automatically.
61 is added to ``sys.path`` automatically.
57 """
62 """
58
63
59 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
64 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
60
65
61 def __init__(self, shell=None, config=None):
66 def __init__(self, shell=None, config=None):
62 super(ExtensionManager, self).__init__(shell=shell, config=config)
67 super(ExtensionManager, self).__init__(shell=shell, config=config)
63 self.shell.on_trait_change(
68 self.shell.on_trait_change(
64 self._on_ipython_dir_changed, 'ipython_dir'
69 self._on_ipython_dir_changed, 'ipython_dir'
65 )
70 )
71 self.loaded = set()
66
72
67 def __del__(self):
73 def __del__(self):
68 self.shell.on_trait_change(
74 self.shell.on_trait_change(
69 self._on_ipython_dir_changed, 'ipython_dir', remove=True
75 self._on_ipython_dir_changed, 'ipython_dir', remove=True
70 )
76 )
71
77
72 @property
78 @property
73 def ipython_extension_dir(self):
79 def ipython_extension_dir(self):
74 return os.path.join(self.shell.ipython_dir, u'extensions')
80 return os.path.join(self.shell.ipython_dir, u'extensions')
75
81
76 def _on_ipython_dir_changed(self):
82 def _on_ipython_dir_changed(self):
77 if not os.path.isdir(self.ipython_extension_dir):
83 if not os.path.isdir(self.ipython_extension_dir):
78 os.makedirs(self.ipython_extension_dir, mode = 0o777)
84 os.makedirs(self.ipython_extension_dir, mode = 0o777)
79
85
80 def load_extension(self, module_str):
86 def load_extension(self, module_str):
81 """Load an IPython extension by its module name.
87 """Load an IPython extension by its module name.
82
88
83 If :func:`load_ipython_extension` returns anything, this function
89 Returns the string "already loaded" if the extension is already loaded,
84 will return that object.
90 "no load function" if the module doesn't have a load_ipython_extension
91 function, or None if it succeeded.
85 """
92 """
93 if module_str in self.loaded:
94 return "already loaded"
95
86 from IPython.utils.syspathcontext import prepended_to_syspath
96 from IPython.utils.syspathcontext import prepended_to_syspath
87
97
88 if module_str not in sys.modules:
98 if module_str not in sys.modules:
89 with prepended_to_syspath(self.ipython_extension_dir):
99 with prepended_to_syspath(self.ipython_extension_dir):
90 __import__(module_str)
100 __import__(module_str)
91 mod = sys.modules[module_str]
101 mod = sys.modules[module_str]
92 return self._call_load_ipython_extension(mod)
102 if self._call_load_ipython_extension(mod):
103 self.loaded.add(module_str)
104 else:
105 return "no load function"
93
106
94 def unload_extension(self, module_str):
107 def unload_extension(self, module_str):
95 """Unload an IPython extension by its module name.
108 """Unload an IPython extension by its module name.
96
109
97 This function looks up the extension's name in ``sys.modules`` and
110 This function looks up the extension's name in ``sys.modules`` and
98 simply calls ``mod.unload_ipython_extension(self)``.
111 simply calls ``mod.unload_ipython_extension(self)``.
112
113 Returns the string "no unload function" if the extension doesn't define
114 a function to unload itself, "not loaded" if the extension isn't loaded,
115 otherwise None.
99 """
116 """
117 if module_str not in self.loaded:
118 return "not loaded"
119
100 if module_str in sys.modules:
120 if module_str in sys.modules:
101 mod = sys.modules[module_str]
121 mod = sys.modules[module_str]
102 self._call_unload_ipython_extension(mod)
122 if self._call_unload_ipython_extension(mod):
123 self.loaded.discard(module_str)
124 else:
125 return "no unload function"
103
126
104 def reload_extension(self, module_str):
127 def reload_extension(self, module_str):
105 """Reload an IPython extension by calling reload.
128 """Reload an IPython extension by calling reload.
106
129
107 If the module has not been loaded before,
130 If the module has not been loaded before,
108 :meth:`InteractiveShell.load_extension` is called. Otherwise
131 :meth:`InteractiveShell.load_extension` is called. Otherwise
109 :func:`reload` is called and then the :func:`load_ipython_extension`
132 :func:`reload` is called and then the :func:`load_ipython_extension`
110 function of the module, if it exists is called.
133 function of the module, if it exists is called.
111 """
134 """
112 from IPython.utils.syspathcontext import prepended_to_syspath
135 from IPython.utils.syspathcontext import prepended_to_syspath
113
136
114 with prepended_to_syspath(self.ipython_extension_dir):
137 if (module_str in self.loaded) and (module_str in sys.modules):
115 if module_str in sys.modules:
138 self.unload_extension(module_str)
116 mod = sys.modules[module_str]
139 mod = sys.modules[module_str]
140 with prepended_to_syspath(self.ipython_extension_dir):
117 reload(mod)
141 reload(mod)
118 self._call_load_ipython_extension(mod)
142 if self._call_load_ipython_extension(mod):
119 else:
143 self.loaded.add(module_str)
120 self.load_extension(module_str)
144 else:
145 self.load_extension(module_str)
121
146
122 def _call_load_ipython_extension(self, mod):
147 def _call_load_ipython_extension(self, mod):
123 if hasattr(mod, 'load_ipython_extension'):
148 if hasattr(mod, 'load_ipython_extension'):
124 return mod.load_ipython_extension(self.shell)
149 mod.load_ipython_extension(self.shell)
150 return True
125
151
126 def _call_unload_ipython_extension(self, mod):
152 def _call_unload_ipython_extension(self, mod):
127 if hasattr(mod, 'unload_ipython_extension'):
153 if hasattr(mod, 'unload_ipython_extension'):
128 return mod.unload_ipython_extension(self.shell)
154 mod.unload_ipython_extension(self.shell)
155 return True
129
156
130 def install_extension(self, url, filename=None):
157 def install_extension(self, url, filename=None):
131 """Download and install an IPython extension.
158 """Download and install an IPython extension.
132
159
133 If filename is given, the file will be so named (inside the extension
160 If filename is given, the file will be so named (inside the extension
134 directory). Otherwise, the name from the URL will be used. The file must
161 directory). Otherwise, the name from the URL will be used. The file must
135 have a .py or .zip extension; otherwise, a ValueError will be raised.
162 have a .py or .zip extension; otherwise, a ValueError will be raised.
136
163
137 Returns the full path to the installed file.
164 Returns the full path to the installed file.
138 """
165 """
139 # Ensure the extension directory exists
166 # Ensure the extension directory exists
140 if not os.path.isdir(self.ipython_extension_dir):
167 if not os.path.isdir(self.ipython_extension_dir):
141 os.makedirs(self.ipython_extension_dir, mode = 0o777)
168 os.makedirs(self.ipython_extension_dir, mode = 0o777)
142
169
143 if os.path.isfile(url):
170 if os.path.isfile(url):
144 src_filename = os.path.basename(url)
171 src_filename = os.path.basename(url)
145 copy = copyfile
172 copy = copyfile
146 else:
173 else:
147 src_filename = urlparse(url).path.split('/')[-1]
174 src_filename = urlparse(url).path.split('/')[-1]
148 copy = urlretrieve
175 copy = urlretrieve
149
176
150 if filename is None:
177 if filename is None:
151 filename = src_filename
178 filename = src_filename
152 if os.path.splitext(filename)[1] not in ('.py', '.zip'):
179 if os.path.splitext(filename)[1] not in ('.py', '.zip'):
153 raise ValueError("The file must have a .py or .zip extension", filename)
180 raise ValueError("The file must have a .py or .zip extension", filename)
154
181
155 filename = os.path.join(self.ipython_extension_dir, filename)
182 filename = os.path.join(self.ipython_extension_dir, filename)
156 copy(url, filename)
183 copy(url, filename)
157 return filename
184 return filename
@@ -1,3006 +1,3006 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Main IPython class."""
2 """Main IPython class."""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 from __future__ import with_statement
17 from __future__ import with_statement
18 from __future__ import absolute_import
18 from __future__ import absolute_import
19 from __future__ import print_function
19 from __future__ import print_function
20
20
21 import __builtin__ as builtin_mod
21 import __builtin__ as builtin_mod
22 import __future__
22 import __future__
23 import abc
23 import abc
24 import ast
24 import ast
25 import atexit
25 import atexit
26 import os
26 import os
27 import re
27 import re
28 import runpy
28 import runpy
29 import sys
29 import sys
30 import tempfile
30 import tempfile
31 import types
31 import types
32 import urllib
32 import urllib
33 from io import open as io_open
33 from io import open as io_open
34
34
35 from IPython.config.configurable import SingletonConfigurable
35 from IPython.config.configurable import SingletonConfigurable
36 from IPython.core import debugger, oinspect
36 from IPython.core import debugger, oinspect
37 from IPython.core import magic
37 from IPython.core import magic
38 from IPython.core import page
38 from IPython.core import page
39 from IPython.core import prefilter
39 from IPython.core import prefilter
40 from IPython.core import shadowns
40 from IPython.core import shadowns
41 from IPython.core import ultratb
41 from IPython.core import ultratb
42 from IPython.core.alias import AliasManager, AliasError
42 from IPython.core.alias import AliasManager, AliasError
43 from IPython.core.autocall import ExitAutocall
43 from IPython.core.autocall import ExitAutocall
44 from IPython.core.builtin_trap import BuiltinTrap
44 from IPython.core.builtin_trap import BuiltinTrap
45 from IPython.core.compilerop import CachingCompiler
45 from IPython.core.compilerop import CachingCompiler
46 from IPython.core.display_trap import DisplayTrap
46 from IPython.core.display_trap import DisplayTrap
47 from IPython.core.displayhook import DisplayHook
47 from IPython.core.displayhook import DisplayHook
48 from IPython.core.displaypub import DisplayPublisher
48 from IPython.core.displaypub import DisplayPublisher
49 from IPython.core.error import UsageError
49 from IPython.core.error import UsageError
50 from IPython.core.extensions import ExtensionManager
50 from IPython.core.extensions import ExtensionManager
51 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
51 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
52 from IPython.core.formatters import DisplayFormatter
52 from IPython.core.formatters import DisplayFormatter
53 from IPython.core.history import HistoryManager
53 from IPython.core.history import HistoryManager
54 from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2
54 from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2
55 from IPython.core.logger import Logger
55 from IPython.core.logger import Logger
56 from IPython.core.macro import Macro
56 from IPython.core.macro import Macro
57 from IPython.core.payload import PayloadManager
57 from IPython.core.payload import PayloadManager
58 from IPython.core.prefilter import PrefilterManager
58 from IPython.core.prefilter import PrefilterManager
59 from IPython.core.profiledir import ProfileDir
59 from IPython.core.profiledir import ProfileDir
60 from IPython.core.pylabtools import pylab_activate
60 from IPython.core.pylabtools import pylab_activate
61 from IPython.core.prompts import PromptManager
61 from IPython.core.prompts import PromptManager
62 from IPython.lib.latextools import LaTeXTool
62 from IPython.lib.latextools import LaTeXTool
63 from IPython.utils import PyColorize
63 from IPython.utils import PyColorize
64 from IPython.utils import io
64 from IPython.utils import io
65 from IPython.utils import py3compat
65 from IPython.utils import py3compat
66 from IPython.utils import openpy
66 from IPython.utils import openpy
67 from IPython.utils.doctestreload import doctest_reload
67 from IPython.utils.doctestreload import doctest_reload
68 from IPython.utils.io import ask_yes_no
68 from IPython.utils.io import ask_yes_no
69 from IPython.utils.ipstruct import Struct
69 from IPython.utils.ipstruct import Struct
70 from IPython.utils.path import get_home_dir, get_ipython_dir, get_py_filename, unquote_filename
70 from IPython.utils.path import get_home_dir, get_ipython_dir, get_py_filename, unquote_filename
71 from IPython.utils.pickleshare import PickleShareDB
71 from IPython.utils.pickleshare import PickleShareDB
72 from IPython.utils.process import system, getoutput
72 from IPython.utils.process import system, getoutput
73 from IPython.utils.strdispatch import StrDispatch
73 from IPython.utils.strdispatch import StrDispatch
74 from IPython.utils.syspathcontext import prepended_to_syspath
74 from IPython.utils.syspathcontext import prepended_to_syspath
75 from IPython.utils.text import (format_screen, LSString, SList,
75 from IPython.utils.text import (format_screen, LSString, SList,
76 DollarFormatter)
76 DollarFormatter)
77 from IPython.utils.traitlets import (Integer, CBool, CaselessStrEnum, Enum,
77 from IPython.utils.traitlets import (Integer, CBool, CaselessStrEnum, Enum,
78 List, Unicode, Instance, Type)
78 List, Unicode, Instance, Type)
79 from IPython.utils.warn import warn, error
79 from IPython.utils.warn import warn, error
80 import IPython.core.hooks
80 import IPython.core.hooks
81
81
82 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
83 # Globals
83 # Globals
84 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
85
85
86 # compiled regexps for autoindent management
86 # compiled regexps for autoindent management
87 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
87 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
88
88
89 #-----------------------------------------------------------------------------
89 #-----------------------------------------------------------------------------
90 # Utilities
90 # Utilities
91 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
92
92
93 def softspace(file, newvalue):
93 def softspace(file, newvalue):
94 """Copied from code.py, to remove the dependency"""
94 """Copied from code.py, to remove the dependency"""
95
95
96 oldvalue = 0
96 oldvalue = 0
97 try:
97 try:
98 oldvalue = file.softspace
98 oldvalue = file.softspace
99 except AttributeError:
99 except AttributeError:
100 pass
100 pass
101 try:
101 try:
102 file.softspace = newvalue
102 file.softspace = newvalue
103 except (AttributeError, TypeError):
103 except (AttributeError, TypeError):
104 # "attribute-less object" or "read-only attributes"
104 # "attribute-less object" or "read-only attributes"
105 pass
105 pass
106 return oldvalue
106 return oldvalue
107
107
108
108
109 def no_op(*a, **kw): pass
109 def no_op(*a, **kw): pass
110
110
111 class NoOpContext(object):
111 class NoOpContext(object):
112 def __enter__(self): pass
112 def __enter__(self): pass
113 def __exit__(self, type, value, traceback): pass
113 def __exit__(self, type, value, traceback): pass
114 no_op_context = NoOpContext()
114 no_op_context = NoOpContext()
115
115
116 class SpaceInInput(Exception): pass
116 class SpaceInInput(Exception): pass
117
117
118 class Bunch: pass
118 class Bunch: pass
119
119
120
120
121 def get_default_colors():
121 def get_default_colors():
122 if sys.platform=='darwin':
122 if sys.platform=='darwin':
123 return "LightBG"
123 return "LightBG"
124 elif os.name=='nt':
124 elif os.name=='nt':
125 return 'Linux'
125 return 'Linux'
126 else:
126 else:
127 return 'Linux'
127 return 'Linux'
128
128
129
129
130 class SeparateUnicode(Unicode):
130 class SeparateUnicode(Unicode):
131 """A Unicode subclass to validate separate_in, separate_out, etc.
131 """A Unicode subclass to validate separate_in, separate_out, etc.
132
132
133 This is a Unicode based trait that converts '0'->'' and '\\n'->'\n'.
133 This is a Unicode based trait that converts '0'->'' and '\\n'->'\n'.
134 """
134 """
135
135
136 def validate(self, obj, value):
136 def validate(self, obj, value):
137 if value == '0': value = ''
137 if value == '0': value = ''
138 value = value.replace('\\n','\n')
138 value = value.replace('\\n','\n')
139 return super(SeparateUnicode, self).validate(obj, value)
139 return super(SeparateUnicode, self).validate(obj, value)
140
140
141
141
142 class ReadlineNoRecord(object):
142 class ReadlineNoRecord(object):
143 """Context manager to execute some code, then reload readline history
143 """Context manager to execute some code, then reload readline history
144 so that interactive input to the code doesn't appear when pressing up."""
144 so that interactive input to the code doesn't appear when pressing up."""
145 def __init__(self, shell):
145 def __init__(self, shell):
146 self.shell = shell
146 self.shell = shell
147 self._nested_level = 0
147 self._nested_level = 0
148
148
149 def __enter__(self):
149 def __enter__(self):
150 if self._nested_level == 0:
150 if self._nested_level == 0:
151 try:
151 try:
152 self.orig_length = self.current_length()
152 self.orig_length = self.current_length()
153 self.readline_tail = self.get_readline_tail()
153 self.readline_tail = self.get_readline_tail()
154 except (AttributeError, IndexError): # Can fail with pyreadline
154 except (AttributeError, IndexError): # Can fail with pyreadline
155 self.orig_length, self.readline_tail = 999999, []
155 self.orig_length, self.readline_tail = 999999, []
156 self._nested_level += 1
156 self._nested_level += 1
157
157
158 def __exit__(self, type, value, traceback):
158 def __exit__(self, type, value, traceback):
159 self._nested_level -= 1
159 self._nested_level -= 1
160 if self._nested_level == 0:
160 if self._nested_level == 0:
161 # Try clipping the end if it's got longer
161 # Try clipping the end if it's got longer
162 try:
162 try:
163 e = self.current_length() - self.orig_length
163 e = self.current_length() - self.orig_length
164 if e > 0:
164 if e > 0:
165 for _ in range(e):
165 for _ in range(e):
166 self.shell.readline.remove_history_item(self.orig_length)
166 self.shell.readline.remove_history_item(self.orig_length)
167
167
168 # If it still doesn't match, just reload readline history.
168 # If it still doesn't match, just reload readline history.
169 if self.current_length() != self.orig_length \
169 if self.current_length() != self.orig_length \
170 or self.get_readline_tail() != self.readline_tail:
170 or self.get_readline_tail() != self.readline_tail:
171 self.shell.refill_readline_hist()
171 self.shell.refill_readline_hist()
172 except (AttributeError, IndexError):
172 except (AttributeError, IndexError):
173 pass
173 pass
174 # Returning False will cause exceptions to propagate
174 # Returning False will cause exceptions to propagate
175 return False
175 return False
176
176
177 def current_length(self):
177 def current_length(self):
178 return self.shell.readline.get_current_history_length()
178 return self.shell.readline.get_current_history_length()
179
179
180 def get_readline_tail(self, n=10):
180 def get_readline_tail(self, n=10):
181 """Get the last n items in readline history."""
181 """Get the last n items in readline history."""
182 end = self.shell.readline.get_current_history_length() + 1
182 end = self.shell.readline.get_current_history_length() + 1
183 start = max(end-n, 1)
183 start = max(end-n, 1)
184 ghi = self.shell.readline.get_history_item
184 ghi = self.shell.readline.get_history_item
185 return [ghi(x) for x in range(start, end)]
185 return [ghi(x) for x in range(start, end)]
186
186
187 #-----------------------------------------------------------------------------
187 #-----------------------------------------------------------------------------
188 # Main IPython class
188 # Main IPython class
189 #-----------------------------------------------------------------------------
189 #-----------------------------------------------------------------------------
190
190
191 class InteractiveShell(SingletonConfigurable):
191 class InteractiveShell(SingletonConfigurable):
192 """An enhanced, interactive shell for Python."""
192 """An enhanced, interactive shell for Python."""
193
193
194 _instance = None
194 _instance = None
195
195
196 autocall = Enum((0,1,2), default_value=0, config=True, help=
196 autocall = Enum((0,1,2), default_value=0, config=True, help=
197 """
197 """
198 Make IPython automatically call any callable object even if you didn't
198 Make IPython automatically call any callable object even if you didn't
199 type explicit parentheses. For example, 'str 43' becomes 'str(43)'
199 type explicit parentheses. For example, 'str 43' becomes 'str(43)'
200 automatically. The value can be '0' to disable the feature, '1' for
200 automatically. The value can be '0' to disable the feature, '1' for
201 'smart' autocall, where it is not applied if there are no more
201 'smart' autocall, where it is not applied if there are no more
202 arguments on the line, and '2' for 'full' autocall, where all callable
202 arguments on the line, and '2' for 'full' autocall, where all callable
203 objects are automatically called (even if no arguments are present).
203 objects are automatically called (even if no arguments are present).
204 """
204 """
205 )
205 )
206 # TODO: remove all autoindent logic and put into frontends.
206 # TODO: remove all autoindent logic and put into frontends.
207 # We can't do this yet because even runlines uses the autoindent.
207 # We can't do this yet because even runlines uses the autoindent.
208 autoindent = CBool(True, config=True, help=
208 autoindent = CBool(True, config=True, help=
209 """
209 """
210 Autoindent IPython code entered interactively.
210 Autoindent IPython code entered interactively.
211 """
211 """
212 )
212 )
213 automagic = CBool(True, config=True, help=
213 automagic = CBool(True, config=True, help=
214 """
214 """
215 Enable magic commands to be called without the leading %.
215 Enable magic commands to be called without the leading %.
216 """
216 """
217 )
217 )
218 cache_size = Integer(1000, config=True, help=
218 cache_size = Integer(1000, config=True, help=
219 """
219 """
220 Set the size of the output cache. The default is 1000, you can
220 Set the size of the output cache. The default is 1000, you can
221 change it permanently in your config file. Setting it to 0 completely
221 change it permanently in your config file. Setting it to 0 completely
222 disables the caching system, and the minimum value accepted is 20 (if
222 disables the caching system, and the minimum value accepted is 20 (if
223 you provide a value less than 20, it is reset to 0 and a warning is
223 you provide a value less than 20, it is reset to 0 and a warning is
224 issued). This limit is defined because otherwise you'll spend more
224 issued). This limit is defined because otherwise you'll spend more
225 time re-flushing a too small cache than working
225 time re-flushing a too small cache than working
226 """
226 """
227 )
227 )
228 color_info = CBool(True, config=True, help=
228 color_info = CBool(True, config=True, help=
229 """
229 """
230 Use colors for displaying information about objects. Because this
230 Use colors for displaying information about objects. Because this
231 information is passed through a pager (like 'less'), and some pagers
231 information is passed through a pager (like 'less'), and some pagers
232 get confused with color codes, this capability can be turned off.
232 get confused with color codes, this capability can be turned off.
233 """
233 """
234 )
234 )
235 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
235 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
236 default_value=get_default_colors(), config=True,
236 default_value=get_default_colors(), config=True,
237 help="Set the color scheme (NoColor, Linux, or LightBG)."
237 help="Set the color scheme (NoColor, Linux, or LightBG)."
238 )
238 )
239 colors_force = CBool(False, help=
239 colors_force = CBool(False, help=
240 """
240 """
241 Force use of ANSI color codes, regardless of OS and readline
241 Force use of ANSI color codes, regardless of OS and readline
242 availability.
242 availability.
243 """
243 """
244 # FIXME: This is essentially a hack to allow ZMQShell to show colors
244 # FIXME: This is essentially a hack to allow ZMQShell to show colors
245 # without readline on Win32. When the ZMQ formatting system is
245 # without readline on Win32. When the ZMQ formatting system is
246 # refactored, this should be removed.
246 # refactored, this should be removed.
247 )
247 )
248 debug = CBool(False, config=True)
248 debug = CBool(False, config=True)
249 deep_reload = CBool(False, config=True, help=
249 deep_reload = CBool(False, config=True, help=
250 """
250 """
251 Enable deep (recursive) reloading by default. IPython can use the
251 Enable deep (recursive) reloading by default. IPython can use the
252 deep_reload module which reloads changes in modules recursively (it
252 deep_reload module which reloads changes in modules recursively (it
253 replaces the reload() function, so you don't need to change anything to
253 replaces the reload() function, so you don't need to change anything to
254 use it). deep_reload() forces a full reload of modules whose code may
254 use it). deep_reload() forces a full reload of modules whose code may
255 have changed, which the default reload() function does not. When
255 have changed, which the default reload() function does not. When
256 deep_reload is off, IPython will use the normal reload(), but
256 deep_reload is off, IPython will use the normal reload(), but
257 deep_reload will still be available as dreload().
257 deep_reload will still be available as dreload().
258 """
258 """
259 )
259 )
260 disable_failing_post_execute = CBool(False, config=True,
260 disable_failing_post_execute = CBool(False, config=True,
261 help="Don't call post-execute functions that have failed in the past."
261 help="Don't call post-execute functions that have failed in the past."
262 )
262 )
263 display_formatter = Instance(DisplayFormatter)
263 display_formatter = Instance(DisplayFormatter)
264 displayhook_class = Type(DisplayHook)
264 displayhook_class = Type(DisplayHook)
265 display_pub_class = Type(DisplayPublisher)
265 display_pub_class = Type(DisplayPublisher)
266 data_pub_class = None
266 data_pub_class = None
267
267
268 exit_now = CBool(False)
268 exit_now = CBool(False)
269 exiter = Instance(ExitAutocall)
269 exiter = Instance(ExitAutocall)
270 def _exiter_default(self):
270 def _exiter_default(self):
271 return ExitAutocall(self)
271 return ExitAutocall(self)
272 # Monotonically increasing execution counter
272 # Monotonically increasing execution counter
273 execution_count = Integer(1)
273 execution_count = Integer(1)
274 filename = Unicode("<ipython console>")
274 filename = Unicode("<ipython console>")
275 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
275 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
276
276
277 # Input splitter, to split entire cells of input into either individual
277 # Input splitter, to split entire cells of input into either individual
278 # interactive statements or whole blocks.
278 # interactive statements or whole blocks.
279 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
279 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
280 (), {})
280 (), {})
281 logstart = CBool(False, config=True, help=
281 logstart = CBool(False, config=True, help=
282 """
282 """
283 Start logging to the default log file.
283 Start logging to the default log file.
284 """
284 """
285 )
285 )
286 logfile = Unicode('', config=True, help=
286 logfile = Unicode('', config=True, help=
287 """
287 """
288 The name of the logfile to use.
288 The name of the logfile to use.
289 """
289 """
290 )
290 )
291 logappend = Unicode('', config=True, help=
291 logappend = Unicode('', config=True, help=
292 """
292 """
293 Start logging to the given file in append mode.
293 Start logging to the given file in append mode.
294 """
294 """
295 )
295 )
296 object_info_string_level = Enum((0,1,2), default_value=0,
296 object_info_string_level = Enum((0,1,2), default_value=0,
297 config=True)
297 config=True)
298 pdb = CBool(False, config=True, help=
298 pdb = CBool(False, config=True, help=
299 """
299 """
300 Automatically call the pdb debugger after every exception.
300 Automatically call the pdb debugger after every exception.
301 """
301 """
302 )
302 )
303 multiline_history = CBool(sys.platform != 'win32', config=True,
303 multiline_history = CBool(sys.platform != 'win32', config=True,
304 help="Save multi-line entries as one entry in readline history"
304 help="Save multi-line entries as one entry in readline history"
305 )
305 )
306
306
307 # deprecated prompt traits:
307 # deprecated prompt traits:
308
308
309 prompt_in1 = Unicode('In [\\#]: ', config=True,
309 prompt_in1 = Unicode('In [\\#]: ', config=True,
310 help="Deprecated, use PromptManager.in_template")
310 help="Deprecated, use PromptManager.in_template")
311 prompt_in2 = Unicode(' .\\D.: ', config=True,
311 prompt_in2 = Unicode(' .\\D.: ', config=True,
312 help="Deprecated, use PromptManager.in2_template")
312 help="Deprecated, use PromptManager.in2_template")
313 prompt_out = Unicode('Out[\\#]: ', config=True,
313 prompt_out = Unicode('Out[\\#]: ', config=True,
314 help="Deprecated, use PromptManager.out_template")
314 help="Deprecated, use PromptManager.out_template")
315 prompts_pad_left = CBool(True, config=True,
315 prompts_pad_left = CBool(True, config=True,
316 help="Deprecated, use PromptManager.justify")
316 help="Deprecated, use PromptManager.justify")
317
317
318 def _prompt_trait_changed(self, name, old, new):
318 def _prompt_trait_changed(self, name, old, new):
319 table = {
319 table = {
320 'prompt_in1' : 'in_template',
320 'prompt_in1' : 'in_template',
321 'prompt_in2' : 'in2_template',
321 'prompt_in2' : 'in2_template',
322 'prompt_out' : 'out_template',
322 'prompt_out' : 'out_template',
323 'prompts_pad_left' : 'justify',
323 'prompts_pad_left' : 'justify',
324 }
324 }
325 warn("InteractiveShell.{name} is deprecated, use PromptManager.{newname}\n".format(
325 warn("InteractiveShell.{name} is deprecated, use PromptManager.{newname}\n".format(
326 name=name, newname=table[name])
326 name=name, newname=table[name])
327 )
327 )
328 # protect against weird cases where self.config may not exist:
328 # protect against weird cases where self.config may not exist:
329 if self.config is not None:
329 if self.config is not None:
330 # propagate to corresponding PromptManager trait
330 # propagate to corresponding PromptManager trait
331 setattr(self.config.PromptManager, table[name], new)
331 setattr(self.config.PromptManager, table[name], new)
332
332
333 _prompt_in1_changed = _prompt_trait_changed
333 _prompt_in1_changed = _prompt_trait_changed
334 _prompt_in2_changed = _prompt_trait_changed
334 _prompt_in2_changed = _prompt_trait_changed
335 _prompt_out_changed = _prompt_trait_changed
335 _prompt_out_changed = _prompt_trait_changed
336 _prompt_pad_left_changed = _prompt_trait_changed
336 _prompt_pad_left_changed = _prompt_trait_changed
337
337
338 show_rewritten_input = CBool(True, config=True,
338 show_rewritten_input = CBool(True, config=True,
339 help="Show rewritten input, e.g. for autocall."
339 help="Show rewritten input, e.g. for autocall."
340 )
340 )
341
341
342 quiet = CBool(False, config=True)
342 quiet = CBool(False, config=True)
343
343
344 history_length = Integer(10000, config=True)
344 history_length = Integer(10000, config=True)
345
345
346 # The readline stuff will eventually be moved to the terminal subclass
346 # The readline stuff will eventually be moved to the terminal subclass
347 # but for now, we can't do that as readline is welded in everywhere.
347 # but for now, we can't do that as readline is welded in everywhere.
348 readline_use = CBool(True, config=True)
348 readline_use = CBool(True, config=True)
349 readline_remove_delims = Unicode('-/~', config=True)
349 readline_remove_delims = Unicode('-/~', config=True)
350 # don't use \M- bindings by default, because they
350 # don't use \M- bindings by default, because they
351 # conflict with 8-bit encodings. See gh-58,gh-88
351 # conflict with 8-bit encodings. See gh-58,gh-88
352 readline_parse_and_bind = List([
352 readline_parse_and_bind = List([
353 'tab: complete',
353 'tab: complete',
354 '"\C-l": clear-screen',
354 '"\C-l": clear-screen',
355 'set show-all-if-ambiguous on',
355 'set show-all-if-ambiguous on',
356 '"\C-o": tab-insert',
356 '"\C-o": tab-insert',
357 '"\C-r": reverse-search-history',
357 '"\C-r": reverse-search-history',
358 '"\C-s": forward-search-history',
358 '"\C-s": forward-search-history',
359 '"\C-p": history-search-backward',
359 '"\C-p": history-search-backward',
360 '"\C-n": history-search-forward',
360 '"\C-n": history-search-forward',
361 '"\e[A": history-search-backward',
361 '"\e[A": history-search-backward',
362 '"\e[B": history-search-forward',
362 '"\e[B": history-search-forward',
363 '"\C-k": kill-line',
363 '"\C-k": kill-line',
364 '"\C-u": unix-line-discard',
364 '"\C-u": unix-line-discard',
365 ], allow_none=False, config=True)
365 ], allow_none=False, config=True)
366
366
367 ast_node_interactivity = Enum(['all', 'last', 'last_expr', 'none'],
367 ast_node_interactivity = Enum(['all', 'last', 'last_expr', 'none'],
368 default_value='last_expr', config=True,
368 default_value='last_expr', config=True,
369 help="""
369 help="""
370 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
370 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
371 run interactively (displaying output from expressions).""")
371 run interactively (displaying output from expressions).""")
372
372
373 # TODO: this part of prompt management should be moved to the frontends.
373 # TODO: this part of prompt management should be moved to the frontends.
374 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
374 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
375 separate_in = SeparateUnicode('\n', config=True)
375 separate_in = SeparateUnicode('\n', config=True)
376 separate_out = SeparateUnicode('', config=True)
376 separate_out = SeparateUnicode('', config=True)
377 separate_out2 = SeparateUnicode('', config=True)
377 separate_out2 = SeparateUnicode('', config=True)
378 wildcards_case_sensitive = CBool(True, config=True)
378 wildcards_case_sensitive = CBool(True, config=True)
379 xmode = CaselessStrEnum(('Context','Plain', 'Verbose'),
379 xmode = CaselessStrEnum(('Context','Plain', 'Verbose'),
380 default_value='Context', config=True)
380 default_value='Context', config=True)
381
381
382 # Subcomponents of InteractiveShell
382 # Subcomponents of InteractiveShell
383 alias_manager = Instance('IPython.core.alias.AliasManager')
383 alias_manager = Instance('IPython.core.alias.AliasManager')
384 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
384 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
385 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap')
385 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap')
386 display_trap = Instance('IPython.core.display_trap.DisplayTrap')
386 display_trap = Instance('IPython.core.display_trap.DisplayTrap')
387 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
387 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
388 payload_manager = Instance('IPython.core.payload.PayloadManager')
388 payload_manager = Instance('IPython.core.payload.PayloadManager')
389 history_manager = Instance('IPython.core.history.HistoryManager')
389 history_manager = Instance('IPython.core.history.HistoryManager')
390 magics_manager = Instance('IPython.core.magic.MagicsManager')
390 magics_manager = Instance('IPython.core.magic.MagicsManager')
391
391
392 profile_dir = Instance('IPython.core.application.ProfileDir')
392 profile_dir = Instance('IPython.core.application.ProfileDir')
393 @property
393 @property
394 def profile(self):
394 def profile(self):
395 if self.profile_dir is not None:
395 if self.profile_dir is not None:
396 name = os.path.basename(self.profile_dir.location)
396 name = os.path.basename(self.profile_dir.location)
397 return name.replace('profile_','')
397 return name.replace('profile_','')
398
398
399
399
400 # Private interface
400 # Private interface
401 _post_execute = Instance(dict)
401 _post_execute = Instance(dict)
402
402
403 # Tracks any GUI loop loaded for pylab
403 # Tracks any GUI loop loaded for pylab
404 pylab_gui_select = None
404 pylab_gui_select = None
405
405
406 def __init__(self, config=None, ipython_dir=None, profile_dir=None,
406 def __init__(self, config=None, ipython_dir=None, profile_dir=None,
407 user_module=None, user_ns=None,
407 user_module=None, user_ns=None,
408 custom_exceptions=((), None)):
408 custom_exceptions=((), None)):
409
409
410 # This is where traits with a config_key argument are updated
410 # This is where traits with a config_key argument are updated
411 # from the values on config.
411 # from the values on config.
412 super(InteractiveShell, self).__init__(config=config)
412 super(InteractiveShell, self).__init__(config=config)
413 self.configurables = [self]
413 self.configurables = [self]
414
414
415 # These are relatively independent and stateless
415 # These are relatively independent and stateless
416 self.init_ipython_dir(ipython_dir)
416 self.init_ipython_dir(ipython_dir)
417 self.init_profile_dir(profile_dir)
417 self.init_profile_dir(profile_dir)
418 self.init_instance_attrs()
418 self.init_instance_attrs()
419 self.init_environment()
419 self.init_environment()
420
420
421 # Check if we're in a virtualenv, and set up sys.path.
421 # Check if we're in a virtualenv, and set up sys.path.
422 self.init_virtualenv()
422 self.init_virtualenv()
423
423
424 # Create namespaces (user_ns, user_global_ns, etc.)
424 # Create namespaces (user_ns, user_global_ns, etc.)
425 self.init_create_namespaces(user_module, user_ns)
425 self.init_create_namespaces(user_module, user_ns)
426 # This has to be done after init_create_namespaces because it uses
426 # This has to be done after init_create_namespaces because it uses
427 # something in self.user_ns, but before init_sys_modules, which
427 # something in self.user_ns, but before init_sys_modules, which
428 # is the first thing to modify sys.
428 # is the first thing to modify sys.
429 # TODO: When we override sys.stdout and sys.stderr before this class
429 # TODO: When we override sys.stdout and sys.stderr before this class
430 # is created, we are saving the overridden ones here. Not sure if this
430 # is created, we are saving the overridden ones here. Not sure if this
431 # is what we want to do.
431 # is what we want to do.
432 self.save_sys_module_state()
432 self.save_sys_module_state()
433 self.init_sys_modules()
433 self.init_sys_modules()
434
434
435 # While we're trying to have each part of the code directly access what
435 # While we're trying to have each part of the code directly access what
436 # it needs without keeping redundant references to objects, we have too
436 # it needs without keeping redundant references to objects, we have too
437 # much legacy code that expects ip.db to exist.
437 # much legacy code that expects ip.db to exist.
438 self.db = PickleShareDB(os.path.join(self.profile_dir.location, 'db'))
438 self.db = PickleShareDB(os.path.join(self.profile_dir.location, 'db'))
439
439
440 self.init_history()
440 self.init_history()
441 self.init_encoding()
441 self.init_encoding()
442 self.init_prefilter()
442 self.init_prefilter()
443
443
444 self.init_syntax_highlighting()
444 self.init_syntax_highlighting()
445 self.init_hooks()
445 self.init_hooks()
446 self.init_pushd_popd_magic()
446 self.init_pushd_popd_magic()
447 # self.init_traceback_handlers use to be here, but we moved it below
447 # self.init_traceback_handlers use to be here, but we moved it below
448 # because it and init_io have to come after init_readline.
448 # because it and init_io have to come after init_readline.
449 self.init_user_ns()
449 self.init_user_ns()
450 self.init_logger()
450 self.init_logger()
451 self.init_alias()
451 self.init_alias()
452 self.init_builtins()
452 self.init_builtins()
453
453
454 # The following was in post_config_initialization
454 # The following was in post_config_initialization
455 self.init_inspector()
455 self.init_inspector()
456 # init_readline() must come before init_io(), because init_io uses
456 # init_readline() must come before init_io(), because init_io uses
457 # readline related things.
457 # readline related things.
458 self.init_readline()
458 self.init_readline()
459 # We save this here in case user code replaces raw_input, but it needs
459 # We save this here in case user code replaces raw_input, but it needs
460 # to be after init_readline(), because PyPy's readline works by replacing
460 # to be after init_readline(), because PyPy's readline works by replacing
461 # raw_input.
461 # raw_input.
462 if py3compat.PY3:
462 if py3compat.PY3:
463 self.raw_input_original = input
463 self.raw_input_original = input
464 else:
464 else:
465 self.raw_input_original = raw_input
465 self.raw_input_original = raw_input
466 # init_completer must come after init_readline, because it needs to
466 # init_completer must come after init_readline, because it needs to
467 # know whether readline is present or not system-wide to configure the
467 # know whether readline is present or not system-wide to configure the
468 # completers, since the completion machinery can now operate
468 # completers, since the completion machinery can now operate
469 # independently of readline (e.g. over the network)
469 # independently of readline (e.g. over the network)
470 self.init_completer()
470 self.init_completer()
471 # TODO: init_io() needs to happen before init_traceback handlers
471 # TODO: init_io() needs to happen before init_traceback handlers
472 # because the traceback handlers hardcode the stdout/stderr streams.
472 # because the traceback handlers hardcode the stdout/stderr streams.
473 # This logic in in debugger.Pdb and should eventually be changed.
473 # This logic in in debugger.Pdb and should eventually be changed.
474 self.init_io()
474 self.init_io()
475 self.init_traceback_handlers(custom_exceptions)
475 self.init_traceback_handlers(custom_exceptions)
476 self.init_prompts()
476 self.init_prompts()
477 self.init_display_formatter()
477 self.init_display_formatter()
478 self.init_display_pub()
478 self.init_display_pub()
479 self.init_data_pub()
479 self.init_data_pub()
480 self.init_displayhook()
480 self.init_displayhook()
481 self.init_reload_doctest()
481 self.init_reload_doctest()
482 self.init_latextool()
482 self.init_latextool()
483 self.init_magics()
483 self.init_magics()
484 self.init_logstart()
484 self.init_logstart()
485 self.init_pdb()
485 self.init_pdb()
486 self.init_extension_manager()
486 self.init_extension_manager()
487 self.init_payload()
487 self.init_payload()
488 self.hooks.late_startup_hook()
488 self.hooks.late_startup_hook()
489 atexit.register(self.atexit_operations)
489 atexit.register(self.atexit_operations)
490
490
491 def get_ipython(self):
491 def get_ipython(self):
492 """Return the currently running IPython instance."""
492 """Return the currently running IPython instance."""
493 return self
493 return self
494
494
495 #-------------------------------------------------------------------------
495 #-------------------------------------------------------------------------
496 # Trait changed handlers
496 # Trait changed handlers
497 #-------------------------------------------------------------------------
497 #-------------------------------------------------------------------------
498
498
499 def _ipython_dir_changed(self, name, new):
499 def _ipython_dir_changed(self, name, new):
500 if not os.path.isdir(new):
500 if not os.path.isdir(new):
501 os.makedirs(new, mode = 0o777)
501 os.makedirs(new, mode = 0o777)
502
502
503 def set_autoindent(self,value=None):
503 def set_autoindent(self,value=None):
504 """Set the autoindent flag, checking for readline support.
504 """Set the autoindent flag, checking for readline support.
505
505
506 If called with no arguments, it acts as a toggle."""
506 If called with no arguments, it acts as a toggle."""
507
507
508 if value != 0 and not self.has_readline:
508 if value != 0 and not self.has_readline:
509 if os.name == 'posix':
509 if os.name == 'posix':
510 warn("The auto-indent feature requires the readline library")
510 warn("The auto-indent feature requires the readline library")
511 self.autoindent = 0
511 self.autoindent = 0
512 return
512 return
513 if value is None:
513 if value is None:
514 self.autoindent = not self.autoindent
514 self.autoindent = not self.autoindent
515 else:
515 else:
516 self.autoindent = value
516 self.autoindent = value
517
517
518 #-------------------------------------------------------------------------
518 #-------------------------------------------------------------------------
519 # init_* methods called by __init__
519 # init_* methods called by __init__
520 #-------------------------------------------------------------------------
520 #-------------------------------------------------------------------------
521
521
522 def init_ipython_dir(self, ipython_dir):
522 def init_ipython_dir(self, ipython_dir):
523 if ipython_dir is not None:
523 if ipython_dir is not None:
524 self.ipython_dir = ipython_dir
524 self.ipython_dir = ipython_dir
525 return
525 return
526
526
527 self.ipython_dir = get_ipython_dir()
527 self.ipython_dir = get_ipython_dir()
528
528
529 def init_profile_dir(self, profile_dir):
529 def init_profile_dir(self, profile_dir):
530 if profile_dir is not None:
530 if profile_dir is not None:
531 self.profile_dir = profile_dir
531 self.profile_dir = profile_dir
532 return
532 return
533 self.profile_dir =\
533 self.profile_dir =\
534 ProfileDir.create_profile_dir_by_name(self.ipython_dir, 'default')
534 ProfileDir.create_profile_dir_by_name(self.ipython_dir, 'default')
535
535
536 def init_instance_attrs(self):
536 def init_instance_attrs(self):
537 self.more = False
537 self.more = False
538
538
539 # command compiler
539 # command compiler
540 self.compile = CachingCompiler()
540 self.compile = CachingCompiler()
541
541
542 # Make an empty namespace, which extension writers can rely on both
542 # Make an empty namespace, which extension writers can rely on both
543 # existing and NEVER being used by ipython itself. This gives them a
543 # existing and NEVER being used by ipython itself. This gives them a
544 # convenient location for storing additional information and state
544 # convenient location for storing additional information and state
545 # their extensions may require, without fear of collisions with other
545 # their extensions may require, without fear of collisions with other
546 # ipython names that may develop later.
546 # ipython names that may develop later.
547 self.meta = Struct()
547 self.meta = Struct()
548
548
549 # Temporary files used for various purposes. Deleted at exit.
549 # Temporary files used for various purposes. Deleted at exit.
550 self.tempfiles = []
550 self.tempfiles = []
551
551
552 # Keep track of readline usage (later set by init_readline)
552 # Keep track of readline usage (later set by init_readline)
553 self.has_readline = False
553 self.has_readline = False
554
554
555 # keep track of where we started running (mainly for crash post-mortem)
555 # keep track of where we started running (mainly for crash post-mortem)
556 # This is not being used anywhere currently.
556 # This is not being used anywhere currently.
557 self.starting_dir = os.getcwdu()
557 self.starting_dir = os.getcwdu()
558
558
559 # Indentation management
559 # Indentation management
560 self.indent_current_nsp = 0
560 self.indent_current_nsp = 0
561
561
562 # Dict to track post-execution functions that have been registered
562 # Dict to track post-execution functions that have been registered
563 self._post_execute = {}
563 self._post_execute = {}
564
564
565 def init_environment(self):
565 def init_environment(self):
566 """Any changes we need to make to the user's environment."""
566 """Any changes we need to make to the user's environment."""
567 pass
567 pass
568
568
569 def init_encoding(self):
569 def init_encoding(self):
570 # Get system encoding at startup time. Certain terminals (like Emacs
570 # Get system encoding at startup time. Certain terminals (like Emacs
571 # under Win32 have it set to None, and we need to have a known valid
571 # under Win32 have it set to None, and we need to have a known valid
572 # encoding to use in the raw_input() method
572 # encoding to use in the raw_input() method
573 try:
573 try:
574 self.stdin_encoding = sys.stdin.encoding or 'ascii'
574 self.stdin_encoding = sys.stdin.encoding or 'ascii'
575 except AttributeError:
575 except AttributeError:
576 self.stdin_encoding = 'ascii'
576 self.stdin_encoding = 'ascii'
577
577
578 def init_syntax_highlighting(self):
578 def init_syntax_highlighting(self):
579 # Python source parser/formatter for syntax highlighting
579 # Python source parser/formatter for syntax highlighting
580 pyformat = PyColorize.Parser().format
580 pyformat = PyColorize.Parser().format
581 self.pycolorize = lambda src: pyformat(src,'str',self.colors)
581 self.pycolorize = lambda src: pyformat(src,'str',self.colors)
582
582
583 def init_pushd_popd_magic(self):
583 def init_pushd_popd_magic(self):
584 # for pushd/popd management
584 # for pushd/popd management
585 self.home_dir = get_home_dir()
585 self.home_dir = get_home_dir()
586
586
587 self.dir_stack = []
587 self.dir_stack = []
588
588
589 def init_logger(self):
589 def init_logger(self):
590 self.logger = Logger(self.home_dir, logfname='ipython_log.py',
590 self.logger = Logger(self.home_dir, logfname='ipython_log.py',
591 logmode='rotate')
591 logmode='rotate')
592
592
593 def init_logstart(self):
593 def init_logstart(self):
594 """Initialize logging in case it was requested at the command line.
594 """Initialize logging in case it was requested at the command line.
595 """
595 """
596 if self.logappend:
596 if self.logappend:
597 self.magic('logstart %s append' % self.logappend)
597 self.magic('logstart %s append' % self.logappend)
598 elif self.logfile:
598 elif self.logfile:
599 self.magic('logstart %s' % self.logfile)
599 self.magic('logstart %s' % self.logfile)
600 elif self.logstart:
600 elif self.logstart:
601 self.magic('logstart')
601 self.magic('logstart')
602
602
603 def init_builtins(self):
603 def init_builtins(self):
604 # A single, static flag that we set to True. Its presence indicates
604 # A single, static flag that we set to True. Its presence indicates
605 # that an IPython shell has been created, and we make no attempts at
605 # that an IPython shell has been created, and we make no attempts at
606 # removing on exit or representing the existence of more than one
606 # removing on exit or representing the existence of more than one
607 # IPython at a time.
607 # IPython at a time.
608 builtin_mod.__dict__['__IPYTHON__'] = True
608 builtin_mod.__dict__['__IPYTHON__'] = True
609
609
610 # In 0.11 we introduced '__IPYTHON__active' as an integer we'd try to
610 # In 0.11 we introduced '__IPYTHON__active' as an integer we'd try to
611 # manage on enter/exit, but with all our shells it's virtually
611 # manage on enter/exit, but with all our shells it's virtually
612 # impossible to get all the cases right. We're leaving the name in for
612 # impossible to get all the cases right. We're leaving the name in for
613 # those who adapted their codes to check for this flag, but will
613 # those who adapted their codes to check for this flag, but will
614 # eventually remove it after a few more releases.
614 # eventually remove it after a few more releases.
615 builtin_mod.__dict__['__IPYTHON__active'] = \
615 builtin_mod.__dict__['__IPYTHON__active'] = \
616 'Deprecated, check for __IPYTHON__'
616 'Deprecated, check for __IPYTHON__'
617
617
618 self.builtin_trap = BuiltinTrap(shell=self)
618 self.builtin_trap = BuiltinTrap(shell=self)
619
619
620 def init_inspector(self):
620 def init_inspector(self):
621 # Object inspector
621 # Object inspector
622 self.inspector = oinspect.Inspector(oinspect.InspectColors,
622 self.inspector = oinspect.Inspector(oinspect.InspectColors,
623 PyColorize.ANSICodeColors,
623 PyColorize.ANSICodeColors,
624 'NoColor',
624 'NoColor',
625 self.object_info_string_level)
625 self.object_info_string_level)
626
626
627 def init_io(self):
627 def init_io(self):
628 # This will just use sys.stdout and sys.stderr. If you want to
628 # This will just use sys.stdout and sys.stderr. If you want to
629 # override sys.stdout and sys.stderr themselves, you need to do that
629 # override sys.stdout and sys.stderr themselves, you need to do that
630 # *before* instantiating this class, because io holds onto
630 # *before* instantiating this class, because io holds onto
631 # references to the underlying streams.
631 # references to the underlying streams.
632 if sys.platform == 'win32' and self.has_readline:
632 if (sys.platform == 'win32' or sys.platform == 'cli') and self.has_readline:
633 io.stdout = io.stderr = io.IOStream(self.readline._outputfile)
633 io.stdout = io.stderr = io.IOStream(self.readline._outputfile)
634 else:
634 else:
635 io.stdout = io.IOStream(sys.stdout)
635 io.stdout = io.IOStream(sys.stdout)
636 io.stderr = io.IOStream(sys.stderr)
636 io.stderr = io.IOStream(sys.stderr)
637
637
638 def init_prompts(self):
638 def init_prompts(self):
639 self.prompt_manager = PromptManager(shell=self, config=self.config)
639 self.prompt_manager = PromptManager(shell=self, config=self.config)
640 self.configurables.append(self.prompt_manager)
640 self.configurables.append(self.prompt_manager)
641 # Set system prompts, so that scripts can decide if they are running
641 # Set system prompts, so that scripts can decide if they are running
642 # interactively.
642 # interactively.
643 sys.ps1 = 'In : '
643 sys.ps1 = 'In : '
644 sys.ps2 = '...: '
644 sys.ps2 = '...: '
645 sys.ps3 = 'Out: '
645 sys.ps3 = 'Out: '
646
646
647 def init_display_formatter(self):
647 def init_display_formatter(self):
648 self.display_formatter = DisplayFormatter(config=self.config)
648 self.display_formatter = DisplayFormatter(config=self.config)
649 self.configurables.append(self.display_formatter)
649 self.configurables.append(self.display_formatter)
650
650
651 def init_display_pub(self):
651 def init_display_pub(self):
652 self.display_pub = self.display_pub_class(config=self.config)
652 self.display_pub = self.display_pub_class(config=self.config)
653 self.configurables.append(self.display_pub)
653 self.configurables.append(self.display_pub)
654
654
655 def init_data_pub(self):
655 def init_data_pub(self):
656 if not self.data_pub_class:
656 if not self.data_pub_class:
657 self.data_pub = None
657 self.data_pub = None
658 return
658 return
659 self.data_pub = self.data_pub_class(config=self.config)
659 self.data_pub = self.data_pub_class(config=self.config)
660 self.configurables.append(self.data_pub)
660 self.configurables.append(self.data_pub)
661
661
662 def init_displayhook(self):
662 def init_displayhook(self):
663 # Initialize displayhook, set in/out prompts and printing system
663 # Initialize displayhook, set in/out prompts and printing system
664 self.displayhook = self.displayhook_class(
664 self.displayhook = self.displayhook_class(
665 config=self.config,
665 config=self.config,
666 shell=self,
666 shell=self,
667 cache_size=self.cache_size,
667 cache_size=self.cache_size,
668 )
668 )
669 self.configurables.append(self.displayhook)
669 self.configurables.append(self.displayhook)
670 # This is a context manager that installs/revmoes the displayhook at
670 # This is a context manager that installs/revmoes the displayhook at
671 # the appropriate time.
671 # the appropriate time.
672 self.display_trap = DisplayTrap(hook=self.displayhook)
672 self.display_trap = DisplayTrap(hook=self.displayhook)
673
673
674 def init_reload_doctest(self):
674 def init_reload_doctest(self):
675 # Do a proper resetting of doctest, including the necessary displayhook
675 # Do a proper resetting of doctest, including the necessary displayhook
676 # monkeypatching
676 # monkeypatching
677 try:
677 try:
678 doctest_reload()
678 doctest_reload()
679 except ImportError:
679 except ImportError:
680 warn("doctest module does not exist.")
680 warn("doctest module does not exist.")
681
681
682 def init_latextool(self):
682 def init_latextool(self):
683 """Configure LaTeXTool."""
683 """Configure LaTeXTool."""
684 cfg = LaTeXTool.instance(config=self.config)
684 cfg = LaTeXTool.instance(config=self.config)
685 if cfg not in self.configurables:
685 if cfg not in self.configurables:
686 self.configurables.append(cfg)
686 self.configurables.append(cfg)
687
687
688 def init_virtualenv(self):
688 def init_virtualenv(self):
689 """Add a virtualenv to sys.path so the user can import modules from it.
689 """Add a virtualenv to sys.path so the user can import modules from it.
690 This isn't perfect: it doesn't use the Python interpreter with which the
690 This isn't perfect: it doesn't use the Python interpreter with which the
691 virtualenv was built, and it ignores the --no-site-packages option. A
691 virtualenv was built, and it ignores the --no-site-packages option. A
692 warning will appear suggesting the user installs IPython in the
692 warning will appear suggesting the user installs IPython in the
693 virtualenv, but for many cases, it probably works well enough.
693 virtualenv, but for many cases, it probably works well enough.
694
694
695 Adapted from code snippets online.
695 Adapted from code snippets online.
696
696
697 http://blog.ufsoft.org/2009/1/29/ipython-and-virtualenv
697 http://blog.ufsoft.org/2009/1/29/ipython-and-virtualenv
698 """
698 """
699 if 'VIRTUAL_ENV' not in os.environ:
699 if 'VIRTUAL_ENV' not in os.environ:
700 # Not in a virtualenv
700 # Not in a virtualenv
701 return
701 return
702
702
703 if sys.executable.startswith(os.environ['VIRTUAL_ENV']):
703 if sys.executable.startswith(os.environ['VIRTUAL_ENV']):
704 # Running properly in the virtualenv, don't need to do anything
704 # Running properly in the virtualenv, don't need to do anything
705 return
705 return
706
706
707 warn("Attempting to work in a virtualenv. If you encounter problems, please "
707 warn("Attempting to work in a virtualenv. If you encounter problems, please "
708 "install IPython inside the virtualenv.\n")
708 "install IPython inside the virtualenv.\n")
709 if sys.platform == "win32":
709 if sys.platform == "win32":
710 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'Lib', 'site-packages')
710 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'Lib', 'site-packages')
711 else:
711 else:
712 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'lib',
712 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'lib',
713 'python%d.%d' % sys.version_info[:2], 'site-packages')
713 'python%d.%d' % sys.version_info[:2], 'site-packages')
714
714
715 import site
715 import site
716 sys.path.insert(0, virtual_env)
716 sys.path.insert(0, virtual_env)
717 site.addsitedir(virtual_env)
717 site.addsitedir(virtual_env)
718
718
719 #-------------------------------------------------------------------------
719 #-------------------------------------------------------------------------
720 # Things related to injections into the sys module
720 # Things related to injections into the sys module
721 #-------------------------------------------------------------------------
721 #-------------------------------------------------------------------------
722
722
723 def save_sys_module_state(self):
723 def save_sys_module_state(self):
724 """Save the state of hooks in the sys module.
724 """Save the state of hooks in the sys module.
725
725
726 This has to be called after self.user_module is created.
726 This has to be called after self.user_module is created.
727 """
727 """
728 self._orig_sys_module_state = {}
728 self._orig_sys_module_state = {}
729 self._orig_sys_module_state['stdin'] = sys.stdin
729 self._orig_sys_module_state['stdin'] = sys.stdin
730 self._orig_sys_module_state['stdout'] = sys.stdout
730 self._orig_sys_module_state['stdout'] = sys.stdout
731 self._orig_sys_module_state['stderr'] = sys.stderr
731 self._orig_sys_module_state['stderr'] = sys.stderr
732 self._orig_sys_module_state['excepthook'] = sys.excepthook
732 self._orig_sys_module_state['excepthook'] = sys.excepthook
733 self._orig_sys_modules_main_name = self.user_module.__name__
733 self._orig_sys_modules_main_name = self.user_module.__name__
734 self._orig_sys_modules_main_mod = sys.modules.get(self.user_module.__name__)
734 self._orig_sys_modules_main_mod = sys.modules.get(self.user_module.__name__)
735
735
736 def restore_sys_module_state(self):
736 def restore_sys_module_state(self):
737 """Restore the state of the sys module."""
737 """Restore the state of the sys module."""
738 try:
738 try:
739 for k, v in self._orig_sys_module_state.iteritems():
739 for k, v in self._orig_sys_module_state.iteritems():
740 setattr(sys, k, v)
740 setattr(sys, k, v)
741 except AttributeError:
741 except AttributeError:
742 pass
742 pass
743 # Reset what what done in self.init_sys_modules
743 # Reset what what done in self.init_sys_modules
744 if self._orig_sys_modules_main_mod is not None:
744 if self._orig_sys_modules_main_mod is not None:
745 sys.modules[self._orig_sys_modules_main_name] = self._orig_sys_modules_main_mod
745 sys.modules[self._orig_sys_modules_main_name] = self._orig_sys_modules_main_mod
746
746
747 #-------------------------------------------------------------------------
747 #-------------------------------------------------------------------------
748 # Things related to hooks
748 # Things related to hooks
749 #-------------------------------------------------------------------------
749 #-------------------------------------------------------------------------
750
750
751 def init_hooks(self):
751 def init_hooks(self):
752 # hooks holds pointers used for user-side customizations
752 # hooks holds pointers used for user-side customizations
753 self.hooks = Struct()
753 self.hooks = Struct()
754
754
755 self.strdispatchers = {}
755 self.strdispatchers = {}
756
756
757 # Set all default hooks, defined in the IPython.hooks module.
757 # Set all default hooks, defined in the IPython.hooks module.
758 hooks = IPython.core.hooks
758 hooks = IPython.core.hooks
759 for hook_name in hooks.__all__:
759 for hook_name in hooks.__all__:
760 # default hooks have priority 100, i.e. low; user hooks should have
760 # default hooks have priority 100, i.e. low; user hooks should have
761 # 0-100 priority
761 # 0-100 priority
762 self.set_hook(hook_name,getattr(hooks,hook_name), 100)
762 self.set_hook(hook_name,getattr(hooks,hook_name), 100)
763
763
764 def set_hook(self,name,hook, priority = 50, str_key = None, re_key = None):
764 def set_hook(self,name,hook, priority = 50, str_key = None, re_key = None):
765 """set_hook(name,hook) -> sets an internal IPython hook.
765 """set_hook(name,hook) -> sets an internal IPython hook.
766
766
767 IPython exposes some of its internal API as user-modifiable hooks. By
767 IPython exposes some of its internal API as user-modifiable hooks. By
768 adding your function to one of these hooks, you can modify IPython's
768 adding your function to one of these hooks, you can modify IPython's
769 behavior to call at runtime your own routines."""
769 behavior to call at runtime your own routines."""
770
770
771 # At some point in the future, this should validate the hook before it
771 # At some point in the future, this should validate the hook before it
772 # accepts it. Probably at least check that the hook takes the number
772 # accepts it. Probably at least check that the hook takes the number
773 # of args it's supposed to.
773 # of args it's supposed to.
774
774
775 f = types.MethodType(hook,self)
775 f = types.MethodType(hook,self)
776
776
777 # check if the hook is for strdispatcher first
777 # check if the hook is for strdispatcher first
778 if str_key is not None:
778 if str_key is not None:
779 sdp = self.strdispatchers.get(name, StrDispatch())
779 sdp = self.strdispatchers.get(name, StrDispatch())
780 sdp.add_s(str_key, f, priority )
780 sdp.add_s(str_key, f, priority )
781 self.strdispatchers[name] = sdp
781 self.strdispatchers[name] = sdp
782 return
782 return
783 if re_key is not None:
783 if re_key is not None:
784 sdp = self.strdispatchers.get(name, StrDispatch())
784 sdp = self.strdispatchers.get(name, StrDispatch())
785 sdp.add_re(re.compile(re_key), f, priority )
785 sdp.add_re(re.compile(re_key), f, priority )
786 self.strdispatchers[name] = sdp
786 self.strdispatchers[name] = sdp
787 return
787 return
788
788
789 dp = getattr(self.hooks, name, None)
789 dp = getattr(self.hooks, name, None)
790 if name not in IPython.core.hooks.__all__:
790 if name not in IPython.core.hooks.__all__:
791 print("Warning! Hook '%s' is not one of %s" % \
791 print("Warning! Hook '%s' is not one of %s" % \
792 (name, IPython.core.hooks.__all__ ))
792 (name, IPython.core.hooks.__all__ ))
793 if not dp:
793 if not dp:
794 dp = IPython.core.hooks.CommandChainDispatcher()
794 dp = IPython.core.hooks.CommandChainDispatcher()
795
795
796 try:
796 try:
797 dp.add(f,priority)
797 dp.add(f,priority)
798 except AttributeError:
798 except AttributeError:
799 # it was not commandchain, plain old func - replace
799 # it was not commandchain, plain old func - replace
800 dp = f
800 dp = f
801
801
802 setattr(self.hooks,name, dp)
802 setattr(self.hooks,name, dp)
803
803
804 def register_post_execute(self, func):
804 def register_post_execute(self, func):
805 """Register a function for calling after code execution.
805 """Register a function for calling after code execution.
806 """
806 """
807 if not callable(func):
807 if not callable(func):
808 raise ValueError('argument %s must be callable' % func)
808 raise ValueError('argument %s must be callable' % func)
809 self._post_execute[func] = True
809 self._post_execute[func] = True
810
810
811 #-------------------------------------------------------------------------
811 #-------------------------------------------------------------------------
812 # Things related to the "main" module
812 # Things related to the "main" module
813 #-------------------------------------------------------------------------
813 #-------------------------------------------------------------------------
814
814
815 def new_main_mod(self,ns=None):
815 def new_main_mod(self,ns=None):
816 """Return a new 'main' module object for user code execution.
816 """Return a new 'main' module object for user code execution.
817 """
817 """
818 main_mod = self._user_main_module
818 main_mod = self._user_main_module
819 init_fakemod_dict(main_mod,ns)
819 init_fakemod_dict(main_mod,ns)
820 return main_mod
820 return main_mod
821
821
822 def cache_main_mod(self,ns,fname):
822 def cache_main_mod(self,ns,fname):
823 """Cache a main module's namespace.
823 """Cache a main module's namespace.
824
824
825 When scripts are executed via %run, we must keep a reference to the
825 When scripts are executed via %run, we must keep a reference to the
826 namespace of their __main__ module (a FakeModule instance) around so
826 namespace of their __main__ module (a FakeModule instance) around so
827 that Python doesn't clear it, rendering objects defined therein
827 that Python doesn't clear it, rendering objects defined therein
828 useless.
828 useless.
829
829
830 This method keeps said reference in a private dict, keyed by the
830 This method keeps said reference in a private dict, keyed by the
831 absolute path of the module object (which corresponds to the script
831 absolute path of the module object (which corresponds to the script
832 path). This way, for multiple executions of the same script we only
832 path). This way, for multiple executions of the same script we only
833 keep one copy of the namespace (the last one), thus preventing memory
833 keep one copy of the namespace (the last one), thus preventing memory
834 leaks from old references while allowing the objects from the last
834 leaks from old references while allowing the objects from the last
835 execution to be accessible.
835 execution to be accessible.
836
836
837 Note: we can not allow the actual FakeModule instances to be deleted,
837 Note: we can not allow the actual FakeModule instances to be deleted,
838 because of how Python tears down modules (it hard-sets all their
838 because of how Python tears down modules (it hard-sets all their
839 references to None without regard for reference counts). This method
839 references to None without regard for reference counts). This method
840 must therefore make a *copy* of the given namespace, to allow the
840 must therefore make a *copy* of the given namespace, to allow the
841 original module's __dict__ to be cleared and reused.
841 original module's __dict__ to be cleared and reused.
842
842
843
843
844 Parameters
844 Parameters
845 ----------
845 ----------
846 ns : a namespace (a dict, typically)
846 ns : a namespace (a dict, typically)
847
847
848 fname : str
848 fname : str
849 Filename associated with the namespace.
849 Filename associated with the namespace.
850
850
851 Examples
851 Examples
852 --------
852 --------
853
853
854 In [10]: import IPython
854 In [10]: import IPython
855
855
856 In [11]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
856 In [11]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
857
857
858 In [12]: IPython.__file__ in _ip._main_ns_cache
858 In [12]: IPython.__file__ in _ip._main_ns_cache
859 Out[12]: True
859 Out[12]: True
860 """
860 """
861 self._main_ns_cache[os.path.abspath(fname)] = ns.copy()
861 self._main_ns_cache[os.path.abspath(fname)] = ns.copy()
862
862
863 def clear_main_mod_cache(self):
863 def clear_main_mod_cache(self):
864 """Clear the cache of main modules.
864 """Clear the cache of main modules.
865
865
866 Mainly for use by utilities like %reset.
866 Mainly for use by utilities like %reset.
867
867
868 Examples
868 Examples
869 --------
869 --------
870
870
871 In [15]: import IPython
871 In [15]: import IPython
872
872
873 In [16]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
873 In [16]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
874
874
875 In [17]: len(_ip._main_ns_cache) > 0
875 In [17]: len(_ip._main_ns_cache) > 0
876 Out[17]: True
876 Out[17]: True
877
877
878 In [18]: _ip.clear_main_mod_cache()
878 In [18]: _ip.clear_main_mod_cache()
879
879
880 In [19]: len(_ip._main_ns_cache) == 0
880 In [19]: len(_ip._main_ns_cache) == 0
881 Out[19]: True
881 Out[19]: True
882 """
882 """
883 self._main_ns_cache.clear()
883 self._main_ns_cache.clear()
884
884
885 #-------------------------------------------------------------------------
885 #-------------------------------------------------------------------------
886 # Things related to debugging
886 # Things related to debugging
887 #-------------------------------------------------------------------------
887 #-------------------------------------------------------------------------
888
888
889 def init_pdb(self):
889 def init_pdb(self):
890 # Set calling of pdb on exceptions
890 # Set calling of pdb on exceptions
891 # self.call_pdb is a property
891 # self.call_pdb is a property
892 self.call_pdb = self.pdb
892 self.call_pdb = self.pdb
893
893
894 def _get_call_pdb(self):
894 def _get_call_pdb(self):
895 return self._call_pdb
895 return self._call_pdb
896
896
897 def _set_call_pdb(self,val):
897 def _set_call_pdb(self,val):
898
898
899 if val not in (0,1,False,True):
899 if val not in (0,1,False,True):
900 raise ValueError('new call_pdb value must be boolean')
900 raise ValueError('new call_pdb value must be boolean')
901
901
902 # store value in instance
902 # store value in instance
903 self._call_pdb = val
903 self._call_pdb = val
904
904
905 # notify the actual exception handlers
905 # notify the actual exception handlers
906 self.InteractiveTB.call_pdb = val
906 self.InteractiveTB.call_pdb = val
907
907
908 call_pdb = property(_get_call_pdb,_set_call_pdb,None,
908 call_pdb = property(_get_call_pdb,_set_call_pdb,None,
909 'Control auto-activation of pdb at exceptions')
909 'Control auto-activation of pdb at exceptions')
910
910
911 def debugger(self,force=False):
911 def debugger(self,force=False):
912 """Call the pydb/pdb debugger.
912 """Call the pydb/pdb debugger.
913
913
914 Keywords:
914 Keywords:
915
915
916 - force(False): by default, this routine checks the instance call_pdb
916 - force(False): by default, this routine checks the instance call_pdb
917 flag and does not actually invoke the debugger if the flag is false.
917 flag and does not actually invoke the debugger if the flag is false.
918 The 'force' option forces the debugger to activate even if the flag
918 The 'force' option forces the debugger to activate even if the flag
919 is false.
919 is false.
920 """
920 """
921
921
922 if not (force or self.call_pdb):
922 if not (force or self.call_pdb):
923 return
923 return
924
924
925 if not hasattr(sys,'last_traceback'):
925 if not hasattr(sys,'last_traceback'):
926 error('No traceback has been produced, nothing to debug.')
926 error('No traceback has been produced, nothing to debug.')
927 return
927 return
928
928
929 # use pydb if available
929 # use pydb if available
930 if debugger.has_pydb:
930 if debugger.has_pydb:
931 from pydb import pm
931 from pydb import pm
932 else:
932 else:
933 # fallback to our internal debugger
933 # fallback to our internal debugger
934 pm = lambda : self.InteractiveTB.debugger(force=True)
934 pm = lambda : self.InteractiveTB.debugger(force=True)
935
935
936 with self.readline_no_record:
936 with self.readline_no_record:
937 pm()
937 pm()
938
938
939 #-------------------------------------------------------------------------
939 #-------------------------------------------------------------------------
940 # Things related to IPython's various namespaces
940 # Things related to IPython's various namespaces
941 #-------------------------------------------------------------------------
941 #-------------------------------------------------------------------------
942 default_user_namespaces = True
942 default_user_namespaces = True
943
943
944 def init_create_namespaces(self, user_module=None, user_ns=None):
944 def init_create_namespaces(self, user_module=None, user_ns=None):
945 # Create the namespace where the user will operate. user_ns is
945 # Create the namespace where the user will operate. user_ns is
946 # normally the only one used, and it is passed to the exec calls as
946 # normally the only one used, and it is passed to the exec calls as
947 # the locals argument. But we do carry a user_global_ns namespace
947 # the locals argument. But we do carry a user_global_ns namespace
948 # given as the exec 'globals' argument, This is useful in embedding
948 # given as the exec 'globals' argument, This is useful in embedding
949 # situations where the ipython shell opens in a context where the
949 # situations where the ipython shell opens in a context where the
950 # distinction between locals and globals is meaningful. For
950 # distinction between locals and globals is meaningful. For
951 # non-embedded contexts, it is just the same object as the user_ns dict.
951 # non-embedded contexts, it is just the same object as the user_ns dict.
952
952
953 # FIXME. For some strange reason, __builtins__ is showing up at user
953 # FIXME. For some strange reason, __builtins__ is showing up at user
954 # level as a dict instead of a module. This is a manual fix, but I
954 # level as a dict instead of a module. This is a manual fix, but I
955 # should really track down where the problem is coming from. Alex
955 # should really track down where the problem is coming from. Alex
956 # Schmolck reported this problem first.
956 # Schmolck reported this problem first.
957
957
958 # A useful post by Alex Martelli on this topic:
958 # A useful post by Alex Martelli on this topic:
959 # Re: inconsistent value from __builtins__
959 # Re: inconsistent value from __builtins__
960 # Von: Alex Martelli <aleaxit@yahoo.com>
960 # Von: Alex Martelli <aleaxit@yahoo.com>
961 # Datum: Freitag 01 Oktober 2004 04:45:34 nachmittags/abends
961 # Datum: Freitag 01 Oktober 2004 04:45:34 nachmittags/abends
962 # Gruppen: comp.lang.python
962 # Gruppen: comp.lang.python
963
963
964 # Michael Hohn <hohn@hooknose.lbl.gov> wrote:
964 # Michael Hohn <hohn@hooknose.lbl.gov> wrote:
965 # > >>> print type(builtin_check.get_global_binding('__builtins__'))
965 # > >>> print type(builtin_check.get_global_binding('__builtins__'))
966 # > <type 'dict'>
966 # > <type 'dict'>
967 # > >>> print type(__builtins__)
967 # > >>> print type(__builtins__)
968 # > <type 'module'>
968 # > <type 'module'>
969 # > Is this difference in return value intentional?
969 # > Is this difference in return value intentional?
970
970
971 # Well, it's documented that '__builtins__' can be either a dictionary
971 # Well, it's documented that '__builtins__' can be either a dictionary
972 # or a module, and it's been that way for a long time. Whether it's
972 # or a module, and it's been that way for a long time. Whether it's
973 # intentional (or sensible), I don't know. In any case, the idea is
973 # intentional (or sensible), I don't know. In any case, the idea is
974 # that if you need to access the built-in namespace directly, you
974 # that if you need to access the built-in namespace directly, you
975 # should start with "import __builtin__" (note, no 's') which will
975 # should start with "import __builtin__" (note, no 's') which will
976 # definitely give you a module. Yeah, it's somewhat confusing:-(.
976 # definitely give you a module. Yeah, it's somewhat confusing:-(.
977
977
978 # These routines return a properly built module and dict as needed by
978 # These routines return a properly built module and dict as needed by
979 # the rest of the code, and can also be used by extension writers to
979 # the rest of the code, and can also be used by extension writers to
980 # generate properly initialized namespaces.
980 # generate properly initialized namespaces.
981 if (user_ns is not None) or (user_module is not None):
981 if (user_ns is not None) or (user_module is not None):
982 self.default_user_namespaces = False
982 self.default_user_namespaces = False
983 self.user_module, self.user_ns = self.prepare_user_module(user_module, user_ns)
983 self.user_module, self.user_ns = self.prepare_user_module(user_module, user_ns)
984
984
985 # A record of hidden variables we have added to the user namespace, so
985 # A record of hidden variables we have added to the user namespace, so
986 # we can list later only variables defined in actual interactive use.
986 # we can list later only variables defined in actual interactive use.
987 self.user_ns_hidden = set()
987 self.user_ns_hidden = set()
988
988
989 # Now that FakeModule produces a real module, we've run into a nasty
989 # Now that FakeModule produces a real module, we've run into a nasty
990 # problem: after script execution (via %run), the module where the user
990 # problem: after script execution (via %run), the module where the user
991 # code ran is deleted. Now that this object is a true module (needed
991 # code ran is deleted. Now that this object is a true module (needed
992 # so docetst and other tools work correctly), the Python module
992 # so docetst and other tools work correctly), the Python module
993 # teardown mechanism runs over it, and sets to None every variable
993 # teardown mechanism runs over it, and sets to None every variable
994 # present in that module. Top-level references to objects from the
994 # present in that module. Top-level references to objects from the
995 # script survive, because the user_ns is updated with them. However,
995 # script survive, because the user_ns is updated with them. However,
996 # calling functions defined in the script that use other things from
996 # calling functions defined in the script that use other things from
997 # the script will fail, because the function's closure had references
997 # the script will fail, because the function's closure had references
998 # to the original objects, which are now all None. So we must protect
998 # to the original objects, which are now all None. So we must protect
999 # these modules from deletion by keeping a cache.
999 # these modules from deletion by keeping a cache.
1000 #
1000 #
1001 # To avoid keeping stale modules around (we only need the one from the
1001 # To avoid keeping stale modules around (we only need the one from the
1002 # last run), we use a dict keyed with the full path to the script, so
1002 # last run), we use a dict keyed with the full path to the script, so
1003 # only the last version of the module is held in the cache. Note,
1003 # only the last version of the module is held in the cache. Note,
1004 # however, that we must cache the module *namespace contents* (their
1004 # however, that we must cache the module *namespace contents* (their
1005 # __dict__). Because if we try to cache the actual modules, old ones
1005 # __dict__). Because if we try to cache the actual modules, old ones
1006 # (uncached) could be destroyed while still holding references (such as
1006 # (uncached) could be destroyed while still holding references (such as
1007 # those held by GUI objects that tend to be long-lived)>
1007 # those held by GUI objects that tend to be long-lived)>
1008 #
1008 #
1009 # The %reset command will flush this cache. See the cache_main_mod()
1009 # The %reset command will flush this cache. See the cache_main_mod()
1010 # and clear_main_mod_cache() methods for details on use.
1010 # and clear_main_mod_cache() methods for details on use.
1011
1011
1012 # This is the cache used for 'main' namespaces
1012 # This is the cache used for 'main' namespaces
1013 self._main_ns_cache = {}
1013 self._main_ns_cache = {}
1014 # And this is the single instance of FakeModule whose __dict__ we keep
1014 # And this is the single instance of FakeModule whose __dict__ we keep
1015 # copying and clearing for reuse on each %run
1015 # copying and clearing for reuse on each %run
1016 self._user_main_module = FakeModule()
1016 self._user_main_module = FakeModule()
1017
1017
1018 # A table holding all the namespaces IPython deals with, so that
1018 # A table holding all the namespaces IPython deals with, so that
1019 # introspection facilities can search easily.
1019 # introspection facilities can search easily.
1020 self.ns_table = {'user_global':self.user_module.__dict__,
1020 self.ns_table = {'user_global':self.user_module.__dict__,
1021 'user_local':self.user_ns,
1021 'user_local':self.user_ns,
1022 'builtin':builtin_mod.__dict__
1022 'builtin':builtin_mod.__dict__
1023 }
1023 }
1024
1024
1025 @property
1025 @property
1026 def user_global_ns(self):
1026 def user_global_ns(self):
1027 return self.user_module.__dict__
1027 return self.user_module.__dict__
1028
1028
1029 def prepare_user_module(self, user_module=None, user_ns=None):
1029 def prepare_user_module(self, user_module=None, user_ns=None):
1030 """Prepare the module and namespace in which user code will be run.
1030 """Prepare the module and namespace in which user code will be run.
1031
1031
1032 When IPython is started normally, both parameters are None: a new module
1032 When IPython is started normally, both parameters are None: a new module
1033 is created automatically, and its __dict__ used as the namespace.
1033 is created automatically, and its __dict__ used as the namespace.
1034
1034
1035 If only user_module is provided, its __dict__ is used as the namespace.
1035 If only user_module is provided, its __dict__ is used as the namespace.
1036 If only user_ns is provided, a dummy module is created, and user_ns
1036 If only user_ns is provided, a dummy module is created, and user_ns
1037 becomes the global namespace. If both are provided (as they may be
1037 becomes the global namespace. If both are provided (as they may be
1038 when embedding), user_ns is the local namespace, and user_module
1038 when embedding), user_ns is the local namespace, and user_module
1039 provides the global namespace.
1039 provides the global namespace.
1040
1040
1041 Parameters
1041 Parameters
1042 ----------
1042 ----------
1043 user_module : module, optional
1043 user_module : module, optional
1044 The current user module in which IPython is being run. If None,
1044 The current user module in which IPython is being run. If None,
1045 a clean module will be created.
1045 a clean module will be created.
1046 user_ns : dict, optional
1046 user_ns : dict, optional
1047 A namespace in which to run interactive commands.
1047 A namespace in which to run interactive commands.
1048
1048
1049 Returns
1049 Returns
1050 -------
1050 -------
1051 A tuple of user_module and user_ns, each properly initialised.
1051 A tuple of user_module and user_ns, each properly initialised.
1052 """
1052 """
1053 if user_module is None and user_ns is not None:
1053 if user_module is None and user_ns is not None:
1054 user_ns.setdefault("__name__", "__main__")
1054 user_ns.setdefault("__name__", "__main__")
1055 class DummyMod(object):
1055 class DummyMod(object):
1056 "A dummy module used for IPython's interactive namespace."
1056 "A dummy module used for IPython's interactive namespace."
1057 pass
1057 pass
1058 user_module = DummyMod()
1058 user_module = DummyMod()
1059 user_module.__dict__ = user_ns
1059 user_module.__dict__ = user_ns
1060
1060
1061 if user_module is None:
1061 if user_module is None:
1062 user_module = types.ModuleType("__main__",
1062 user_module = types.ModuleType("__main__",
1063 doc="Automatically created module for IPython interactive environment")
1063 doc="Automatically created module for IPython interactive environment")
1064
1064
1065 # We must ensure that __builtin__ (without the final 's') is always
1065 # We must ensure that __builtin__ (without the final 's') is always
1066 # available and pointing to the __builtin__ *module*. For more details:
1066 # available and pointing to the __builtin__ *module*. For more details:
1067 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1067 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1068 user_module.__dict__.setdefault('__builtin__', builtin_mod)
1068 user_module.__dict__.setdefault('__builtin__', builtin_mod)
1069 user_module.__dict__.setdefault('__builtins__', builtin_mod)
1069 user_module.__dict__.setdefault('__builtins__', builtin_mod)
1070
1070
1071 if user_ns is None:
1071 if user_ns is None:
1072 user_ns = user_module.__dict__
1072 user_ns = user_module.__dict__
1073
1073
1074 return user_module, user_ns
1074 return user_module, user_ns
1075
1075
1076 def init_sys_modules(self):
1076 def init_sys_modules(self):
1077 # We need to insert into sys.modules something that looks like a
1077 # We need to insert into sys.modules something that looks like a
1078 # module but which accesses the IPython namespace, for shelve and
1078 # module but which accesses the IPython namespace, for shelve and
1079 # pickle to work interactively. Normally they rely on getting
1079 # pickle to work interactively. Normally they rely on getting
1080 # everything out of __main__, but for embedding purposes each IPython
1080 # everything out of __main__, but for embedding purposes each IPython
1081 # instance has its own private namespace, so we can't go shoving
1081 # instance has its own private namespace, so we can't go shoving
1082 # everything into __main__.
1082 # everything into __main__.
1083
1083
1084 # note, however, that we should only do this for non-embedded
1084 # note, however, that we should only do this for non-embedded
1085 # ipythons, which really mimic the __main__.__dict__ with their own
1085 # ipythons, which really mimic the __main__.__dict__ with their own
1086 # namespace. Embedded instances, on the other hand, should not do
1086 # namespace. Embedded instances, on the other hand, should not do
1087 # this because they need to manage the user local/global namespaces
1087 # this because they need to manage the user local/global namespaces
1088 # only, but they live within a 'normal' __main__ (meaning, they
1088 # only, but they live within a 'normal' __main__ (meaning, they
1089 # shouldn't overtake the execution environment of the script they're
1089 # shouldn't overtake the execution environment of the script they're
1090 # embedded in).
1090 # embedded in).
1091
1091
1092 # This is overridden in the InteractiveShellEmbed subclass to a no-op.
1092 # This is overridden in the InteractiveShellEmbed subclass to a no-op.
1093 main_name = self.user_module.__name__
1093 main_name = self.user_module.__name__
1094 sys.modules[main_name] = self.user_module
1094 sys.modules[main_name] = self.user_module
1095
1095
1096 def init_user_ns(self):
1096 def init_user_ns(self):
1097 """Initialize all user-visible namespaces to their minimum defaults.
1097 """Initialize all user-visible namespaces to their minimum defaults.
1098
1098
1099 Certain history lists are also initialized here, as they effectively
1099 Certain history lists are also initialized here, as they effectively
1100 act as user namespaces.
1100 act as user namespaces.
1101
1101
1102 Notes
1102 Notes
1103 -----
1103 -----
1104 All data structures here are only filled in, they are NOT reset by this
1104 All data structures here are only filled in, they are NOT reset by this
1105 method. If they were not empty before, data will simply be added to
1105 method. If they were not empty before, data will simply be added to
1106 therm.
1106 therm.
1107 """
1107 """
1108 # This function works in two parts: first we put a few things in
1108 # This function works in two parts: first we put a few things in
1109 # user_ns, and we sync that contents into user_ns_hidden so that these
1109 # user_ns, and we sync that contents into user_ns_hidden so that these
1110 # initial variables aren't shown by %who. After the sync, we add the
1110 # initial variables aren't shown by %who. After the sync, we add the
1111 # rest of what we *do* want the user to see with %who even on a new
1111 # rest of what we *do* want the user to see with %who even on a new
1112 # session (probably nothing, so theye really only see their own stuff)
1112 # session (probably nothing, so theye really only see their own stuff)
1113
1113
1114 # The user dict must *always* have a __builtin__ reference to the
1114 # The user dict must *always* have a __builtin__ reference to the
1115 # Python standard __builtin__ namespace, which must be imported.
1115 # Python standard __builtin__ namespace, which must be imported.
1116 # This is so that certain operations in prompt evaluation can be
1116 # This is so that certain operations in prompt evaluation can be
1117 # reliably executed with builtins. Note that we can NOT use
1117 # reliably executed with builtins. Note that we can NOT use
1118 # __builtins__ (note the 's'), because that can either be a dict or a
1118 # __builtins__ (note the 's'), because that can either be a dict or a
1119 # module, and can even mutate at runtime, depending on the context
1119 # module, and can even mutate at runtime, depending on the context
1120 # (Python makes no guarantees on it). In contrast, __builtin__ is
1120 # (Python makes no guarantees on it). In contrast, __builtin__ is
1121 # always a module object, though it must be explicitly imported.
1121 # always a module object, though it must be explicitly imported.
1122
1122
1123 # For more details:
1123 # For more details:
1124 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1124 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1125 ns = dict()
1125 ns = dict()
1126
1126
1127 # Put 'help' in the user namespace
1127 # Put 'help' in the user namespace
1128 try:
1128 try:
1129 from site import _Helper
1129 from site import _Helper
1130 ns['help'] = _Helper()
1130 ns['help'] = _Helper()
1131 except ImportError:
1131 except ImportError:
1132 warn('help() not available - check site.py')
1132 warn('help() not available - check site.py')
1133
1133
1134 # make global variables for user access to the histories
1134 # make global variables for user access to the histories
1135 ns['_ih'] = self.history_manager.input_hist_parsed
1135 ns['_ih'] = self.history_manager.input_hist_parsed
1136 ns['_oh'] = self.history_manager.output_hist
1136 ns['_oh'] = self.history_manager.output_hist
1137 ns['_dh'] = self.history_manager.dir_hist
1137 ns['_dh'] = self.history_manager.dir_hist
1138
1138
1139 ns['_sh'] = shadowns
1139 ns['_sh'] = shadowns
1140
1140
1141 # user aliases to input and output histories. These shouldn't show up
1141 # user aliases to input and output histories. These shouldn't show up
1142 # in %who, as they can have very large reprs.
1142 # in %who, as they can have very large reprs.
1143 ns['In'] = self.history_manager.input_hist_parsed
1143 ns['In'] = self.history_manager.input_hist_parsed
1144 ns['Out'] = self.history_manager.output_hist
1144 ns['Out'] = self.history_manager.output_hist
1145
1145
1146 # Store myself as the public api!!!
1146 # Store myself as the public api!!!
1147 ns['get_ipython'] = self.get_ipython
1147 ns['get_ipython'] = self.get_ipython
1148
1148
1149 ns['exit'] = self.exiter
1149 ns['exit'] = self.exiter
1150 ns['quit'] = self.exiter
1150 ns['quit'] = self.exiter
1151
1151
1152 # Sync what we've added so far to user_ns_hidden so these aren't seen
1152 # Sync what we've added so far to user_ns_hidden so these aren't seen
1153 # by %who
1153 # by %who
1154 self.user_ns_hidden.update(ns)
1154 self.user_ns_hidden.update(ns)
1155
1155
1156 # Anything put into ns now would show up in %who. Think twice before
1156 # Anything put into ns now would show up in %who. Think twice before
1157 # putting anything here, as we really want %who to show the user their
1157 # putting anything here, as we really want %who to show the user their
1158 # stuff, not our variables.
1158 # stuff, not our variables.
1159
1159
1160 # Finally, update the real user's namespace
1160 # Finally, update the real user's namespace
1161 self.user_ns.update(ns)
1161 self.user_ns.update(ns)
1162
1162
1163 @property
1163 @property
1164 def all_ns_refs(self):
1164 def all_ns_refs(self):
1165 """Get a list of references to all the namespace dictionaries in which
1165 """Get a list of references to all the namespace dictionaries in which
1166 IPython might store a user-created object.
1166 IPython might store a user-created object.
1167
1167
1168 Note that this does not include the displayhook, which also caches
1168 Note that this does not include the displayhook, which also caches
1169 objects from the output."""
1169 objects from the output."""
1170 return [self.user_ns, self.user_global_ns,
1170 return [self.user_ns, self.user_global_ns,
1171 self._user_main_module.__dict__] + self._main_ns_cache.values()
1171 self._user_main_module.__dict__] + self._main_ns_cache.values()
1172
1172
1173 def reset(self, new_session=True):
1173 def reset(self, new_session=True):
1174 """Clear all internal namespaces, and attempt to release references to
1174 """Clear all internal namespaces, and attempt to release references to
1175 user objects.
1175 user objects.
1176
1176
1177 If new_session is True, a new history session will be opened.
1177 If new_session is True, a new history session will be opened.
1178 """
1178 """
1179 # Clear histories
1179 # Clear histories
1180 self.history_manager.reset(new_session)
1180 self.history_manager.reset(new_session)
1181 # Reset counter used to index all histories
1181 # Reset counter used to index all histories
1182 if new_session:
1182 if new_session:
1183 self.execution_count = 1
1183 self.execution_count = 1
1184
1184
1185 # Flush cached output items
1185 # Flush cached output items
1186 if self.displayhook.do_full_cache:
1186 if self.displayhook.do_full_cache:
1187 self.displayhook.flush()
1187 self.displayhook.flush()
1188
1188
1189 # The main execution namespaces must be cleared very carefully,
1189 # The main execution namespaces must be cleared very carefully,
1190 # skipping the deletion of the builtin-related keys, because doing so
1190 # skipping the deletion of the builtin-related keys, because doing so
1191 # would cause errors in many object's __del__ methods.
1191 # would cause errors in many object's __del__ methods.
1192 if self.user_ns is not self.user_global_ns:
1192 if self.user_ns is not self.user_global_ns:
1193 self.user_ns.clear()
1193 self.user_ns.clear()
1194 ns = self.user_global_ns
1194 ns = self.user_global_ns
1195 drop_keys = set(ns.keys())
1195 drop_keys = set(ns.keys())
1196 drop_keys.discard('__builtin__')
1196 drop_keys.discard('__builtin__')
1197 drop_keys.discard('__builtins__')
1197 drop_keys.discard('__builtins__')
1198 drop_keys.discard('__name__')
1198 drop_keys.discard('__name__')
1199 for k in drop_keys:
1199 for k in drop_keys:
1200 del ns[k]
1200 del ns[k]
1201
1201
1202 self.user_ns_hidden.clear()
1202 self.user_ns_hidden.clear()
1203
1203
1204 # Restore the user namespaces to minimal usability
1204 # Restore the user namespaces to minimal usability
1205 self.init_user_ns()
1205 self.init_user_ns()
1206
1206
1207 # Restore the default and user aliases
1207 # Restore the default and user aliases
1208 self.alias_manager.clear_aliases()
1208 self.alias_manager.clear_aliases()
1209 self.alias_manager.init_aliases()
1209 self.alias_manager.init_aliases()
1210
1210
1211 # Flush the private list of module references kept for script
1211 # Flush the private list of module references kept for script
1212 # execution protection
1212 # execution protection
1213 self.clear_main_mod_cache()
1213 self.clear_main_mod_cache()
1214
1214
1215 # Clear out the namespace from the last %run
1215 # Clear out the namespace from the last %run
1216 self.new_main_mod()
1216 self.new_main_mod()
1217
1217
1218 def del_var(self, varname, by_name=False):
1218 def del_var(self, varname, by_name=False):
1219 """Delete a variable from the various namespaces, so that, as
1219 """Delete a variable from the various namespaces, so that, as
1220 far as possible, we're not keeping any hidden references to it.
1220 far as possible, we're not keeping any hidden references to it.
1221
1221
1222 Parameters
1222 Parameters
1223 ----------
1223 ----------
1224 varname : str
1224 varname : str
1225 The name of the variable to delete.
1225 The name of the variable to delete.
1226 by_name : bool
1226 by_name : bool
1227 If True, delete variables with the given name in each
1227 If True, delete variables with the given name in each
1228 namespace. If False (default), find the variable in the user
1228 namespace. If False (default), find the variable in the user
1229 namespace, and delete references to it.
1229 namespace, and delete references to it.
1230 """
1230 """
1231 if varname in ('__builtin__', '__builtins__'):
1231 if varname in ('__builtin__', '__builtins__'):
1232 raise ValueError("Refusing to delete %s" % varname)
1232 raise ValueError("Refusing to delete %s" % varname)
1233
1233
1234 ns_refs = self.all_ns_refs
1234 ns_refs = self.all_ns_refs
1235
1235
1236 if by_name: # Delete by name
1236 if by_name: # Delete by name
1237 for ns in ns_refs:
1237 for ns in ns_refs:
1238 try:
1238 try:
1239 del ns[varname]
1239 del ns[varname]
1240 except KeyError:
1240 except KeyError:
1241 pass
1241 pass
1242 else: # Delete by object
1242 else: # Delete by object
1243 try:
1243 try:
1244 obj = self.user_ns[varname]
1244 obj = self.user_ns[varname]
1245 except KeyError:
1245 except KeyError:
1246 raise NameError("name '%s' is not defined" % varname)
1246 raise NameError("name '%s' is not defined" % varname)
1247 # Also check in output history
1247 # Also check in output history
1248 ns_refs.append(self.history_manager.output_hist)
1248 ns_refs.append(self.history_manager.output_hist)
1249 for ns in ns_refs:
1249 for ns in ns_refs:
1250 to_delete = [n for n, o in ns.iteritems() if o is obj]
1250 to_delete = [n for n, o in ns.iteritems() if o is obj]
1251 for name in to_delete:
1251 for name in to_delete:
1252 del ns[name]
1252 del ns[name]
1253
1253
1254 # displayhook keeps extra references, but not in a dictionary
1254 # displayhook keeps extra references, but not in a dictionary
1255 for name in ('_', '__', '___'):
1255 for name in ('_', '__', '___'):
1256 if getattr(self.displayhook, name) is obj:
1256 if getattr(self.displayhook, name) is obj:
1257 setattr(self.displayhook, name, None)
1257 setattr(self.displayhook, name, None)
1258
1258
1259 def reset_selective(self, regex=None):
1259 def reset_selective(self, regex=None):
1260 """Clear selective variables from internal namespaces based on a
1260 """Clear selective variables from internal namespaces based on a
1261 specified regular expression.
1261 specified regular expression.
1262
1262
1263 Parameters
1263 Parameters
1264 ----------
1264 ----------
1265 regex : string or compiled pattern, optional
1265 regex : string or compiled pattern, optional
1266 A regular expression pattern that will be used in searching
1266 A regular expression pattern that will be used in searching
1267 variable names in the users namespaces.
1267 variable names in the users namespaces.
1268 """
1268 """
1269 if regex is not None:
1269 if regex is not None:
1270 try:
1270 try:
1271 m = re.compile(regex)
1271 m = re.compile(regex)
1272 except TypeError:
1272 except TypeError:
1273 raise TypeError('regex must be a string or compiled pattern')
1273 raise TypeError('regex must be a string or compiled pattern')
1274 # Search for keys in each namespace that match the given regex
1274 # Search for keys in each namespace that match the given regex
1275 # If a match is found, delete the key/value pair.
1275 # If a match is found, delete the key/value pair.
1276 for ns in self.all_ns_refs:
1276 for ns in self.all_ns_refs:
1277 for var in ns:
1277 for var in ns:
1278 if m.search(var):
1278 if m.search(var):
1279 del ns[var]
1279 del ns[var]
1280
1280
1281 def push(self, variables, interactive=True):
1281 def push(self, variables, interactive=True):
1282 """Inject a group of variables into the IPython user namespace.
1282 """Inject a group of variables into the IPython user namespace.
1283
1283
1284 Parameters
1284 Parameters
1285 ----------
1285 ----------
1286 variables : dict, str or list/tuple of str
1286 variables : dict, str or list/tuple of str
1287 The variables to inject into the user's namespace. If a dict, a
1287 The variables to inject into the user's namespace. If a dict, a
1288 simple update is done. If a str, the string is assumed to have
1288 simple update is done. If a str, the string is assumed to have
1289 variable names separated by spaces. A list/tuple of str can also
1289 variable names separated by spaces. A list/tuple of str can also
1290 be used to give the variable names. If just the variable names are
1290 be used to give the variable names. If just the variable names are
1291 give (list/tuple/str) then the variable values looked up in the
1291 give (list/tuple/str) then the variable values looked up in the
1292 callers frame.
1292 callers frame.
1293 interactive : bool
1293 interactive : bool
1294 If True (default), the variables will be listed with the ``who``
1294 If True (default), the variables will be listed with the ``who``
1295 magic.
1295 magic.
1296 """
1296 """
1297 vdict = None
1297 vdict = None
1298
1298
1299 # We need a dict of name/value pairs to do namespace updates.
1299 # We need a dict of name/value pairs to do namespace updates.
1300 if isinstance(variables, dict):
1300 if isinstance(variables, dict):
1301 vdict = variables
1301 vdict = variables
1302 elif isinstance(variables, (basestring, list, tuple)):
1302 elif isinstance(variables, (basestring, list, tuple)):
1303 if isinstance(variables, basestring):
1303 if isinstance(variables, basestring):
1304 vlist = variables.split()
1304 vlist = variables.split()
1305 else:
1305 else:
1306 vlist = variables
1306 vlist = variables
1307 vdict = {}
1307 vdict = {}
1308 cf = sys._getframe(1)
1308 cf = sys._getframe(1)
1309 for name in vlist:
1309 for name in vlist:
1310 try:
1310 try:
1311 vdict[name] = eval(name, cf.f_globals, cf.f_locals)
1311 vdict[name] = eval(name, cf.f_globals, cf.f_locals)
1312 except:
1312 except:
1313 print('Could not get variable %s from %s' %
1313 print('Could not get variable %s from %s' %
1314 (name,cf.f_code.co_name))
1314 (name,cf.f_code.co_name))
1315 else:
1315 else:
1316 raise ValueError('variables must be a dict/str/list/tuple')
1316 raise ValueError('variables must be a dict/str/list/tuple')
1317
1317
1318 # Propagate variables to user namespace
1318 # Propagate variables to user namespace
1319 self.user_ns.update(vdict)
1319 self.user_ns.update(vdict)
1320
1320
1321 # And configure interactive visibility
1321 # And configure interactive visibility
1322 user_ns_hidden = self.user_ns_hidden
1322 user_ns_hidden = self.user_ns_hidden
1323 if interactive:
1323 if interactive:
1324 user_ns_hidden.difference_update(vdict)
1324 user_ns_hidden.difference_update(vdict)
1325 else:
1325 else:
1326 user_ns_hidden.update(vdict)
1326 user_ns_hidden.update(vdict)
1327
1327
1328 def drop_by_id(self, variables):
1328 def drop_by_id(self, variables):
1329 """Remove a dict of variables from the user namespace, if they are the
1329 """Remove a dict of variables from the user namespace, if they are the
1330 same as the values in the dictionary.
1330 same as the values in the dictionary.
1331
1331
1332 This is intended for use by extensions: variables that they've added can
1332 This is intended for use by extensions: variables that they've added can
1333 be taken back out if they are unloaded, without removing any that the
1333 be taken back out if they are unloaded, without removing any that the
1334 user has overwritten.
1334 user has overwritten.
1335
1335
1336 Parameters
1336 Parameters
1337 ----------
1337 ----------
1338 variables : dict
1338 variables : dict
1339 A dictionary mapping object names (as strings) to the objects.
1339 A dictionary mapping object names (as strings) to the objects.
1340 """
1340 """
1341 for name, obj in variables.iteritems():
1341 for name, obj in variables.iteritems():
1342 if name in self.user_ns and self.user_ns[name] is obj:
1342 if name in self.user_ns and self.user_ns[name] is obj:
1343 del self.user_ns[name]
1343 del self.user_ns[name]
1344 self.user_ns_hidden.discard(name)
1344 self.user_ns_hidden.discard(name)
1345
1345
1346 #-------------------------------------------------------------------------
1346 #-------------------------------------------------------------------------
1347 # Things related to object introspection
1347 # Things related to object introspection
1348 #-------------------------------------------------------------------------
1348 #-------------------------------------------------------------------------
1349
1349
1350 def _ofind(self, oname, namespaces=None):
1350 def _ofind(self, oname, namespaces=None):
1351 """Find an object in the available namespaces.
1351 """Find an object in the available namespaces.
1352
1352
1353 self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic
1353 self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic
1354
1354
1355 Has special code to detect magic functions.
1355 Has special code to detect magic functions.
1356 """
1356 """
1357 oname = oname.strip()
1357 oname = oname.strip()
1358 #print '1- oname: <%r>' % oname # dbg
1358 #print '1- oname: <%r>' % oname # dbg
1359 if not oname.startswith(ESC_MAGIC) and \
1359 if not oname.startswith(ESC_MAGIC) and \
1360 not oname.startswith(ESC_MAGIC2) and \
1360 not oname.startswith(ESC_MAGIC2) and \
1361 not py3compat.isidentifier(oname, dotted=True):
1361 not py3compat.isidentifier(oname, dotted=True):
1362 return dict(found=False)
1362 return dict(found=False)
1363
1363
1364 alias_ns = None
1364 alias_ns = None
1365 if namespaces is None:
1365 if namespaces is None:
1366 # Namespaces to search in:
1366 # Namespaces to search in:
1367 # Put them in a list. The order is important so that we
1367 # Put them in a list. The order is important so that we
1368 # find things in the same order that Python finds them.
1368 # find things in the same order that Python finds them.
1369 namespaces = [ ('Interactive', self.user_ns),
1369 namespaces = [ ('Interactive', self.user_ns),
1370 ('Interactive (global)', self.user_global_ns),
1370 ('Interactive (global)', self.user_global_ns),
1371 ('Python builtin', builtin_mod.__dict__),
1371 ('Python builtin', builtin_mod.__dict__),
1372 ('Alias', self.alias_manager.alias_table),
1372 ('Alias', self.alias_manager.alias_table),
1373 ]
1373 ]
1374 alias_ns = self.alias_manager.alias_table
1374 alias_ns = self.alias_manager.alias_table
1375
1375
1376 # initialize results to 'null'
1376 # initialize results to 'null'
1377 found = False; obj = None; ospace = None; ds = None;
1377 found = False; obj = None; ospace = None; ds = None;
1378 ismagic = False; isalias = False; parent = None
1378 ismagic = False; isalias = False; parent = None
1379
1379
1380 # We need to special-case 'print', which as of python2.6 registers as a
1380 # We need to special-case 'print', which as of python2.6 registers as a
1381 # function but should only be treated as one if print_function was
1381 # function but should only be treated as one if print_function was
1382 # loaded with a future import. In this case, just bail.
1382 # loaded with a future import. In this case, just bail.
1383 if (oname == 'print' and not py3compat.PY3 and not \
1383 if (oname == 'print' and not py3compat.PY3 and not \
1384 (self.compile.compiler_flags & __future__.CO_FUTURE_PRINT_FUNCTION)):
1384 (self.compile.compiler_flags & __future__.CO_FUTURE_PRINT_FUNCTION)):
1385 return {'found':found, 'obj':obj, 'namespace':ospace,
1385 return {'found':found, 'obj':obj, 'namespace':ospace,
1386 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1386 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1387
1387
1388 # Look for the given name by splitting it in parts. If the head is
1388 # Look for the given name by splitting it in parts. If the head is
1389 # found, then we look for all the remaining parts as members, and only
1389 # found, then we look for all the remaining parts as members, and only
1390 # declare success if we can find them all.
1390 # declare success if we can find them all.
1391 oname_parts = oname.split('.')
1391 oname_parts = oname.split('.')
1392 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1392 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1393 for nsname,ns in namespaces:
1393 for nsname,ns in namespaces:
1394 try:
1394 try:
1395 obj = ns[oname_head]
1395 obj = ns[oname_head]
1396 except KeyError:
1396 except KeyError:
1397 continue
1397 continue
1398 else:
1398 else:
1399 #print 'oname_rest:', oname_rest # dbg
1399 #print 'oname_rest:', oname_rest # dbg
1400 for part in oname_rest:
1400 for part in oname_rest:
1401 try:
1401 try:
1402 parent = obj
1402 parent = obj
1403 obj = getattr(obj,part)
1403 obj = getattr(obj,part)
1404 except:
1404 except:
1405 # Blanket except b/c some badly implemented objects
1405 # Blanket except b/c some badly implemented objects
1406 # allow __getattr__ to raise exceptions other than
1406 # allow __getattr__ to raise exceptions other than
1407 # AttributeError, which then crashes IPython.
1407 # AttributeError, which then crashes IPython.
1408 break
1408 break
1409 else:
1409 else:
1410 # If we finish the for loop (no break), we got all members
1410 # If we finish the for loop (no break), we got all members
1411 found = True
1411 found = True
1412 ospace = nsname
1412 ospace = nsname
1413 if ns == alias_ns:
1413 if ns == alias_ns:
1414 isalias = True
1414 isalias = True
1415 break # namespace loop
1415 break # namespace loop
1416
1416
1417 # Try to see if it's magic
1417 # Try to see if it's magic
1418 if not found:
1418 if not found:
1419 obj = None
1419 obj = None
1420 if oname.startswith(ESC_MAGIC2):
1420 if oname.startswith(ESC_MAGIC2):
1421 oname = oname.lstrip(ESC_MAGIC2)
1421 oname = oname.lstrip(ESC_MAGIC2)
1422 obj = self.find_cell_magic(oname)
1422 obj = self.find_cell_magic(oname)
1423 elif oname.startswith(ESC_MAGIC):
1423 elif oname.startswith(ESC_MAGIC):
1424 oname = oname.lstrip(ESC_MAGIC)
1424 oname = oname.lstrip(ESC_MAGIC)
1425 obj = self.find_line_magic(oname)
1425 obj = self.find_line_magic(oname)
1426 else:
1426 else:
1427 # search without prefix, so run? will find %run?
1427 # search without prefix, so run? will find %run?
1428 obj = self.find_line_magic(oname)
1428 obj = self.find_line_magic(oname)
1429 if obj is None:
1429 if obj is None:
1430 obj = self.find_cell_magic(oname)
1430 obj = self.find_cell_magic(oname)
1431 if obj is not None:
1431 if obj is not None:
1432 found = True
1432 found = True
1433 ospace = 'IPython internal'
1433 ospace = 'IPython internal'
1434 ismagic = True
1434 ismagic = True
1435
1435
1436 # Last try: special-case some literals like '', [], {}, etc:
1436 # Last try: special-case some literals like '', [], {}, etc:
1437 if not found and oname_head in ["''",'""','[]','{}','()']:
1437 if not found and oname_head in ["''",'""','[]','{}','()']:
1438 obj = eval(oname_head)
1438 obj = eval(oname_head)
1439 found = True
1439 found = True
1440 ospace = 'Interactive'
1440 ospace = 'Interactive'
1441
1441
1442 return {'found':found, 'obj':obj, 'namespace':ospace,
1442 return {'found':found, 'obj':obj, 'namespace':ospace,
1443 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1443 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1444
1444
1445 def _ofind_property(self, oname, info):
1445 def _ofind_property(self, oname, info):
1446 """Second part of object finding, to look for property details."""
1446 """Second part of object finding, to look for property details."""
1447 if info.found:
1447 if info.found:
1448 # Get the docstring of the class property if it exists.
1448 # Get the docstring of the class property if it exists.
1449 path = oname.split('.')
1449 path = oname.split('.')
1450 root = '.'.join(path[:-1])
1450 root = '.'.join(path[:-1])
1451 if info.parent is not None:
1451 if info.parent is not None:
1452 try:
1452 try:
1453 target = getattr(info.parent, '__class__')
1453 target = getattr(info.parent, '__class__')
1454 # The object belongs to a class instance.
1454 # The object belongs to a class instance.
1455 try:
1455 try:
1456 target = getattr(target, path[-1])
1456 target = getattr(target, path[-1])
1457 # The class defines the object.
1457 # The class defines the object.
1458 if isinstance(target, property):
1458 if isinstance(target, property):
1459 oname = root + '.__class__.' + path[-1]
1459 oname = root + '.__class__.' + path[-1]
1460 info = Struct(self._ofind(oname))
1460 info = Struct(self._ofind(oname))
1461 except AttributeError: pass
1461 except AttributeError: pass
1462 except AttributeError: pass
1462 except AttributeError: pass
1463
1463
1464 # We return either the new info or the unmodified input if the object
1464 # We return either the new info or the unmodified input if the object
1465 # hadn't been found
1465 # hadn't been found
1466 return info
1466 return info
1467
1467
1468 def _object_find(self, oname, namespaces=None):
1468 def _object_find(self, oname, namespaces=None):
1469 """Find an object and return a struct with info about it."""
1469 """Find an object and return a struct with info about it."""
1470 inf = Struct(self._ofind(oname, namespaces))
1470 inf = Struct(self._ofind(oname, namespaces))
1471 return Struct(self._ofind_property(oname, inf))
1471 return Struct(self._ofind_property(oname, inf))
1472
1472
1473 def _inspect(self, meth, oname, namespaces=None, **kw):
1473 def _inspect(self, meth, oname, namespaces=None, **kw):
1474 """Generic interface to the inspector system.
1474 """Generic interface to the inspector system.
1475
1475
1476 This function is meant to be called by pdef, pdoc & friends."""
1476 This function is meant to be called by pdef, pdoc & friends."""
1477 info = self._object_find(oname, namespaces)
1477 info = self._object_find(oname, namespaces)
1478 if info.found:
1478 if info.found:
1479 pmethod = getattr(self.inspector, meth)
1479 pmethod = getattr(self.inspector, meth)
1480 formatter = format_screen if info.ismagic else None
1480 formatter = format_screen if info.ismagic else None
1481 if meth == 'pdoc':
1481 if meth == 'pdoc':
1482 pmethod(info.obj, oname, formatter)
1482 pmethod(info.obj, oname, formatter)
1483 elif meth == 'pinfo':
1483 elif meth == 'pinfo':
1484 pmethod(info.obj, oname, formatter, info, **kw)
1484 pmethod(info.obj, oname, formatter, info, **kw)
1485 else:
1485 else:
1486 pmethod(info.obj, oname)
1486 pmethod(info.obj, oname)
1487 else:
1487 else:
1488 print('Object `%s` not found.' % oname)
1488 print('Object `%s` not found.' % oname)
1489 return 'not found' # so callers can take other action
1489 return 'not found' # so callers can take other action
1490
1490
1491 def object_inspect(self, oname, detail_level=0):
1491 def object_inspect(self, oname, detail_level=0):
1492 with self.builtin_trap:
1492 with self.builtin_trap:
1493 info = self._object_find(oname)
1493 info = self._object_find(oname)
1494 if info.found:
1494 if info.found:
1495 return self.inspector.info(info.obj, oname, info=info,
1495 return self.inspector.info(info.obj, oname, info=info,
1496 detail_level=detail_level
1496 detail_level=detail_level
1497 )
1497 )
1498 else:
1498 else:
1499 return oinspect.object_info(name=oname, found=False)
1499 return oinspect.object_info(name=oname, found=False)
1500
1500
1501 #-------------------------------------------------------------------------
1501 #-------------------------------------------------------------------------
1502 # Things related to history management
1502 # Things related to history management
1503 #-------------------------------------------------------------------------
1503 #-------------------------------------------------------------------------
1504
1504
1505 def init_history(self):
1505 def init_history(self):
1506 """Sets up the command history, and starts regular autosaves."""
1506 """Sets up the command history, and starts regular autosaves."""
1507 self.history_manager = HistoryManager(shell=self, config=self.config)
1507 self.history_manager = HistoryManager(shell=self, config=self.config)
1508 self.configurables.append(self.history_manager)
1508 self.configurables.append(self.history_manager)
1509
1509
1510 #-------------------------------------------------------------------------
1510 #-------------------------------------------------------------------------
1511 # Things related to exception handling and tracebacks (not debugging)
1511 # Things related to exception handling and tracebacks (not debugging)
1512 #-------------------------------------------------------------------------
1512 #-------------------------------------------------------------------------
1513
1513
1514 def init_traceback_handlers(self, custom_exceptions):
1514 def init_traceback_handlers(self, custom_exceptions):
1515 # Syntax error handler.
1515 # Syntax error handler.
1516 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
1516 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
1517
1517
1518 # The interactive one is initialized with an offset, meaning we always
1518 # The interactive one is initialized with an offset, meaning we always
1519 # want to remove the topmost item in the traceback, which is our own
1519 # want to remove the topmost item in the traceback, which is our own
1520 # internal code. Valid modes: ['Plain','Context','Verbose']
1520 # internal code. Valid modes: ['Plain','Context','Verbose']
1521 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1521 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1522 color_scheme='NoColor',
1522 color_scheme='NoColor',
1523 tb_offset = 1,
1523 tb_offset = 1,
1524 check_cache=self.compile.check_cache)
1524 check_cache=self.compile.check_cache)
1525
1525
1526 # The instance will store a pointer to the system-wide exception hook,
1526 # The instance will store a pointer to the system-wide exception hook,
1527 # so that runtime code (such as magics) can access it. This is because
1527 # so that runtime code (such as magics) can access it. This is because
1528 # during the read-eval loop, it may get temporarily overwritten.
1528 # during the read-eval loop, it may get temporarily overwritten.
1529 self.sys_excepthook = sys.excepthook
1529 self.sys_excepthook = sys.excepthook
1530
1530
1531 # and add any custom exception handlers the user may have specified
1531 # and add any custom exception handlers the user may have specified
1532 self.set_custom_exc(*custom_exceptions)
1532 self.set_custom_exc(*custom_exceptions)
1533
1533
1534 # Set the exception mode
1534 # Set the exception mode
1535 self.InteractiveTB.set_mode(mode=self.xmode)
1535 self.InteractiveTB.set_mode(mode=self.xmode)
1536
1536
1537 def set_custom_exc(self, exc_tuple, handler):
1537 def set_custom_exc(self, exc_tuple, handler):
1538 """set_custom_exc(exc_tuple,handler)
1538 """set_custom_exc(exc_tuple,handler)
1539
1539
1540 Set a custom exception handler, which will be called if any of the
1540 Set a custom exception handler, which will be called if any of the
1541 exceptions in exc_tuple occur in the mainloop (specifically, in the
1541 exceptions in exc_tuple occur in the mainloop (specifically, in the
1542 run_code() method).
1542 run_code() method).
1543
1543
1544 Parameters
1544 Parameters
1545 ----------
1545 ----------
1546
1546
1547 exc_tuple : tuple of exception classes
1547 exc_tuple : tuple of exception classes
1548 A *tuple* of exception classes, for which to call the defined
1548 A *tuple* of exception classes, for which to call the defined
1549 handler. It is very important that you use a tuple, and NOT A
1549 handler. It is very important that you use a tuple, and NOT A
1550 LIST here, because of the way Python's except statement works. If
1550 LIST here, because of the way Python's except statement works. If
1551 you only want to trap a single exception, use a singleton tuple::
1551 you only want to trap a single exception, use a singleton tuple::
1552
1552
1553 exc_tuple == (MyCustomException,)
1553 exc_tuple == (MyCustomException,)
1554
1554
1555 handler : callable
1555 handler : callable
1556 handler must have the following signature::
1556 handler must have the following signature::
1557
1557
1558 def my_handler(self, etype, value, tb, tb_offset=None):
1558 def my_handler(self, etype, value, tb, tb_offset=None):
1559 ...
1559 ...
1560 return structured_traceback
1560 return structured_traceback
1561
1561
1562 Your handler must return a structured traceback (a list of strings),
1562 Your handler must return a structured traceback (a list of strings),
1563 or None.
1563 or None.
1564
1564
1565 This will be made into an instance method (via types.MethodType)
1565 This will be made into an instance method (via types.MethodType)
1566 of IPython itself, and it will be called if any of the exceptions
1566 of IPython itself, and it will be called if any of the exceptions
1567 listed in the exc_tuple are caught. If the handler is None, an
1567 listed in the exc_tuple are caught. If the handler is None, an
1568 internal basic one is used, which just prints basic info.
1568 internal basic one is used, which just prints basic info.
1569
1569
1570 To protect IPython from crashes, if your handler ever raises an
1570 To protect IPython from crashes, if your handler ever raises an
1571 exception or returns an invalid result, it will be immediately
1571 exception or returns an invalid result, it will be immediately
1572 disabled.
1572 disabled.
1573
1573
1574 WARNING: by putting in your own exception handler into IPython's main
1574 WARNING: by putting in your own exception handler into IPython's main
1575 execution loop, you run a very good chance of nasty crashes. This
1575 execution loop, you run a very good chance of nasty crashes. This
1576 facility should only be used if you really know what you are doing."""
1576 facility should only be used if you really know what you are doing."""
1577
1577
1578 assert type(exc_tuple)==type(()) , \
1578 assert type(exc_tuple)==type(()) , \
1579 "The custom exceptions must be given AS A TUPLE."
1579 "The custom exceptions must be given AS A TUPLE."
1580
1580
1581 def dummy_handler(self,etype,value,tb,tb_offset=None):
1581 def dummy_handler(self,etype,value,tb,tb_offset=None):
1582 print('*** Simple custom exception handler ***')
1582 print('*** Simple custom exception handler ***')
1583 print('Exception type :',etype)
1583 print('Exception type :',etype)
1584 print('Exception value:',value)
1584 print('Exception value:',value)
1585 print('Traceback :',tb)
1585 print('Traceback :',tb)
1586 #print 'Source code :','\n'.join(self.buffer)
1586 #print 'Source code :','\n'.join(self.buffer)
1587
1587
1588 def validate_stb(stb):
1588 def validate_stb(stb):
1589 """validate structured traceback return type
1589 """validate structured traceback return type
1590
1590
1591 return type of CustomTB *should* be a list of strings, but allow
1591 return type of CustomTB *should* be a list of strings, but allow
1592 single strings or None, which are harmless.
1592 single strings or None, which are harmless.
1593
1593
1594 This function will *always* return a list of strings,
1594 This function will *always* return a list of strings,
1595 and will raise a TypeError if stb is inappropriate.
1595 and will raise a TypeError if stb is inappropriate.
1596 """
1596 """
1597 msg = "CustomTB must return list of strings, not %r" % stb
1597 msg = "CustomTB must return list of strings, not %r" % stb
1598 if stb is None:
1598 if stb is None:
1599 return []
1599 return []
1600 elif isinstance(stb, basestring):
1600 elif isinstance(stb, basestring):
1601 return [stb]
1601 return [stb]
1602 elif not isinstance(stb, list):
1602 elif not isinstance(stb, list):
1603 raise TypeError(msg)
1603 raise TypeError(msg)
1604 # it's a list
1604 # it's a list
1605 for line in stb:
1605 for line in stb:
1606 # check every element
1606 # check every element
1607 if not isinstance(line, basestring):
1607 if not isinstance(line, basestring):
1608 raise TypeError(msg)
1608 raise TypeError(msg)
1609 return stb
1609 return stb
1610
1610
1611 if handler is None:
1611 if handler is None:
1612 wrapped = dummy_handler
1612 wrapped = dummy_handler
1613 else:
1613 else:
1614 def wrapped(self,etype,value,tb,tb_offset=None):
1614 def wrapped(self,etype,value,tb,tb_offset=None):
1615 """wrap CustomTB handler, to protect IPython from user code
1615 """wrap CustomTB handler, to protect IPython from user code
1616
1616
1617 This makes it harder (but not impossible) for custom exception
1617 This makes it harder (but not impossible) for custom exception
1618 handlers to crash IPython.
1618 handlers to crash IPython.
1619 """
1619 """
1620 try:
1620 try:
1621 stb = handler(self,etype,value,tb,tb_offset=tb_offset)
1621 stb = handler(self,etype,value,tb,tb_offset=tb_offset)
1622 return validate_stb(stb)
1622 return validate_stb(stb)
1623 except:
1623 except:
1624 # clear custom handler immediately
1624 # clear custom handler immediately
1625 self.set_custom_exc((), None)
1625 self.set_custom_exc((), None)
1626 print("Custom TB Handler failed, unregistering", file=io.stderr)
1626 print("Custom TB Handler failed, unregistering", file=io.stderr)
1627 # show the exception in handler first
1627 # show the exception in handler first
1628 stb = self.InteractiveTB.structured_traceback(*sys.exc_info())
1628 stb = self.InteractiveTB.structured_traceback(*sys.exc_info())
1629 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1629 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1630 print("The original exception:", file=io.stdout)
1630 print("The original exception:", file=io.stdout)
1631 stb = self.InteractiveTB.structured_traceback(
1631 stb = self.InteractiveTB.structured_traceback(
1632 (etype,value,tb), tb_offset=tb_offset
1632 (etype,value,tb), tb_offset=tb_offset
1633 )
1633 )
1634 return stb
1634 return stb
1635
1635
1636 self.CustomTB = types.MethodType(wrapped,self)
1636 self.CustomTB = types.MethodType(wrapped,self)
1637 self.custom_exceptions = exc_tuple
1637 self.custom_exceptions = exc_tuple
1638
1638
1639 def excepthook(self, etype, value, tb):
1639 def excepthook(self, etype, value, tb):
1640 """One more defense for GUI apps that call sys.excepthook.
1640 """One more defense for GUI apps that call sys.excepthook.
1641
1641
1642 GUI frameworks like wxPython trap exceptions and call
1642 GUI frameworks like wxPython trap exceptions and call
1643 sys.excepthook themselves. I guess this is a feature that
1643 sys.excepthook themselves. I guess this is a feature that
1644 enables them to keep running after exceptions that would
1644 enables them to keep running after exceptions that would
1645 otherwise kill their mainloop. This is a bother for IPython
1645 otherwise kill their mainloop. This is a bother for IPython
1646 which excepts to catch all of the program exceptions with a try:
1646 which excepts to catch all of the program exceptions with a try:
1647 except: statement.
1647 except: statement.
1648
1648
1649 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
1649 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
1650 any app directly invokes sys.excepthook, it will look to the user like
1650 any app directly invokes sys.excepthook, it will look to the user like
1651 IPython crashed. In order to work around this, we can disable the
1651 IPython crashed. In order to work around this, we can disable the
1652 CrashHandler and replace it with this excepthook instead, which prints a
1652 CrashHandler and replace it with this excepthook instead, which prints a
1653 regular traceback using our InteractiveTB. In this fashion, apps which
1653 regular traceback using our InteractiveTB. In this fashion, apps which
1654 call sys.excepthook will generate a regular-looking exception from
1654 call sys.excepthook will generate a regular-looking exception from
1655 IPython, and the CrashHandler will only be triggered by real IPython
1655 IPython, and the CrashHandler will only be triggered by real IPython
1656 crashes.
1656 crashes.
1657
1657
1658 This hook should be used sparingly, only in places which are not likely
1658 This hook should be used sparingly, only in places which are not likely
1659 to be true IPython errors.
1659 to be true IPython errors.
1660 """
1660 """
1661 self.showtraceback((etype,value,tb),tb_offset=0)
1661 self.showtraceback((etype,value,tb),tb_offset=0)
1662
1662
1663 def _get_exc_info(self, exc_tuple=None):
1663 def _get_exc_info(self, exc_tuple=None):
1664 """get exc_info from a given tuple, sys.exc_info() or sys.last_type etc.
1664 """get exc_info from a given tuple, sys.exc_info() or sys.last_type etc.
1665
1665
1666 Ensures sys.last_type,value,traceback hold the exc_info we found,
1666 Ensures sys.last_type,value,traceback hold the exc_info we found,
1667 from whichever source.
1667 from whichever source.
1668
1668
1669 raises ValueError if none of these contain any information
1669 raises ValueError if none of these contain any information
1670 """
1670 """
1671 if exc_tuple is None:
1671 if exc_tuple is None:
1672 etype, value, tb = sys.exc_info()
1672 etype, value, tb = sys.exc_info()
1673 else:
1673 else:
1674 etype, value, tb = exc_tuple
1674 etype, value, tb = exc_tuple
1675
1675
1676 if etype is None:
1676 if etype is None:
1677 if hasattr(sys, 'last_type'):
1677 if hasattr(sys, 'last_type'):
1678 etype, value, tb = sys.last_type, sys.last_value, \
1678 etype, value, tb = sys.last_type, sys.last_value, \
1679 sys.last_traceback
1679 sys.last_traceback
1680
1680
1681 if etype is None:
1681 if etype is None:
1682 raise ValueError("No exception to find")
1682 raise ValueError("No exception to find")
1683
1683
1684 # Now store the exception info in sys.last_type etc.
1684 # Now store the exception info in sys.last_type etc.
1685 # WARNING: these variables are somewhat deprecated and not
1685 # WARNING: these variables are somewhat deprecated and not
1686 # necessarily safe to use in a threaded environment, but tools
1686 # necessarily safe to use in a threaded environment, but tools
1687 # like pdb depend on their existence, so let's set them. If we
1687 # like pdb depend on their existence, so let's set them. If we
1688 # find problems in the field, we'll need to revisit their use.
1688 # find problems in the field, we'll need to revisit their use.
1689 sys.last_type = etype
1689 sys.last_type = etype
1690 sys.last_value = value
1690 sys.last_value = value
1691 sys.last_traceback = tb
1691 sys.last_traceback = tb
1692
1692
1693 return etype, value, tb
1693 return etype, value, tb
1694
1694
1695
1695
1696 def showtraceback(self,exc_tuple = None,filename=None,tb_offset=None,
1696 def showtraceback(self,exc_tuple = None,filename=None,tb_offset=None,
1697 exception_only=False):
1697 exception_only=False):
1698 """Display the exception that just occurred.
1698 """Display the exception that just occurred.
1699
1699
1700 If nothing is known about the exception, this is the method which
1700 If nothing is known about the exception, this is the method which
1701 should be used throughout the code for presenting user tracebacks,
1701 should be used throughout the code for presenting user tracebacks,
1702 rather than directly invoking the InteractiveTB object.
1702 rather than directly invoking the InteractiveTB object.
1703
1703
1704 A specific showsyntaxerror() also exists, but this method can take
1704 A specific showsyntaxerror() also exists, but this method can take
1705 care of calling it if needed, so unless you are explicitly catching a
1705 care of calling it if needed, so unless you are explicitly catching a
1706 SyntaxError exception, don't try to analyze the stack manually and
1706 SyntaxError exception, don't try to analyze the stack manually and
1707 simply call this method."""
1707 simply call this method."""
1708
1708
1709 try:
1709 try:
1710 try:
1710 try:
1711 etype, value, tb = self._get_exc_info(exc_tuple)
1711 etype, value, tb = self._get_exc_info(exc_tuple)
1712 except ValueError:
1712 except ValueError:
1713 self.write_err('No traceback available to show.\n')
1713 self.write_err('No traceback available to show.\n')
1714 return
1714 return
1715
1715
1716 if issubclass(etype, SyntaxError):
1716 if issubclass(etype, SyntaxError):
1717 # Though this won't be called by syntax errors in the input
1717 # Though this won't be called by syntax errors in the input
1718 # line, there may be SyntaxError cases with imported code.
1718 # line, there may be SyntaxError cases with imported code.
1719 self.showsyntaxerror(filename)
1719 self.showsyntaxerror(filename)
1720 elif etype is UsageError:
1720 elif etype is UsageError:
1721 self.write_err("UsageError: %s" % value)
1721 self.write_err("UsageError: %s" % value)
1722 else:
1722 else:
1723 if exception_only:
1723 if exception_only:
1724 stb = ['An exception has occurred, use %tb to see '
1724 stb = ['An exception has occurred, use %tb to see '
1725 'the full traceback.\n']
1725 'the full traceback.\n']
1726 stb.extend(self.InteractiveTB.get_exception_only(etype,
1726 stb.extend(self.InteractiveTB.get_exception_only(etype,
1727 value))
1727 value))
1728 else:
1728 else:
1729 try:
1729 try:
1730 # Exception classes can customise their traceback - we
1730 # Exception classes can customise their traceback - we
1731 # use this in IPython.parallel for exceptions occurring
1731 # use this in IPython.parallel for exceptions occurring
1732 # in the engines. This should return a list of strings.
1732 # in the engines. This should return a list of strings.
1733 stb = value._render_traceback_()
1733 stb = value._render_traceback_()
1734 except Exception:
1734 except Exception:
1735 stb = self.InteractiveTB.structured_traceback(etype,
1735 stb = self.InteractiveTB.structured_traceback(etype,
1736 value, tb, tb_offset=tb_offset)
1736 value, tb, tb_offset=tb_offset)
1737
1737
1738 self._showtraceback(etype, value, stb)
1738 self._showtraceback(etype, value, stb)
1739 if self.call_pdb:
1739 if self.call_pdb:
1740 # drop into debugger
1740 # drop into debugger
1741 self.debugger(force=True)
1741 self.debugger(force=True)
1742 return
1742 return
1743
1743
1744 # Actually show the traceback
1744 # Actually show the traceback
1745 self._showtraceback(etype, value, stb)
1745 self._showtraceback(etype, value, stb)
1746
1746
1747 except KeyboardInterrupt:
1747 except KeyboardInterrupt:
1748 self.write_err("\nKeyboardInterrupt\n")
1748 self.write_err("\nKeyboardInterrupt\n")
1749
1749
1750 def _showtraceback(self, etype, evalue, stb):
1750 def _showtraceback(self, etype, evalue, stb):
1751 """Actually show a traceback.
1751 """Actually show a traceback.
1752
1752
1753 Subclasses may override this method to put the traceback on a different
1753 Subclasses may override this method to put the traceback on a different
1754 place, like a side channel.
1754 place, like a side channel.
1755 """
1755 """
1756 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1756 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1757
1757
1758 def showsyntaxerror(self, filename=None):
1758 def showsyntaxerror(self, filename=None):
1759 """Display the syntax error that just occurred.
1759 """Display the syntax error that just occurred.
1760
1760
1761 This doesn't display a stack trace because there isn't one.
1761 This doesn't display a stack trace because there isn't one.
1762
1762
1763 If a filename is given, it is stuffed in the exception instead
1763 If a filename is given, it is stuffed in the exception instead
1764 of what was there before (because Python's parser always uses
1764 of what was there before (because Python's parser always uses
1765 "<string>" when reading from a string).
1765 "<string>" when reading from a string).
1766 """
1766 """
1767 etype, value, last_traceback = self._get_exc_info()
1767 etype, value, last_traceback = self._get_exc_info()
1768
1768
1769 if filename and issubclass(etype, SyntaxError):
1769 if filename and issubclass(etype, SyntaxError):
1770 try:
1770 try:
1771 value.filename = filename
1771 value.filename = filename
1772 except:
1772 except:
1773 # Not the format we expect; leave it alone
1773 # Not the format we expect; leave it alone
1774 pass
1774 pass
1775
1775
1776 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1776 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1777 self._showtraceback(etype, value, stb)
1777 self._showtraceback(etype, value, stb)
1778
1778
1779 # This is overridden in TerminalInteractiveShell to show a message about
1779 # This is overridden in TerminalInteractiveShell to show a message about
1780 # the %paste magic.
1780 # the %paste magic.
1781 def showindentationerror(self):
1781 def showindentationerror(self):
1782 """Called by run_cell when there's an IndentationError in code entered
1782 """Called by run_cell when there's an IndentationError in code entered
1783 at the prompt.
1783 at the prompt.
1784
1784
1785 This is overridden in TerminalInteractiveShell to show a message about
1785 This is overridden in TerminalInteractiveShell to show a message about
1786 the %paste magic."""
1786 the %paste magic."""
1787 self.showsyntaxerror()
1787 self.showsyntaxerror()
1788
1788
1789 #-------------------------------------------------------------------------
1789 #-------------------------------------------------------------------------
1790 # Things related to readline
1790 # Things related to readline
1791 #-------------------------------------------------------------------------
1791 #-------------------------------------------------------------------------
1792
1792
1793 def init_readline(self):
1793 def init_readline(self):
1794 """Command history completion/saving/reloading."""
1794 """Command history completion/saving/reloading."""
1795
1795
1796 if self.readline_use:
1796 if self.readline_use:
1797 import IPython.utils.rlineimpl as readline
1797 import IPython.utils.rlineimpl as readline
1798
1798
1799 self.rl_next_input = None
1799 self.rl_next_input = None
1800 self.rl_do_indent = False
1800 self.rl_do_indent = False
1801
1801
1802 if not self.readline_use or not readline.have_readline:
1802 if not self.readline_use or not readline.have_readline:
1803 self.has_readline = False
1803 self.has_readline = False
1804 self.readline = None
1804 self.readline = None
1805 # Set a number of methods that depend on readline to be no-op
1805 # Set a number of methods that depend on readline to be no-op
1806 self.readline_no_record = no_op_context
1806 self.readline_no_record = no_op_context
1807 self.set_readline_completer = no_op
1807 self.set_readline_completer = no_op
1808 self.set_custom_completer = no_op
1808 self.set_custom_completer = no_op
1809 if self.readline_use:
1809 if self.readline_use:
1810 warn('Readline services not available or not loaded.')
1810 warn('Readline services not available or not loaded.')
1811 else:
1811 else:
1812 self.has_readline = True
1812 self.has_readline = True
1813 self.readline = readline
1813 self.readline = readline
1814 sys.modules['readline'] = readline
1814 sys.modules['readline'] = readline
1815
1815
1816 # Platform-specific configuration
1816 # Platform-specific configuration
1817 if os.name == 'nt':
1817 if os.name == 'nt':
1818 # FIXME - check with Frederick to see if we can harmonize
1818 # FIXME - check with Frederick to see if we can harmonize
1819 # naming conventions with pyreadline to avoid this
1819 # naming conventions with pyreadline to avoid this
1820 # platform-dependent check
1820 # platform-dependent check
1821 self.readline_startup_hook = readline.set_pre_input_hook
1821 self.readline_startup_hook = readline.set_pre_input_hook
1822 else:
1822 else:
1823 self.readline_startup_hook = readline.set_startup_hook
1823 self.readline_startup_hook = readline.set_startup_hook
1824
1824
1825 # Load user's initrc file (readline config)
1825 # Load user's initrc file (readline config)
1826 # Or if libedit is used, load editrc.
1826 # Or if libedit is used, load editrc.
1827 inputrc_name = os.environ.get('INPUTRC')
1827 inputrc_name = os.environ.get('INPUTRC')
1828 if inputrc_name is None:
1828 if inputrc_name is None:
1829 inputrc_name = '.inputrc'
1829 inputrc_name = '.inputrc'
1830 if readline.uses_libedit:
1830 if readline.uses_libedit:
1831 inputrc_name = '.editrc'
1831 inputrc_name = '.editrc'
1832 inputrc_name = os.path.join(self.home_dir, inputrc_name)
1832 inputrc_name = os.path.join(self.home_dir, inputrc_name)
1833 if os.path.isfile(inputrc_name):
1833 if os.path.isfile(inputrc_name):
1834 try:
1834 try:
1835 readline.read_init_file(inputrc_name)
1835 readline.read_init_file(inputrc_name)
1836 except:
1836 except:
1837 warn('Problems reading readline initialization file <%s>'
1837 warn('Problems reading readline initialization file <%s>'
1838 % inputrc_name)
1838 % inputrc_name)
1839
1839
1840 # Configure readline according to user's prefs
1840 # Configure readline according to user's prefs
1841 # This is only done if GNU readline is being used. If libedit
1841 # This is only done if GNU readline is being used. If libedit
1842 # is being used (as on Leopard) the readline config is
1842 # is being used (as on Leopard) the readline config is
1843 # not run as the syntax for libedit is different.
1843 # not run as the syntax for libedit is different.
1844 if not readline.uses_libedit:
1844 if not readline.uses_libedit:
1845 for rlcommand in self.readline_parse_and_bind:
1845 for rlcommand in self.readline_parse_and_bind:
1846 #print "loading rl:",rlcommand # dbg
1846 #print "loading rl:",rlcommand # dbg
1847 readline.parse_and_bind(rlcommand)
1847 readline.parse_and_bind(rlcommand)
1848
1848
1849 # Remove some chars from the delimiters list. If we encounter
1849 # Remove some chars from the delimiters list. If we encounter
1850 # unicode chars, discard them.
1850 # unicode chars, discard them.
1851 delims = readline.get_completer_delims()
1851 delims = readline.get_completer_delims()
1852 if not py3compat.PY3:
1852 if not py3compat.PY3:
1853 delims = delims.encode("ascii", "ignore")
1853 delims = delims.encode("ascii", "ignore")
1854 for d in self.readline_remove_delims:
1854 for d in self.readline_remove_delims:
1855 delims = delims.replace(d, "")
1855 delims = delims.replace(d, "")
1856 delims = delims.replace(ESC_MAGIC, '')
1856 delims = delims.replace(ESC_MAGIC, '')
1857 readline.set_completer_delims(delims)
1857 readline.set_completer_delims(delims)
1858 # otherwise we end up with a monster history after a while:
1858 # otherwise we end up with a monster history after a while:
1859 readline.set_history_length(self.history_length)
1859 readline.set_history_length(self.history_length)
1860
1860
1861 self.refill_readline_hist()
1861 self.refill_readline_hist()
1862 self.readline_no_record = ReadlineNoRecord(self)
1862 self.readline_no_record = ReadlineNoRecord(self)
1863
1863
1864 # Configure auto-indent for all platforms
1864 # Configure auto-indent for all platforms
1865 self.set_autoindent(self.autoindent)
1865 self.set_autoindent(self.autoindent)
1866
1866
1867 def refill_readline_hist(self):
1867 def refill_readline_hist(self):
1868 # Load the last 1000 lines from history
1868 # Load the last 1000 lines from history
1869 self.readline.clear_history()
1869 self.readline.clear_history()
1870 stdin_encoding = sys.stdin.encoding or "utf-8"
1870 stdin_encoding = sys.stdin.encoding or "utf-8"
1871 last_cell = u""
1871 last_cell = u""
1872 for _, _, cell in self.history_manager.get_tail(1000,
1872 for _, _, cell in self.history_manager.get_tail(1000,
1873 include_latest=True):
1873 include_latest=True):
1874 # Ignore blank lines and consecutive duplicates
1874 # Ignore blank lines and consecutive duplicates
1875 cell = cell.rstrip()
1875 cell = cell.rstrip()
1876 if cell and (cell != last_cell):
1876 if cell and (cell != last_cell):
1877 if self.multiline_history:
1877 if self.multiline_history:
1878 self.readline.add_history(py3compat.unicode_to_str(cell,
1878 self.readline.add_history(py3compat.unicode_to_str(cell,
1879 stdin_encoding))
1879 stdin_encoding))
1880 else:
1880 else:
1881 for line in cell.splitlines():
1881 for line in cell.splitlines():
1882 self.readline.add_history(py3compat.unicode_to_str(line,
1882 self.readline.add_history(py3compat.unicode_to_str(line,
1883 stdin_encoding))
1883 stdin_encoding))
1884 last_cell = cell
1884 last_cell = cell
1885
1885
1886 def set_next_input(self, s):
1886 def set_next_input(self, s):
1887 """ Sets the 'default' input string for the next command line.
1887 """ Sets the 'default' input string for the next command line.
1888
1888
1889 Requires readline.
1889 Requires readline.
1890
1890
1891 Example:
1891 Example:
1892
1892
1893 [D:\ipython]|1> _ip.set_next_input("Hello Word")
1893 [D:\ipython]|1> _ip.set_next_input("Hello Word")
1894 [D:\ipython]|2> Hello Word_ # cursor is here
1894 [D:\ipython]|2> Hello Word_ # cursor is here
1895 """
1895 """
1896 self.rl_next_input = py3compat.cast_bytes_py2(s)
1896 self.rl_next_input = py3compat.cast_bytes_py2(s)
1897
1897
1898 # Maybe move this to the terminal subclass?
1898 # Maybe move this to the terminal subclass?
1899 def pre_readline(self):
1899 def pre_readline(self):
1900 """readline hook to be used at the start of each line.
1900 """readline hook to be used at the start of each line.
1901
1901
1902 Currently it handles auto-indent only."""
1902 Currently it handles auto-indent only."""
1903
1903
1904 if self.rl_do_indent:
1904 if self.rl_do_indent:
1905 self.readline.insert_text(self._indent_current_str())
1905 self.readline.insert_text(self._indent_current_str())
1906 if self.rl_next_input is not None:
1906 if self.rl_next_input is not None:
1907 self.readline.insert_text(self.rl_next_input)
1907 self.readline.insert_text(self.rl_next_input)
1908 self.rl_next_input = None
1908 self.rl_next_input = None
1909
1909
1910 def _indent_current_str(self):
1910 def _indent_current_str(self):
1911 """return the current level of indentation as a string"""
1911 """return the current level of indentation as a string"""
1912 return self.input_splitter.indent_spaces * ' '
1912 return self.input_splitter.indent_spaces * ' '
1913
1913
1914 #-------------------------------------------------------------------------
1914 #-------------------------------------------------------------------------
1915 # Things related to text completion
1915 # Things related to text completion
1916 #-------------------------------------------------------------------------
1916 #-------------------------------------------------------------------------
1917
1917
1918 def init_completer(self):
1918 def init_completer(self):
1919 """Initialize the completion machinery.
1919 """Initialize the completion machinery.
1920
1920
1921 This creates completion machinery that can be used by client code,
1921 This creates completion machinery that can be used by client code,
1922 either interactively in-process (typically triggered by the readline
1922 either interactively in-process (typically triggered by the readline
1923 library), programatically (such as in test suites) or out-of-prcess
1923 library), programatically (such as in test suites) or out-of-prcess
1924 (typically over the network by remote frontends).
1924 (typically over the network by remote frontends).
1925 """
1925 """
1926 from IPython.core.completer import IPCompleter
1926 from IPython.core.completer import IPCompleter
1927 from IPython.core.completerlib import (module_completer,
1927 from IPython.core.completerlib import (module_completer,
1928 magic_run_completer, cd_completer, reset_completer)
1928 magic_run_completer, cd_completer, reset_completer)
1929
1929
1930 self.Completer = IPCompleter(shell=self,
1930 self.Completer = IPCompleter(shell=self,
1931 namespace=self.user_ns,
1931 namespace=self.user_ns,
1932 global_namespace=self.user_global_ns,
1932 global_namespace=self.user_global_ns,
1933 alias_table=self.alias_manager.alias_table,
1933 alias_table=self.alias_manager.alias_table,
1934 use_readline=self.has_readline,
1934 use_readline=self.has_readline,
1935 config=self.config,
1935 config=self.config,
1936 )
1936 )
1937 self.configurables.append(self.Completer)
1937 self.configurables.append(self.Completer)
1938
1938
1939 # Add custom completers to the basic ones built into IPCompleter
1939 # Add custom completers to the basic ones built into IPCompleter
1940 sdisp = self.strdispatchers.get('complete_command', StrDispatch())
1940 sdisp = self.strdispatchers.get('complete_command', StrDispatch())
1941 self.strdispatchers['complete_command'] = sdisp
1941 self.strdispatchers['complete_command'] = sdisp
1942 self.Completer.custom_completers = sdisp
1942 self.Completer.custom_completers = sdisp
1943
1943
1944 self.set_hook('complete_command', module_completer, str_key = 'import')
1944 self.set_hook('complete_command', module_completer, str_key = 'import')
1945 self.set_hook('complete_command', module_completer, str_key = 'from')
1945 self.set_hook('complete_command', module_completer, str_key = 'from')
1946 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
1946 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
1947 self.set_hook('complete_command', cd_completer, str_key = '%cd')
1947 self.set_hook('complete_command', cd_completer, str_key = '%cd')
1948 self.set_hook('complete_command', reset_completer, str_key = '%reset')
1948 self.set_hook('complete_command', reset_completer, str_key = '%reset')
1949
1949
1950 # Only configure readline if we truly are using readline. IPython can
1950 # Only configure readline if we truly are using readline. IPython can
1951 # do tab-completion over the network, in GUIs, etc, where readline
1951 # do tab-completion over the network, in GUIs, etc, where readline
1952 # itself may be absent
1952 # itself may be absent
1953 if self.has_readline:
1953 if self.has_readline:
1954 self.set_readline_completer()
1954 self.set_readline_completer()
1955
1955
1956 def complete(self, text, line=None, cursor_pos=None):
1956 def complete(self, text, line=None, cursor_pos=None):
1957 """Return the completed text and a list of completions.
1957 """Return the completed text and a list of completions.
1958
1958
1959 Parameters
1959 Parameters
1960 ----------
1960 ----------
1961
1961
1962 text : string
1962 text : string
1963 A string of text to be completed on. It can be given as empty and
1963 A string of text to be completed on. It can be given as empty and
1964 instead a line/position pair are given. In this case, the
1964 instead a line/position pair are given. In this case, the
1965 completer itself will split the line like readline does.
1965 completer itself will split the line like readline does.
1966
1966
1967 line : string, optional
1967 line : string, optional
1968 The complete line that text is part of.
1968 The complete line that text is part of.
1969
1969
1970 cursor_pos : int, optional
1970 cursor_pos : int, optional
1971 The position of the cursor on the input line.
1971 The position of the cursor on the input line.
1972
1972
1973 Returns
1973 Returns
1974 -------
1974 -------
1975 text : string
1975 text : string
1976 The actual text that was completed.
1976 The actual text that was completed.
1977
1977
1978 matches : list
1978 matches : list
1979 A sorted list with all possible completions.
1979 A sorted list with all possible completions.
1980
1980
1981 The optional arguments allow the completion to take more context into
1981 The optional arguments allow the completion to take more context into
1982 account, and are part of the low-level completion API.
1982 account, and are part of the low-level completion API.
1983
1983
1984 This is a wrapper around the completion mechanism, similar to what
1984 This is a wrapper around the completion mechanism, similar to what
1985 readline does at the command line when the TAB key is hit. By
1985 readline does at the command line when the TAB key is hit. By
1986 exposing it as a method, it can be used by other non-readline
1986 exposing it as a method, it can be used by other non-readline
1987 environments (such as GUIs) for text completion.
1987 environments (such as GUIs) for text completion.
1988
1988
1989 Simple usage example:
1989 Simple usage example:
1990
1990
1991 In [1]: x = 'hello'
1991 In [1]: x = 'hello'
1992
1992
1993 In [2]: _ip.complete('x.l')
1993 In [2]: _ip.complete('x.l')
1994 Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip'])
1994 Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip'])
1995 """
1995 """
1996
1996
1997 # Inject names into __builtin__ so we can complete on the added names.
1997 # Inject names into __builtin__ so we can complete on the added names.
1998 with self.builtin_trap:
1998 with self.builtin_trap:
1999 return self.Completer.complete(text, line, cursor_pos)
1999 return self.Completer.complete(text, line, cursor_pos)
2000
2000
2001 def set_custom_completer(self, completer, pos=0):
2001 def set_custom_completer(self, completer, pos=0):
2002 """Adds a new custom completer function.
2002 """Adds a new custom completer function.
2003
2003
2004 The position argument (defaults to 0) is the index in the completers
2004 The position argument (defaults to 0) is the index in the completers
2005 list where you want the completer to be inserted."""
2005 list where you want the completer to be inserted."""
2006
2006
2007 newcomp = types.MethodType(completer,self.Completer)
2007 newcomp = types.MethodType(completer,self.Completer)
2008 self.Completer.matchers.insert(pos,newcomp)
2008 self.Completer.matchers.insert(pos,newcomp)
2009
2009
2010 def set_readline_completer(self):
2010 def set_readline_completer(self):
2011 """Reset readline's completer to be our own."""
2011 """Reset readline's completer to be our own."""
2012 self.readline.set_completer(self.Completer.rlcomplete)
2012 self.readline.set_completer(self.Completer.rlcomplete)
2013
2013
2014 def set_completer_frame(self, frame=None):
2014 def set_completer_frame(self, frame=None):
2015 """Set the frame of the completer."""
2015 """Set the frame of the completer."""
2016 if frame:
2016 if frame:
2017 self.Completer.namespace = frame.f_locals
2017 self.Completer.namespace = frame.f_locals
2018 self.Completer.global_namespace = frame.f_globals
2018 self.Completer.global_namespace = frame.f_globals
2019 else:
2019 else:
2020 self.Completer.namespace = self.user_ns
2020 self.Completer.namespace = self.user_ns
2021 self.Completer.global_namespace = self.user_global_ns
2021 self.Completer.global_namespace = self.user_global_ns
2022
2022
2023 #-------------------------------------------------------------------------
2023 #-------------------------------------------------------------------------
2024 # Things related to magics
2024 # Things related to magics
2025 #-------------------------------------------------------------------------
2025 #-------------------------------------------------------------------------
2026
2026
2027 def init_magics(self):
2027 def init_magics(self):
2028 from IPython.core import magics as m
2028 from IPython.core import magics as m
2029 self.magics_manager = magic.MagicsManager(shell=self,
2029 self.magics_manager = magic.MagicsManager(shell=self,
2030 confg=self.config,
2030 confg=self.config,
2031 user_magics=m.UserMagics(self))
2031 user_magics=m.UserMagics(self))
2032 self.configurables.append(self.magics_manager)
2032 self.configurables.append(self.magics_manager)
2033
2033
2034 # Expose as public API from the magics manager
2034 # Expose as public API from the magics manager
2035 self.register_magics = self.magics_manager.register
2035 self.register_magics = self.magics_manager.register
2036 self.register_magic_function = self.magics_manager.register_function
2036 self.register_magic_function = self.magics_manager.register_function
2037 self.define_magic = self.magics_manager.define_magic
2037 self.define_magic = self.magics_manager.define_magic
2038
2038
2039 self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics,
2039 self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics,
2040 m.ConfigMagics, m.DeprecatedMagics, m.DisplayMagics, m.ExecutionMagics,
2040 m.ConfigMagics, m.DeprecatedMagics, m.DisplayMagics, m.ExecutionMagics,
2041 m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics,
2041 m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics,
2042 m.NamespaceMagics, m.OSMagics, m.PylabMagics, m.ScriptMagics,
2042 m.NamespaceMagics, m.OSMagics, m.PylabMagics, m.ScriptMagics,
2043 )
2043 )
2044
2044
2045 # Register Magic Aliases
2045 # Register Magic Aliases
2046 mman = self.magics_manager
2046 mman = self.magics_manager
2047 mman.register_alias('ed', 'edit')
2047 mman.register_alias('ed', 'edit')
2048 mman.register_alias('hist', 'history')
2048 mman.register_alias('hist', 'history')
2049 mman.register_alias('rep', 'recall')
2049 mman.register_alias('rep', 'recall')
2050
2050
2051 # FIXME: Move the color initialization to the DisplayHook, which
2051 # FIXME: Move the color initialization to the DisplayHook, which
2052 # should be split into a prompt manager and displayhook. We probably
2052 # should be split into a prompt manager and displayhook. We probably
2053 # even need a centralize colors management object.
2053 # even need a centralize colors management object.
2054 self.magic('colors %s' % self.colors)
2054 self.magic('colors %s' % self.colors)
2055
2055
2056 def run_line_magic(self, magic_name, line):
2056 def run_line_magic(self, magic_name, line):
2057 """Execute the given line magic.
2057 """Execute the given line magic.
2058
2058
2059 Parameters
2059 Parameters
2060 ----------
2060 ----------
2061 magic_name : str
2061 magic_name : str
2062 Name of the desired magic function, without '%' prefix.
2062 Name of the desired magic function, without '%' prefix.
2063
2063
2064 line : str
2064 line : str
2065 The rest of the input line as a single string.
2065 The rest of the input line as a single string.
2066 """
2066 """
2067 fn = self.find_line_magic(magic_name)
2067 fn = self.find_line_magic(magic_name)
2068 if fn is None:
2068 if fn is None:
2069 cm = self.find_cell_magic(magic_name)
2069 cm = self.find_cell_magic(magic_name)
2070 etpl = "Line magic function `%%%s` not found%s."
2070 etpl = "Line magic function `%%%s` not found%s."
2071 extra = '' if cm is None else (' (But cell magic `%%%%%s` exists, '
2071 extra = '' if cm is None else (' (But cell magic `%%%%%s` exists, '
2072 'did you mean that instead?)' % magic_name )
2072 'did you mean that instead?)' % magic_name )
2073 error(etpl % (magic_name, extra))
2073 error(etpl % (magic_name, extra))
2074 else:
2074 else:
2075 # Note: this is the distance in the stack to the user's frame.
2075 # Note: this is the distance in the stack to the user's frame.
2076 # This will need to be updated if the internal calling logic gets
2076 # This will need to be updated if the internal calling logic gets
2077 # refactored, or else we'll be expanding the wrong variables.
2077 # refactored, or else we'll be expanding the wrong variables.
2078 stack_depth = 2
2078 stack_depth = 2
2079 magic_arg_s = self.var_expand(line, stack_depth)
2079 magic_arg_s = self.var_expand(line, stack_depth)
2080 # Put magic args in a list so we can call with f(*a) syntax
2080 # Put magic args in a list so we can call with f(*a) syntax
2081 args = [magic_arg_s]
2081 args = [magic_arg_s]
2082 kwargs = {}
2082 kwargs = {}
2083 # Grab local namespace if we need it:
2083 # Grab local namespace if we need it:
2084 if getattr(fn, "needs_local_scope", False):
2084 if getattr(fn, "needs_local_scope", False):
2085 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
2085 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
2086 with self.builtin_trap:
2086 with self.builtin_trap:
2087 result = fn(*args,**kwargs)
2087 result = fn(*args,**kwargs)
2088 return result
2088 return result
2089
2089
2090 def run_cell_magic(self, magic_name, line, cell):
2090 def run_cell_magic(self, magic_name, line, cell):
2091 """Execute the given cell magic.
2091 """Execute the given cell magic.
2092
2092
2093 Parameters
2093 Parameters
2094 ----------
2094 ----------
2095 magic_name : str
2095 magic_name : str
2096 Name of the desired magic function, without '%' prefix.
2096 Name of the desired magic function, without '%' prefix.
2097
2097
2098 line : str
2098 line : str
2099 The rest of the first input line as a single string.
2099 The rest of the first input line as a single string.
2100
2100
2101 cell : str
2101 cell : str
2102 The body of the cell as a (possibly multiline) string.
2102 The body of the cell as a (possibly multiline) string.
2103 """
2103 """
2104 fn = self.find_cell_magic(magic_name)
2104 fn = self.find_cell_magic(magic_name)
2105 if fn is None:
2105 if fn is None:
2106 lm = self.find_line_magic(magic_name)
2106 lm = self.find_line_magic(magic_name)
2107 etpl = "Cell magic function `%%%%%s` not found%s."
2107 etpl = "Cell magic function `%%%%%s` not found%s."
2108 extra = '' if lm is None else (' (But line magic `%%%s` exists, '
2108 extra = '' if lm is None else (' (But line magic `%%%s` exists, '
2109 'did you mean that instead?)' % magic_name )
2109 'did you mean that instead?)' % magic_name )
2110 error(etpl % (magic_name, extra))
2110 error(etpl % (magic_name, extra))
2111 else:
2111 else:
2112 # Note: this is the distance in the stack to the user's frame.
2112 # Note: this is the distance in the stack to the user's frame.
2113 # This will need to be updated if the internal calling logic gets
2113 # This will need to be updated if the internal calling logic gets
2114 # refactored, or else we'll be expanding the wrong variables.
2114 # refactored, or else we'll be expanding the wrong variables.
2115 stack_depth = 2
2115 stack_depth = 2
2116 magic_arg_s = self.var_expand(line, stack_depth)
2116 magic_arg_s = self.var_expand(line, stack_depth)
2117 with self.builtin_trap:
2117 with self.builtin_trap:
2118 result = fn(magic_arg_s, cell)
2118 result = fn(magic_arg_s, cell)
2119 return result
2119 return result
2120
2120
2121 def find_line_magic(self, magic_name):
2121 def find_line_magic(self, magic_name):
2122 """Find and return a line magic by name.
2122 """Find and return a line magic by name.
2123
2123
2124 Returns None if the magic isn't found."""
2124 Returns None if the magic isn't found."""
2125 return self.magics_manager.magics['line'].get(magic_name)
2125 return self.magics_manager.magics['line'].get(magic_name)
2126
2126
2127 def find_cell_magic(self, magic_name):
2127 def find_cell_magic(self, magic_name):
2128 """Find and return a cell magic by name.
2128 """Find and return a cell magic by name.
2129
2129
2130 Returns None if the magic isn't found."""
2130 Returns None if the magic isn't found."""
2131 return self.magics_manager.magics['cell'].get(magic_name)
2131 return self.magics_manager.magics['cell'].get(magic_name)
2132
2132
2133 def find_magic(self, magic_name, magic_kind='line'):
2133 def find_magic(self, magic_name, magic_kind='line'):
2134 """Find and return a magic of the given type by name.
2134 """Find and return a magic of the given type by name.
2135
2135
2136 Returns None if the magic isn't found."""
2136 Returns None if the magic isn't found."""
2137 return self.magics_manager.magics[magic_kind].get(magic_name)
2137 return self.magics_manager.magics[magic_kind].get(magic_name)
2138
2138
2139 def magic(self, arg_s):
2139 def magic(self, arg_s):
2140 """DEPRECATED. Use run_line_magic() instead.
2140 """DEPRECATED. Use run_line_magic() instead.
2141
2141
2142 Call a magic function by name.
2142 Call a magic function by name.
2143
2143
2144 Input: a string containing the name of the magic function to call and
2144 Input: a string containing the name of the magic function to call and
2145 any additional arguments to be passed to the magic.
2145 any additional arguments to be passed to the magic.
2146
2146
2147 magic('name -opt foo bar') is equivalent to typing at the ipython
2147 magic('name -opt foo bar') is equivalent to typing at the ipython
2148 prompt:
2148 prompt:
2149
2149
2150 In[1]: %name -opt foo bar
2150 In[1]: %name -opt foo bar
2151
2151
2152 To call a magic without arguments, simply use magic('name').
2152 To call a magic without arguments, simply use magic('name').
2153
2153
2154 This provides a proper Python function to call IPython's magics in any
2154 This provides a proper Python function to call IPython's magics in any
2155 valid Python code you can type at the interpreter, including loops and
2155 valid Python code you can type at the interpreter, including loops and
2156 compound statements.
2156 compound statements.
2157 """
2157 """
2158 # TODO: should we issue a loud deprecation warning here?
2158 # TODO: should we issue a loud deprecation warning here?
2159 magic_name, _, magic_arg_s = arg_s.partition(' ')
2159 magic_name, _, magic_arg_s = arg_s.partition(' ')
2160 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
2160 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
2161 return self.run_line_magic(magic_name, magic_arg_s)
2161 return self.run_line_magic(magic_name, magic_arg_s)
2162
2162
2163 #-------------------------------------------------------------------------
2163 #-------------------------------------------------------------------------
2164 # Things related to macros
2164 # Things related to macros
2165 #-------------------------------------------------------------------------
2165 #-------------------------------------------------------------------------
2166
2166
2167 def define_macro(self, name, themacro):
2167 def define_macro(self, name, themacro):
2168 """Define a new macro
2168 """Define a new macro
2169
2169
2170 Parameters
2170 Parameters
2171 ----------
2171 ----------
2172 name : str
2172 name : str
2173 The name of the macro.
2173 The name of the macro.
2174 themacro : str or Macro
2174 themacro : str or Macro
2175 The action to do upon invoking the macro. If a string, a new
2175 The action to do upon invoking the macro. If a string, a new
2176 Macro object is created by passing the string to it.
2176 Macro object is created by passing the string to it.
2177 """
2177 """
2178
2178
2179 from IPython.core import macro
2179 from IPython.core import macro
2180
2180
2181 if isinstance(themacro, basestring):
2181 if isinstance(themacro, basestring):
2182 themacro = macro.Macro(themacro)
2182 themacro = macro.Macro(themacro)
2183 if not isinstance(themacro, macro.Macro):
2183 if not isinstance(themacro, macro.Macro):
2184 raise ValueError('A macro must be a string or a Macro instance.')
2184 raise ValueError('A macro must be a string or a Macro instance.')
2185 self.user_ns[name] = themacro
2185 self.user_ns[name] = themacro
2186
2186
2187 #-------------------------------------------------------------------------
2187 #-------------------------------------------------------------------------
2188 # Things related to the running of system commands
2188 # Things related to the running of system commands
2189 #-------------------------------------------------------------------------
2189 #-------------------------------------------------------------------------
2190
2190
2191 def system_piped(self, cmd):
2191 def system_piped(self, cmd):
2192 """Call the given cmd in a subprocess, piping stdout/err
2192 """Call the given cmd in a subprocess, piping stdout/err
2193
2193
2194 Parameters
2194 Parameters
2195 ----------
2195 ----------
2196 cmd : str
2196 cmd : str
2197 Command to execute (can not end in '&', as background processes are
2197 Command to execute (can not end in '&', as background processes are
2198 not supported. Should not be a command that expects input
2198 not supported. Should not be a command that expects input
2199 other than simple text.
2199 other than simple text.
2200 """
2200 """
2201 if cmd.rstrip().endswith('&'):
2201 if cmd.rstrip().endswith('&'):
2202 # this is *far* from a rigorous test
2202 # this is *far* from a rigorous test
2203 # We do not support backgrounding processes because we either use
2203 # We do not support backgrounding processes because we either use
2204 # pexpect or pipes to read from. Users can always just call
2204 # pexpect or pipes to read from. Users can always just call
2205 # os.system() or use ip.system=ip.system_raw
2205 # os.system() or use ip.system=ip.system_raw
2206 # if they really want a background process.
2206 # if they really want a background process.
2207 raise OSError("Background processes not supported.")
2207 raise OSError("Background processes not supported.")
2208
2208
2209 # we explicitly do NOT return the subprocess status code, because
2209 # we explicitly do NOT return the subprocess status code, because
2210 # a non-None value would trigger :func:`sys.displayhook` calls.
2210 # a non-None value would trigger :func:`sys.displayhook` calls.
2211 # Instead, we store the exit_code in user_ns.
2211 # Instead, we store the exit_code in user_ns.
2212 self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=1))
2212 self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=1))
2213
2213
2214 def system_raw(self, cmd):
2214 def system_raw(self, cmd):
2215 """Call the given cmd in a subprocess using os.system
2215 """Call the given cmd in a subprocess using os.system
2216
2216
2217 Parameters
2217 Parameters
2218 ----------
2218 ----------
2219 cmd : str
2219 cmd : str
2220 Command to execute.
2220 Command to execute.
2221 """
2221 """
2222 cmd = self.var_expand(cmd, depth=1)
2222 cmd = self.var_expand(cmd, depth=1)
2223 # protect os.system from UNC paths on Windows, which it can't handle:
2223 # protect os.system from UNC paths on Windows, which it can't handle:
2224 if sys.platform == 'win32':
2224 if sys.platform == 'win32':
2225 from IPython.utils._process_win32 import AvoidUNCPath
2225 from IPython.utils._process_win32 import AvoidUNCPath
2226 with AvoidUNCPath() as path:
2226 with AvoidUNCPath() as path:
2227 if path is not None:
2227 if path is not None:
2228 cmd = '"pushd %s &&"%s' % (path, cmd)
2228 cmd = '"pushd %s &&"%s' % (path, cmd)
2229 cmd = py3compat.unicode_to_str(cmd)
2229 cmd = py3compat.unicode_to_str(cmd)
2230 ec = os.system(cmd)
2230 ec = os.system(cmd)
2231 else:
2231 else:
2232 cmd = py3compat.unicode_to_str(cmd)
2232 cmd = py3compat.unicode_to_str(cmd)
2233 ec = os.system(cmd)
2233 ec = os.system(cmd)
2234
2234
2235 # We explicitly do NOT return the subprocess status code, because
2235 # We explicitly do NOT return the subprocess status code, because
2236 # a non-None value would trigger :func:`sys.displayhook` calls.
2236 # a non-None value would trigger :func:`sys.displayhook` calls.
2237 # Instead, we store the exit_code in user_ns.
2237 # Instead, we store the exit_code in user_ns.
2238 self.user_ns['_exit_code'] = ec
2238 self.user_ns['_exit_code'] = ec
2239
2239
2240 # use piped system by default, because it is better behaved
2240 # use piped system by default, because it is better behaved
2241 system = system_piped
2241 system = system_piped
2242
2242
2243 def getoutput(self, cmd, split=True, depth=0):
2243 def getoutput(self, cmd, split=True, depth=0):
2244 """Get output (possibly including stderr) from a subprocess.
2244 """Get output (possibly including stderr) from a subprocess.
2245
2245
2246 Parameters
2246 Parameters
2247 ----------
2247 ----------
2248 cmd : str
2248 cmd : str
2249 Command to execute (can not end in '&', as background processes are
2249 Command to execute (can not end in '&', as background processes are
2250 not supported.
2250 not supported.
2251 split : bool, optional
2251 split : bool, optional
2252 If True, split the output into an IPython SList. Otherwise, an
2252 If True, split the output into an IPython SList. Otherwise, an
2253 IPython LSString is returned. These are objects similar to normal
2253 IPython LSString is returned. These are objects similar to normal
2254 lists and strings, with a few convenience attributes for easier
2254 lists and strings, with a few convenience attributes for easier
2255 manipulation of line-based output. You can use '?' on them for
2255 manipulation of line-based output. You can use '?' on them for
2256 details.
2256 details.
2257 depth : int, optional
2257 depth : int, optional
2258 How many frames above the caller are the local variables which should
2258 How many frames above the caller are the local variables which should
2259 be expanded in the command string? The default (0) assumes that the
2259 be expanded in the command string? The default (0) assumes that the
2260 expansion variables are in the stack frame calling this function.
2260 expansion variables are in the stack frame calling this function.
2261 """
2261 """
2262 if cmd.rstrip().endswith('&'):
2262 if cmd.rstrip().endswith('&'):
2263 # this is *far* from a rigorous test
2263 # this is *far* from a rigorous test
2264 raise OSError("Background processes not supported.")
2264 raise OSError("Background processes not supported.")
2265 out = getoutput(self.var_expand(cmd, depth=depth+1))
2265 out = getoutput(self.var_expand(cmd, depth=depth+1))
2266 if split:
2266 if split:
2267 out = SList(out.splitlines())
2267 out = SList(out.splitlines())
2268 else:
2268 else:
2269 out = LSString(out)
2269 out = LSString(out)
2270 return out
2270 return out
2271
2271
2272 #-------------------------------------------------------------------------
2272 #-------------------------------------------------------------------------
2273 # Things related to aliases
2273 # Things related to aliases
2274 #-------------------------------------------------------------------------
2274 #-------------------------------------------------------------------------
2275
2275
2276 def init_alias(self):
2276 def init_alias(self):
2277 self.alias_manager = AliasManager(shell=self, config=self.config)
2277 self.alias_manager = AliasManager(shell=self, config=self.config)
2278 self.configurables.append(self.alias_manager)
2278 self.configurables.append(self.alias_manager)
2279 self.ns_table['alias'] = self.alias_manager.alias_table,
2279 self.ns_table['alias'] = self.alias_manager.alias_table,
2280
2280
2281 #-------------------------------------------------------------------------
2281 #-------------------------------------------------------------------------
2282 # Things related to extensions
2282 # Things related to extensions
2283 #-------------------------------------------------------------------------
2283 #-------------------------------------------------------------------------
2284
2284
2285 def init_extension_manager(self):
2285 def init_extension_manager(self):
2286 self.extension_manager = ExtensionManager(shell=self, config=self.config)
2286 self.extension_manager = ExtensionManager(shell=self, config=self.config)
2287 self.configurables.append(self.extension_manager)
2287 self.configurables.append(self.extension_manager)
2288
2288
2289 #-------------------------------------------------------------------------
2289 #-------------------------------------------------------------------------
2290 # Things related to payloads
2290 # Things related to payloads
2291 #-------------------------------------------------------------------------
2291 #-------------------------------------------------------------------------
2292
2292
2293 def init_payload(self):
2293 def init_payload(self):
2294 self.payload_manager = PayloadManager(config=self.config)
2294 self.payload_manager = PayloadManager(config=self.config)
2295 self.configurables.append(self.payload_manager)
2295 self.configurables.append(self.payload_manager)
2296
2296
2297 #-------------------------------------------------------------------------
2297 #-------------------------------------------------------------------------
2298 # Things related to the prefilter
2298 # Things related to the prefilter
2299 #-------------------------------------------------------------------------
2299 #-------------------------------------------------------------------------
2300
2300
2301 def init_prefilter(self):
2301 def init_prefilter(self):
2302 self.prefilter_manager = PrefilterManager(shell=self, config=self.config)
2302 self.prefilter_manager = PrefilterManager(shell=self, config=self.config)
2303 self.configurables.append(self.prefilter_manager)
2303 self.configurables.append(self.prefilter_manager)
2304 # Ultimately this will be refactored in the new interpreter code, but
2304 # Ultimately this will be refactored in the new interpreter code, but
2305 # for now, we should expose the main prefilter method (there's legacy
2305 # for now, we should expose the main prefilter method (there's legacy
2306 # code out there that may rely on this).
2306 # code out there that may rely on this).
2307 self.prefilter = self.prefilter_manager.prefilter_lines
2307 self.prefilter = self.prefilter_manager.prefilter_lines
2308
2308
2309 def auto_rewrite_input(self, cmd):
2309 def auto_rewrite_input(self, cmd):
2310 """Print to the screen the rewritten form of the user's command.
2310 """Print to the screen the rewritten form of the user's command.
2311
2311
2312 This shows visual feedback by rewriting input lines that cause
2312 This shows visual feedback by rewriting input lines that cause
2313 automatic calling to kick in, like::
2313 automatic calling to kick in, like::
2314
2314
2315 /f x
2315 /f x
2316
2316
2317 into::
2317 into::
2318
2318
2319 ------> f(x)
2319 ------> f(x)
2320
2320
2321 after the user's input prompt. This helps the user understand that the
2321 after the user's input prompt. This helps the user understand that the
2322 input line was transformed automatically by IPython.
2322 input line was transformed automatically by IPython.
2323 """
2323 """
2324 if not self.show_rewritten_input:
2324 if not self.show_rewritten_input:
2325 return
2325 return
2326
2326
2327 rw = self.prompt_manager.render('rewrite') + cmd
2327 rw = self.prompt_manager.render('rewrite') + cmd
2328
2328
2329 try:
2329 try:
2330 # plain ascii works better w/ pyreadline, on some machines, so
2330 # plain ascii works better w/ pyreadline, on some machines, so
2331 # we use it and only print uncolored rewrite if we have unicode
2331 # we use it and only print uncolored rewrite if we have unicode
2332 rw = str(rw)
2332 rw = str(rw)
2333 print(rw, file=io.stdout)
2333 print(rw, file=io.stdout)
2334 except UnicodeEncodeError:
2334 except UnicodeEncodeError:
2335 print("------> " + cmd)
2335 print("------> " + cmd)
2336
2336
2337 #-------------------------------------------------------------------------
2337 #-------------------------------------------------------------------------
2338 # Things related to extracting values/expressions from kernel and user_ns
2338 # Things related to extracting values/expressions from kernel and user_ns
2339 #-------------------------------------------------------------------------
2339 #-------------------------------------------------------------------------
2340
2340
2341 def _simple_error(self):
2341 def _simple_error(self):
2342 etype, value = sys.exc_info()[:2]
2342 etype, value = sys.exc_info()[:2]
2343 return u'[ERROR] {e.__name__}: {v}'.format(e=etype, v=value)
2343 return u'[ERROR] {e.__name__}: {v}'.format(e=etype, v=value)
2344
2344
2345 def user_variables(self, names):
2345 def user_variables(self, names):
2346 """Get a list of variable names from the user's namespace.
2346 """Get a list of variable names from the user's namespace.
2347
2347
2348 Parameters
2348 Parameters
2349 ----------
2349 ----------
2350 names : list of strings
2350 names : list of strings
2351 A list of names of variables to be read from the user namespace.
2351 A list of names of variables to be read from the user namespace.
2352
2352
2353 Returns
2353 Returns
2354 -------
2354 -------
2355 A dict, keyed by the input names and with the repr() of each value.
2355 A dict, keyed by the input names and with the repr() of each value.
2356 """
2356 """
2357 out = {}
2357 out = {}
2358 user_ns = self.user_ns
2358 user_ns = self.user_ns
2359 for varname in names:
2359 for varname in names:
2360 try:
2360 try:
2361 value = repr(user_ns[varname])
2361 value = repr(user_ns[varname])
2362 except:
2362 except:
2363 value = self._simple_error()
2363 value = self._simple_error()
2364 out[varname] = value
2364 out[varname] = value
2365 return out
2365 return out
2366
2366
2367 def user_expressions(self, expressions):
2367 def user_expressions(self, expressions):
2368 """Evaluate a dict of expressions in the user's namespace.
2368 """Evaluate a dict of expressions in the user's namespace.
2369
2369
2370 Parameters
2370 Parameters
2371 ----------
2371 ----------
2372 expressions : dict
2372 expressions : dict
2373 A dict with string keys and string values. The expression values
2373 A dict with string keys and string values. The expression values
2374 should be valid Python expressions, each of which will be evaluated
2374 should be valid Python expressions, each of which will be evaluated
2375 in the user namespace.
2375 in the user namespace.
2376
2376
2377 Returns
2377 Returns
2378 -------
2378 -------
2379 A dict, keyed like the input expressions dict, with the repr() of each
2379 A dict, keyed like the input expressions dict, with the repr() of each
2380 value.
2380 value.
2381 """
2381 """
2382 out = {}
2382 out = {}
2383 user_ns = self.user_ns
2383 user_ns = self.user_ns
2384 global_ns = self.user_global_ns
2384 global_ns = self.user_global_ns
2385 for key, expr in expressions.iteritems():
2385 for key, expr in expressions.iteritems():
2386 try:
2386 try:
2387 value = repr(eval(expr, global_ns, user_ns))
2387 value = repr(eval(expr, global_ns, user_ns))
2388 except:
2388 except:
2389 value = self._simple_error()
2389 value = self._simple_error()
2390 out[key] = value
2390 out[key] = value
2391 return out
2391 return out
2392
2392
2393 #-------------------------------------------------------------------------
2393 #-------------------------------------------------------------------------
2394 # Things related to the running of code
2394 # Things related to the running of code
2395 #-------------------------------------------------------------------------
2395 #-------------------------------------------------------------------------
2396
2396
2397 def ex(self, cmd):
2397 def ex(self, cmd):
2398 """Execute a normal python statement in user namespace."""
2398 """Execute a normal python statement in user namespace."""
2399 with self.builtin_trap:
2399 with self.builtin_trap:
2400 exec cmd in self.user_global_ns, self.user_ns
2400 exec cmd in self.user_global_ns, self.user_ns
2401
2401
2402 def ev(self, expr):
2402 def ev(self, expr):
2403 """Evaluate python expression expr in user namespace.
2403 """Evaluate python expression expr in user namespace.
2404
2404
2405 Returns the result of evaluation
2405 Returns the result of evaluation
2406 """
2406 """
2407 with self.builtin_trap:
2407 with self.builtin_trap:
2408 return eval(expr, self.user_global_ns, self.user_ns)
2408 return eval(expr, self.user_global_ns, self.user_ns)
2409
2409
2410 def safe_execfile(self, fname, *where, **kw):
2410 def safe_execfile(self, fname, *where, **kw):
2411 """A safe version of the builtin execfile().
2411 """A safe version of the builtin execfile().
2412
2412
2413 This version will never throw an exception, but instead print
2413 This version will never throw an exception, but instead print
2414 helpful error messages to the screen. This only works on pure
2414 helpful error messages to the screen. This only works on pure
2415 Python files with the .py extension.
2415 Python files with the .py extension.
2416
2416
2417 Parameters
2417 Parameters
2418 ----------
2418 ----------
2419 fname : string
2419 fname : string
2420 The name of the file to be executed.
2420 The name of the file to be executed.
2421 where : tuple
2421 where : tuple
2422 One or two namespaces, passed to execfile() as (globals,locals).
2422 One or two namespaces, passed to execfile() as (globals,locals).
2423 If only one is given, it is passed as both.
2423 If only one is given, it is passed as both.
2424 exit_ignore : bool (False)
2424 exit_ignore : bool (False)
2425 If True, then silence SystemExit for non-zero status (it is always
2425 If True, then silence SystemExit for non-zero status (it is always
2426 silenced for zero status, as it is so common).
2426 silenced for zero status, as it is so common).
2427 raise_exceptions : bool (False)
2427 raise_exceptions : bool (False)
2428 If True raise exceptions everywhere. Meant for testing.
2428 If True raise exceptions everywhere. Meant for testing.
2429
2429
2430 """
2430 """
2431 kw.setdefault('exit_ignore', False)
2431 kw.setdefault('exit_ignore', False)
2432 kw.setdefault('raise_exceptions', False)
2432 kw.setdefault('raise_exceptions', False)
2433
2433
2434 fname = os.path.abspath(os.path.expanduser(fname))
2434 fname = os.path.abspath(os.path.expanduser(fname))
2435
2435
2436 # Make sure we can open the file
2436 # Make sure we can open the file
2437 try:
2437 try:
2438 with open(fname) as thefile:
2438 with open(fname) as thefile:
2439 pass
2439 pass
2440 except:
2440 except:
2441 warn('Could not open file <%s> for safe execution.' % fname)
2441 warn('Could not open file <%s> for safe execution.' % fname)
2442 return
2442 return
2443
2443
2444 # Find things also in current directory. This is needed to mimic the
2444 # Find things also in current directory. This is needed to mimic the
2445 # behavior of running a script from the system command line, where
2445 # behavior of running a script from the system command line, where
2446 # Python inserts the script's directory into sys.path
2446 # Python inserts the script's directory into sys.path
2447 dname = os.path.dirname(fname)
2447 dname = os.path.dirname(fname)
2448
2448
2449 with prepended_to_syspath(dname):
2449 with prepended_to_syspath(dname):
2450 try:
2450 try:
2451 py3compat.execfile(fname,*where)
2451 py3compat.execfile(fname,*where)
2452 except SystemExit as status:
2452 except SystemExit as status:
2453 # If the call was made with 0 or None exit status (sys.exit(0)
2453 # If the call was made with 0 or None exit status (sys.exit(0)
2454 # or sys.exit() ), don't bother showing a traceback, as both of
2454 # or sys.exit() ), don't bother showing a traceback, as both of
2455 # these are considered normal by the OS:
2455 # these are considered normal by the OS:
2456 # > python -c'import sys;sys.exit(0)'; echo $?
2456 # > python -c'import sys;sys.exit(0)'; echo $?
2457 # 0
2457 # 0
2458 # > python -c'import sys;sys.exit()'; echo $?
2458 # > python -c'import sys;sys.exit()'; echo $?
2459 # 0
2459 # 0
2460 # For other exit status, we show the exception unless
2460 # For other exit status, we show the exception unless
2461 # explicitly silenced, but only in short form.
2461 # explicitly silenced, but only in short form.
2462 if kw['raise_exceptions']:
2462 if kw['raise_exceptions']:
2463 raise
2463 raise
2464 if status.code not in (0, None) and not kw['exit_ignore']:
2464 if status.code not in (0, None) and not kw['exit_ignore']:
2465 self.showtraceback(exception_only=True)
2465 self.showtraceback(exception_only=True)
2466 except:
2466 except:
2467 if kw['raise_exceptions']:
2467 if kw['raise_exceptions']:
2468 raise
2468 raise
2469 self.showtraceback()
2469 self.showtraceback()
2470
2470
2471 def safe_execfile_ipy(self, fname):
2471 def safe_execfile_ipy(self, fname):
2472 """Like safe_execfile, but for .ipy files with IPython syntax.
2472 """Like safe_execfile, but for .ipy files with IPython syntax.
2473
2473
2474 Parameters
2474 Parameters
2475 ----------
2475 ----------
2476 fname : str
2476 fname : str
2477 The name of the file to execute. The filename must have a
2477 The name of the file to execute. The filename must have a
2478 .ipy extension.
2478 .ipy extension.
2479 """
2479 """
2480 fname = os.path.abspath(os.path.expanduser(fname))
2480 fname = os.path.abspath(os.path.expanduser(fname))
2481
2481
2482 # Make sure we can open the file
2482 # Make sure we can open the file
2483 try:
2483 try:
2484 with open(fname) as thefile:
2484 with open(fname) as thefile:
2485 pass
2485 pass
2486 except:
2486 except:
2487 warn('Could not open file <%s> for safe execution.' % fname)
2487 warn('Could not open file <%s> for safe execution.' % fname)
2488 return
2488 return
2489
2489
2490 # Find things also in current directory. This is needed to mimic the
2490 # Find things also in current directory. This is needed to mimic the
2491 # behavior of running a script from the system command line, where
2491 # behavior of running a script from the system command line, where
2492 # Python inserts the script's directory into sys.path
2492 # Python inserts the script's directory into sys.path
2493 dname = os.path.dirname(fname)
2493 dname = os.path.dirname(fname)
2494
2494
2495 with prepended_to_syspath(dname):
2495 with prepended_to_syspath(dname):
2496 try:
2496 try:
2497 with open(fname) as thefile:
2497 with open(fname) as thefile:
2498 # self.run_cell currently captures all exceptions
2498 # self.run_cell currently captures all exceptions
2499 # raised in user code. It would be nice if there were
2499 # raised in user code. It would be nice if there were
2500 # versions of runlines, execfile that did raise, so
2500 # versions of runlines, execfile that did raise, so
2501 # we could catch the errors.
2501 # we could catch the errors.
2502 self.run_cell(thefile.read(), store_history=False)
2502 self.run_cell(thefile.read(), store_history=False)
2503 except:
2503 except:
2504 self.showtraceback()
2504 self.showtraceback()
2505 warn('Unknown failure executing file: <%s>' % fname)
2505 warn('Unknown failure executing file: <%s>' % fname)
2506
2506
2507 def safe_run_module(self, mod_name, where):
2507 def safe_run_module(self, mod_name, where):
2508 """A safe version of runpy.run_module().
2508 """A safe version of runpy.run_module().
2509
2509
2510 This version will never throw an exception, but instead print
2510 This version will never throw an exception, but instead print
2511 helpful error messages to the screen.
2511 helpful error messages to the screen.
2512
2512
2513 Parameters
2513 Parameters
2514 ----------
2514 ----------
2515 mod_name : string
2515 mod_name : string
2516 The name of the module to be executed.
2516 The name of the module to be executed.
2517 where : dict
2517 where : dict
2518 The globals namespace.
2518 The globals namespace.
2519 """
2519 """
2520 try:
2520 try:
2521 where.update(
2521 where.update(
2522 runpy.run_module(str(mod_name), run_name="__main__",
2522 runpy.run_module(str(mod_name), run_name="__main__",
2523 alter_sys=True)
2523 alter_sys=True)
2524 )
2524 )
2525 except:
2525 except:
2526 self.showtraceback()
2526 self.showtraceback()
2527 warn('Unknown failure executing module: <%s>' % mod_name)
2527 warn('Unknown failure executing module: <%s>' % mod_name)
2528
2528
2529 def _run_cached_cell_magic(self, magic_name, line):
2529 def _run_cached_cell_magic(self, magic_name, line):
2530 """Special method to call a cell magic with the data stored in self.
2530 """Special method to call a cell magic with the data stored in self.
2531 """
2531 """
2532 cell = self._current_cell_magic_body
2532 cell = self._current_cell_magic_body
2533 self._current_cell_magic_body = None
2533 self._current_cell_magic_body = None
2534 return self.run_cell_magic(magic_name, line, cell)
2534 return self.run_cell_magic(magic_name, line, cell)
2535
2535
2536 def run_cell(self, raw_cell, store_history=False, silent=False):
2536 def run_cell(self, raw_cell, store_history=False, silent=False):
2537 """Run a complete IPython cell.
2537 """Run a complete IPython cell.
2538
2538
2539 Parameters
2539 Parameters
2540 ----------
2540 ----------
2541 raw_cell : str
2541 raw_cell : str
2542 The code (including IPython code such as %magic functions) to run.
2542 The code (including IPython code such as %magic functions) to run.
2543 store_history : bool
2543 store_history : bool
2544 If True, the raw and translated cell will be stored in IPython's
2544 If True, the raw and translated cell will be stored in IPython's
2545 history. For user code calling back into IPython's machinery, this
2545 history. For user code calling back into IPython's machinery, this
2546 should be set to False.
2546 should be set to False.
2547 silent : bool
2547 silent : bool
2548 If True, avoid side-effects, such as implicit displayhooks and
2548 If True, avoid side-effects, such as implicit displayhooks and
2549 and logging. silent=True forces store_history=False.
2549 and logging. silent=True forces store_history=False.
2550 """
2550 """
2551 if (not raw_cell) or raw_cell.isspace():
2551 if (not raw_cell) or raw_cell.isspace():
2552 return
2552 return
2553
2553
2554 if silent:
2554 if silent:
2555 store_history = False
2555 store_history = False
2556
2556
2557 self.input_splitter.push(raw_cell)
2557 self.input_splitter.push(raw_cell)
2558
2558
2559 # Check for cell magics, which leave state behind. This interface is
2559 # Check for cell magics, which leave state behind. This interface is
2560 # ugly, we need to do something cleaner later... Now the logic is
2560 # ugly, we need to do something cleaner later... Now the logic is
2561 # simply that the input_splitter remembers if there was a cell magic,
2561 # simply that the input_splitter remembers if there was a cell magic,
2562 # and in that case we grab the cell body.
2562 # and in that case we grab the cell body.
2563 if self.input_splitter.cell_magic_parts:
2563 if self.input_splitter.cell_magic_parts:
2564 self._current_cell_magic_body = \
2564 self._current_cell_magic_body = \
2565 ''.join(self.input_splitter.cell_magic_parts)
2565 ''.join(self.input_splitter.cell_magic_parts)
2566 cell = self.input_splitter.source_reset()
2566 cell = self.input_splitter.source_reset()
2567
2567
2568 with self.builtin_trap:
2568 with self.builtin_trap:
2569 prefilter_failed = False
2569 prefilter_failed = False
2570 if len(cell.splitlines()) == 1:
2570 if len(cell.splitlines()) == 1:
2571 try:
2571 try:
2572 # use prefilter_lines to handle trailing newlines
2572 # use prefilter_lines to handle trailing newlines
2573 # restore trailing newline for ast.parse
2573 # restore trailing newline for ast.parse
2574 cell = self.prefilter_manager.prefilter_lines(cell) + '\n'
2574 cell = self.prefilter_manager.prefilter_lines(cell) + '\n'
2575 except AliasError as e:
2575 except AliasError as e:
2576 error(e)
2576 error(e)
2577 prefilter_failed = True
2577 prefilter_failed = True
2578 except Exception:
2578 except Exception:
2579 # don't allow prefilter errors to crash IPython
2579 # don't allow prefilter errors to crash IPython
2580 self.showtraceback()
2580 self.showtraceback()
2581 prefilter_failed = True
2581 prefilter_failed = True
2582
2582
2583 # Store raw and processed history
2583 # Store raw and processed history
2584 if store_history:
2584 if store_history:
2585 self.history_manager.store_inputs(self.execution_count,
2585 self.history_manager.store_inputs(self.execution_count,
2586 cell, raw_cell)
2586 cell, raw_cell)
2587 if not silent:
2587 if not silent:
2588 self.logger.log(cell, raw_cell)
2588 self.logger.log(cell, raw_cell)
2589
2589
2590 if not prefilter_failed:
2590 if not prefilter_failed:
2591 # don't run if prefilter failed
2591 # don't run if prefilter failed
2592 cell_name = self.compile.cache(cell, self.execution_count)
2592 cell_name = self.compile.cache(cell, self.execution_count)
2593
2593
2594 with self.display_trap:
2594 with self.display_trap:
2595 try:
2595 try:
2596 code_ast = self.compile.ast_parse(cell,
2596 code_ast = self.compile.ast_parse(cell,
2597 filename=cell_name)
2597 filename=cell_name)
2598 except IndentationError:
2598 except IndentationError:
2599 self.showindentationerror()
2599 self.showindentationerror()
2600 if store_history:
2600 if store_history:
2601 self.execution_count += 1
2601 self.execution_count += 1
2602 return None
2602 return None
2603 except (OverflowError, SyntaxError, ValueError, TypeError,
2603 except (OverflowError, SyntaxError, ValueError, TypeError,
2604 MemoryError):
2604 MemoryError):
2605 self.showsyntaxerror()
2605 self.showsyntaxerror()
2606 if store_history:
2606 if store_history:
2607 self.execution_count += 1
2607 self.execution_count += 1
2608 return None
2608 return None
2609
2609
2610 interactivity = "none" if silent else self.ast_node_interactivity
2610 interactivity = "none" if silent else self.ast_node_interactivity
2611 self.run_ast_nodes(code_ast.body, cell_name,
2611 self.run_ast_nodes(code_ast.body, cell_name,
2612 interactivity=interactivity)
2612 interactivity=interactivity)
2613
2613
2614 # Execute any registered post-execution functions.
2614 # Execute any registered post-execution functions.
2615 # unless we are silent
2615 # unless we are silent
2616 post_exec = [] if silent else self._post_execute.iteritems()
2616 post_exec = [] if silent else self._post_execute.iteritems()
2617
2617
2618 for func, status in post_exec:
2618 for func, status in post_exec:
2619 if self.disable_failing_post_execute and not status:
2619 if self.disable_failing_post_execute and not status:
2620 continue
2620 continue
2621 try:
2621 try:
2622 func()
2622 func()
2623 except KeyboardInterrupt:
2623 except KeyboardInterrupt:
2624 print("\nKeyboardInterrupt", file=io.stderr)
2624 print("\nKeyboardInterrupt", file=io.stderr)
2625 except Exception:
2625 except Exception:
2626 # register as failing:
2626 # register as failing:
2627 self._post_execute[func] = False
2627 self._post_execute[func] = False
2628 self.showtraceback()
2628 self.showtraceback()
2629 print('\n'.join([
2629 print('\n'.join([
2630 "post-execution function %r produced an error." % func,
2630 "post-execution function %r produced an error." % func,
2631 "If this problem persists, you can disable failing post-exec functions with:",
2631 "If this problem persists, you can disable failing post-exec functions with:",
2632 "",
2632 "",
2633 " get_ipython().disable_failing_post_execute = True"
2633 " get_ipython().disable_failing_post_execute = True"
2634 ]), file=io.stderr)
2634 ]), file=io.stderr)
2635
2635
2636 if store_history:
2636 if store_history:
2637 # Write output to the database. Does nothing unless
2637 # Write output to the database. Does nothing unless
2638 # history output logging is enabled.
2638 # history output logging is enabled.
2639 self.history_manager.store_output(self.execution_count)
2639 self.history_manager.store_output(self.execution_count)
2640 # Each cell is a *single* input, regardless of how many lines it has
2640 # Each cell is a *single* input, regardless of how many lines it has
2641 self.execution_count += 1
2641 self.execution_count += 1
2642
2642
2643 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'):
2643 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'):
2644 """Run a sequence of AST nodes. The execution mode depends on the
2644 """Run a sequence of AST nodes. The execution mode depends on the
2645 interactivity parameter.
2645 interactivity parameter.
2646
2646
2647 Parameters
2647 Parameters
2648 ----------
2648 ----------
2649 nodelist : list
2649 nodelist : list
2650 A sequence of AST nodes to run.
2650 A sequence of AST nodes to run.
2651 cell_name : str
2651 cell_name : str
2652 Will be passed to the compiler as the filename of the cell. Typically
2652 Will be passed to the compiler as the filename of the cell. Typically
2653 the value returned by ip.compile.cache(cell).
2653 the value returned by ip.compile.cache(cell).
2654 interactivity : str
2654 interactivity : str
2655 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
2655 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
2656 run interactively (displaying output from expressions). 'last_expr'
2656 run interactively (displaying output from expressions). 'last_expr'
2657 will run the last node interactively only if it is an expression (i.e.
2657 will run the last node interactively only if it is an expression (i.e.
2658 expressions in loops or other blocks are not displayed. Other values
2658 expressions in loops or other blocks are not displayed. Other values
2659 for this parameter will raise a ValueError.
2659 for this parameter will raise a ValueError.
2660 """
2660 """
2661 if not nodelist:
2661 if not nodelist:
2662 return
2662 return
2663
2663
2664 if interactivity == 'last_expr':
2664 if interactivity == 'last_expr':
2665 if isinstance(nodelist[-1], ast.Expr):
2665 if isinstance(nodelist[-1], ast.Expr):
2666 interactivity = "last"
2666 interactivity = "last"
2667 else:
2667 else:
2668 interactivity = "none"
2668 interactivity = "none"
2669
2669
2670 if interactivity == 'none':
2670 if interactivity == 'none':
2671 to_run_exec, to_run_interactive = nodelist, []
2671 to_run_exec, to_run_interactive = nodelist, []
2672 elif interactivity == 'last':
2672 elif interactivity == 'last':
2673 to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:]
2673 to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:]
2674 elif interactivity == 'all':
2674 elif interactivity == 'all':
2675 to_run_exec, to_run_interactive = [], nodelist
2675 to_run_exec, to_run_interactive = [], nodelist
2676 else:
2676 else:
2677 raise ValueError("Interactivity was %r" % interactivity)
2677 raise ValueError("Interactivity was %r" % interactivity)
2678
2678
2679 exec_count = self.execution_count
2679 exec_count = self.execution_count
2680
2680
2681 try:
2681 try:
2682 for i, node in enumerate(to_run_exec):
2682 for i, node in enumerate(to_run_exec):
2683 mod = ast.Module([node])
2683 mod = ast.Module([node])
2684 code = self.compile(mod, cell_name, "exec")
2684 code = self.compile(mod, cell_name, "exec")
2685 if self.run_code(code):
2685 if self.run_code(code):
2686 return True
2686 return True
2687
2687
2688 for i, node in enumerate(to_run_interactive):
2688 for i, node in enumerate(to_run_interactive):
2689 mod = ast.Interactive([node])
2689 mod = ast.Interactive([node])
2690 code = self.compile(mod, cell_name, "single")
2690 code = self.compile(mod, cell_name, "single")
2691 if self.run_code(code):
2691 if self.run_code(code):
2692 return True
2692 return True
2693
2693
2694 # Flush softspace
2694 # Flush softspace
2695 if softspace(sys.stdout, 0):
2695 if softspace(sys.stdout, 0):
2696 print()
2696 print()
2697
2697
2698 except:
2698 except:
2699 # It's possible to have exceptions raised here, typically by
2699 # It's possible to have exceptions raised here, typically by
2700 # compilation of odd code (such as a naked 'return' outside a
2700 # compilation of odd code (such as a naked 'return' outside a
2701 # function) that did parse but isn't valid. Typically the exception
2701 # function) that did parse but isn't valid. Typically the exception
2702 # is a SyntaxError, but it's safest just to catch anything and show
2702 # is a SyntaxError, but it's safest just to catch anything and show
2703 # the user a traceback.
2703 # the user a traceback.
2704
2704
2705 # We do only one try/except outside the loop to minimize the impact
2705 # We do only one try/except outside the loop to minimize the impact
2706 # on runtime, and also because if any node in the node list is
2706 # on runtime, and also because if any node in the node list is
2707 # broken, we should stop execution completely.
2707 # broken, we should stop execution completely.
2708 self.showtraceback()
2708 self.showtraceback()
2709
2709
2710 return False
2710 return False
2711
2711
2712 def run_code(self, code_obj):
2712 def run_code(self, code_obj):
2713 """Execute a code object.
2713 """Execute a code object.
2714
2714
2715 When an exception occurs, self.showtraceback() is called to display a
2715 When an exception occurs, self.showtraceback() is called to display a
2716 traceback.
2716 traceback.
2717
2717
2718 Parameters
2718 Parameters
2719 ----------
2719 ----------
2720 code_obj : code object
2720 code_obj : code object
2721 A compiled code object, to be executed
2721 A compiled code object, to be executed
2722
2722
2723 Returns
2723 Returns
2724 -------
2724 -------
2725 False : successful execution.
2725 False : successful execution.
2726 True : an error occurred.
2726 True : an error occurred.
2727 """
2727 """
2728
2728
2729 # Set our own excepthook in case the user code tries to call it
2729 # Set our own excepthook in case the user code tries to call it
2730 # directly, so that the IPython crash handler doesn't get triggered
2730 # directly, so that the IPython crash handler doesn't get triggered
2731 old_excepthook,sys.excepthook = sys.excepthook, self.excepthook
2731 old_excepthook,sys.excepthook = sys.excepthook, self.excepthook
2732
2732
2733 # we save the original sys.excepthook in the instance, in case config
2733 # we save the original sys.excepthook in the instance, in case config
2734 # code (such as magics) needs access to it.
2734 # code (such as magics) needs access to it.
2735 self.sys_excepthook = old_excepthook
2735 self.sys_excepthook = old_excepthook
2736 outflag = 1 # happens in more places, so it's easier as default
2736 outflag = 1 # happens in more places, so it's easier as default
2737 try:
2737 try:
2738 try:
2738 try:
2739 self.hooks.pre_run_code_hook()
2739 self.hooks.pre_run_code_hook()
2740 #rprint('Running code', repr(code_obj)) # dbg
2740 #rprint('Running code', repr(code_obj)) # dbg
2741 exec code_obj in self.user_global_ns, self.user_ns
2741 exec code_obj in self.user_global_ns, self.user_ns
2742 finally:
2742 finally:
2743 # Reset our crash handler in place
2743 # Reset our crash handler in place
2744 sys.excepthook = old_excepthook
2744 sys.excepthook = old_excepthook
2745 except SystemExit:
2745 except SystemExit:
2746 self.showtraceback(exception_only=True)
2746 self.showtraceback(exception_only=True)
2747 warn("To exit: use 'exit', 'quit', or Ctrl-D.", level=1)
2747 warn("To exit: use 'exit', 'quit', or Ctrl-D.", level=1)
2748 except self.custom_exceptions:
2748 except self.custom_exceptions:
2749 etype,value,tb = sys.exc_info()
2749 etype,value,tb = sys.exc_info()
2750 self.CustomTB(etype,value,tb)
2750 self.CustomTB(etype,value,tb)
2751 except:
2751 except:
2752 self.showtraceback()
2752 self.showtraceback()
2753 else:
2753 else:
2754 outflag = 0
2754 outflag = 0
2755 return outflag
2755 return outflag
2756
2756
2757 # For backwards compatibility
2757 # For backwards compatibility
2758 runcode = run_code
2758 runcode = run_code
2759
2759
2760 #-------------------------------------------------------------------------
2760 #-------------------------------------------------------------------------
2761 # Things related to GUI support and pylab
2761 # Things related to GUI support and pylab
2762 #-------------------------------------------------------------------------
2762 #-------------------------------------------------------------------------
2763
2763
2764 def enable_gui(self, gui=None):
2764 def enable_gui(self, gui=None):
2765 raise NotImplementedError('Implement enable_gui in a subclass')
2765 raise NotImplementedError('Implement enable_gui in a subclass')
2766
2766
2767 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
2767 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
2768 """Activate pylab support at runtime.
2768 """Activate pylab support at runtime.
2769
2769
2770 This turns on support for matplotlib, preloads into the interactive
2770 This turns on support for matplotlib, preloads into the interactive
2771 namespace all of numpy and pylab, and configures IPython to correctly
2771 namespace all of numpy and pylab, and configures IPython to correctly
2772 interact with the GUI event loop. The GUI backend to be used can be
2772 interact with the GUI event loop. The GUI backend to be used can be
2773 optionally selected with the optional :param:`gui` argument.
2773 optionally selected with the optional :param:`gui` argument.
2774
2774
2775 Parameters
2775 Parameters
2776 ----------
2776 ----------
2777 gui : optional, string
2777 gui : optional, string
2778
2778
2779 If given, dictates the choice of matplotlib GUI backend to use
2779 If given, dictates the choice of matplotlib GUI backend to use
2780 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
2780 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
2781 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
2781 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
2782 matplotlib (as dictated by the matplotlib build-time options plus the
2782 matplotlib (as dictated by the matplotlib build-time options plus the
2783 user's matplotlibrc configuration file). Note that not all backends
2783 user's matplotlibrc configuration file). Note that not all backends
2784 make sense in all contexts, for example a terminal ipython can't
2784 make sense in all contexts, for example a terminal ipython can't
2785 display figures inline.
2785 display figures inline.
2786 """
2786 """
2787 from IPython.core.pylabtools import mpl_runner
2787 from IPython.core.pylabtools import mpl_runner
2788 # We want to prevent the loading of pylab to pollute the user's
2788 # We want to prevent the loading of pylab to pollute the user's
2789 # namespace as shown by the %who* magics, so we execute the activation
2789 # namespace as shown by the %who* magics, so we execute the activation
2790 # code in an empty namespace, and we update *both* user_ns and
2790 # code in an empty namespace, and we update *both* user_ns and
2791 # user_ns_hidden with this information.
2791 # user_ns_hidden with this information.
2792 ns = {}
2792 ns = {}
2793 try:
2793 try:
2794 gui = pylab_activate(ns, gui, import_all, self, welcome_message=welcome_message)
2794 gui = pylab_activate(ns, gui, import_all, self, welcome_message=welcome_message)
2795 except KeyError:
2795 except KeyError:
2796 error("Backend %r not supported" % gui)
2796 error("Backend %r not supported" % gui)
2797 return
2797 return
2798 self.user_ns.update(ns)
2798 self.user_ns.update(ns)
2799 self.user_ns_hidden.update(ns)
2799 self.user_ns_hidden.update(ns)
2800 # Now we must activate the gui pylab wants to use, and fix %run to take
2800 # Now we must activate the gui pylab wants to use, and fix %run to take
2801 # plot updates into account
2801 # plot updates into account
2802 self.enable_gui(gui)
2802 self.enable_gui(gui)
2803 self.magics_manager.registry['ExecutionMagics'].default_runner = \
2803 self.magics_manager.registry['ExecutionMagics'].default_runner = \
2804 mpl_runner(self.safe_execfile)
2804 mpl_runner(self.safe_execfile)
2805
2805
2806 #-------------------------------------------------------------------------
2806 #-------------------------------------------------------------------------
2807 # Utilities
2807 # Utilities
2808 #-------------------------------------------------------------------------
2808 #-------------------------------------------------------------------------
2809
2809
2810 def var_expand(self, cmd, depth=0, formatter=DollarFormatter()):
2810 def var_expand(self, cmd, depth=0, formatter=DollarFormatter()):
2811 """Expand python variables in a string.
2811 """Expand python variables in a string.
2812
2812
2813 The depth argument indicates how many frames above the caller should
2813 The depth argument indicates how many frames above the caller should
2814 be walked to look for the local namespace where to expand variables.
2814 be walked to look for the local namespace where to expand variables.
2815
2815
2816 The global namespace for expansion is always the user's interactive
2816 The global namespace for expansion is always the user's interactive
2817 namespace.
2817 namespace.
2818 """
2818 """
2819 ns = self.user_ns.copy()
2819 ns = self.user_ns.copy()
2820 ns.update(sys._getframe(depth+1).f_locals)
2820 ns.update(sys._getframe(depth+1).f_locals)
2821 try:
2821 try:
2822 # We have to use .vformat() here, because 'self' is a valid and common
2822 # We have to use .vformat() here, because 'self' is a valid and common
2823 # name, and expanding **ns for .format() would make it collide with
2823 # name, and expanding **ns for .format() would make it collide with
2824 # the 'self' argument of the method.
2824 # the 'self' argument of the method.
2825 cmd = formatter.vformat(cmd, args=[], kwargs=ns)
2825 cmd = formatter.vformat(cmd, args=[], kwargs=ns)
2826 except Exception:
2826 except Exception:
2827 # if formatter couldn't format, just let it go untransformed
2827 # if formatter couldn't format, just let it go untransformed
2828 pass
2828 pass
2829 return cmd
2829 return cmd
2830
2830
2831 def mktempfile(self, data=None, prefix='ipython_edit_'):
2831 def mktempfile(self, data=None, prefix='ipython_edit_'):
2832 """Make a new tempfile and return its filename.
2832 """Make a new tempfile and return its filename.
2833
2833
2834 This makes a call to tempfile.mktemp, but it registers the created
2834 This makes a call to tempfile.mktemp, but it registers the created
2835 filename internally so ipython cleans it up at exit time.
2835 filename internally so ipython cleans it up at exit time.
2836
2836
2837 Optional inputs:
2837 Optional inputs:
2838
2838
2839 - data(None): if data is given, it gets written out to the temp file
2839 - data(None): if data is given, it gets written out to the temp file
2840 immediately, and the file is closed again."""
2840 immediately, and the file is closed again."""
2841
2841
2842 filename = tempfile.mktemp('.py', prefix)
2842 filename = tempfile.mktemp('.py', prefix)
2843 self.tempfiles.append(filename)
2843 self.tempfiles.append(filename)
2844
2844
2845 if data:
2845 if data:
2846 tmp_file = open(filename,'w')
2846 tmp_file = open(filename,'w')
2847 tmp_file.write(data)
2847 tmp_file.write(data)
2848 tmp_file.close()
2848 tmp_file.close()
2849 return filename
2849 return filename
2850
2850
2851 # TODO: This should be removed when Term is refactored.
2851 # TODO: This should be removed when Term is refactored.
2852 def write(self,data):
2852 def write(self,data):
2853 """Write a string to the default output"""
2853 """Write a string to the default output"""
2854 io.stdout.write(data)
2854 io.stdout.write(data)
2855
2855
2856 # TODO: This should be removed when Term is refactored.
2856 # TODO: This should be removed when Term is refactored.
2857 def write_err(self,data):
2857 def write_err(self,data):
2858 """Write a string to the default error output"""
2858 """Write a string to the default error output"""
2859 io.stderr.write(data)
2859 io.stderr.write(data)
2860
2860
2861 def ask_yes_no(self, prompt, default=None):
2861 def ask_yes_no(self, prompt, default=None):
2862 if self.quiet:
2862 if self.quiet:
2863 return True
2863 return True
2864 return ask_yes_no(prompt,default)
2864 return ask_yes_no(prompt,default)
2865
2865
2866 def show_usage(self):
2866 def show_usage(self):
2867 """Show a usage message"""
2867 """Show a usage message"""
2868 page.page(IPython.core.usage.interactive_usage)
2868 page.page(IPython.core.usage.interactive_usage)
2869
2869
2870 def extract_input_lines(self, range_str, raw=False):
2870 def extract_input_lines(self, range_str, raw=False):
2871 """Return as a string a set of input history slices.
2871 """Return as a string a set of input history slices.
2872
2872
2873 Parameters
2873 Parameters
2874 ----------
2874 ----------
2875 range_str : string
2875 range_str : string
2876 The set of slices is given as a string, like "~5/6-~4/2 4:8 9",
2876 The set of slices is given as a string, like "~5/6-~4/2 4:8 9",
2877 since this function is for use by magic functions which get their
2877 since this function is for use by magic functions which get their
2878 arguments as strings. The number before the / is the session
2878 arguments as strings. The number before the / is the session
2879 number: ~n goes n back from the current session.
2879 number: ~n goes n back from the current session.
2880
2880
2881 Optional Parameters:
2881 Optional Parameters:
2882 - raw(False): by default, the processed input is used. If this is
2882 - raw(False): by default, the processed input is used. If this is
2883 true, the raw input history is used instead.
2883 true, the raw input history is used instead.
2884
2884
2885 Note that slices can be called with two notations:
2885 Note that slices can be called with two notations:
2886
2886
2887 N:M -> standard python form, means including items N...(M-1).
2887 N:M -> standard python form, means including items N...(M-1).
2888
2888
2889 N-M -> include items N..M (closed endpoint)."""
2889 N-M -> include items N..M (closed endpoint)."""
2890 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
2890 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
2891 return "\n".join(x for _, _, x in lines)
2891 return "\n".join(x for _, _, x in lines)
2892
2892
2893 def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True):
2893 def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True):
2894 """Get a code string from history, file, url, or a string or macro.
2894 """Get a code string from history, file, url, or a string or macro.
2895
2895
2896 This is mainly used by magic functions.
2896 This is mainly used by magic functions.
2897
2897
2898 Parameters
2898 Parameters
2899 ----------
2899 ----------
2900
2900
2901 target : str
2901 target : str
2902
2902
2903 A string specifying code to retrieve. This will be tried respectively
2903 A string specifying code to retrieve. This will be tried respectively
2904 as: ranges of input history (see %history for syntax), url,
2904 as: ranges of input history (see %history for syntax), url,
2905 correspnding .py file, filename, or an expression evaluating to a
2905 correspnding .py file, filename, or an expression evaluating to a
2906 string or Macro in the user namespace.
2906 string or Macro in the user namespace.
2907
2907
2908 raw : bool
2908 raw : bool
2909 If true (default), retrieve raw history. Has no effect on the other
2909 If true (default), retrieve raw history. Has no effect on the other
2910 retrieval mechanisms.
2910 retrieval mechanisms.
2911
2911
2912 py_only : bool (default False)
2912 py_only : bool (default False)
2913 Only try to fetch python code, do not try alternative methods to decode file
2913 Only try to fetch python code, do not try alternative methods to decode file
2914 if unicode fails.
2914 if unicode fails.
2915
2915
2916 Returns
2916 Returns
2917 -------
2917 -------
2918 A string of code.
2918 A string of code.
2919
2919
2920 ValueError is raised if nothing is found, and TypeError if it evaluates
2920 ValueError is raised if nothing is found, and TypeError if it evaluates
2921 to an object of another type. In each case, .args[0] is a printable
2921 to an object of another type. In each case, .args[0] is a printable
2922 message.
2922 message.
2923 """
2923 """
2924 code = self.extract_input_lines(target, raw=raw) # Grab history
2924 code = self.extract_input_lines(target, raw=raw) # Grab history
2925 if code:
2925 if code:
2926 return code
2926 return code
2927 utarget = unquote_filename(target)
2927 utarget = unquote_filename(target)
2928 try:
2928 try:
2929 if utarget.startswith(('http://', 'https://')):
2929 if utarget.startswith(('http://', 'https://')):
2930 return openpy.read_py_url(utarget, skip_encoding_cookie=skip_encoding_cookie)
2930 return openpy.read_py_url(utarget, skip_encoding_cookie=skip_encoding_cookie)
2931 except UnicodeDecodeError:
2931 except UnicodeDecodeError:
2932 if not py_only :
2932 if not py_only :
2933 response = urllib.urlopen(target)
2933 response = urllib.urlopen(target)
2934 return response.read().decode('latin1')
2934 return response.read().decode('latin1')
2935 raise ValueError(("'%s' seem to be unreadable.") % utarget)
2935 raise ValueError(("'%s' seem to be unreadable.") % utarget)
2936
2936
2937 potential_target = [target]
2937 potential_target = [target]
2938 try :
2938 try :
2939 potential_target.insert(0,get_py_filename(target))
2939 potential_target.insert(0,get_py_filename(target))
2940 except IOError:
2940 except IOError:
2941 pass
2941 pass
2942
2942
2943 for tgt in potential_target :
2943 for tgt in potential_target :
2944 if os.path.isfile(tgt): # Read file
2944 if os.path.isfile(tgt): # Read file
2945 try :
2945 try :
2946 return openpy.read_py_file(tgt, skip_encoding_cookie=skip_encoding_cookie)
2946 return openpy.read_py_file(tgt, skip_encoding_cookie=skip_encoding_cookie)
2947 except UnicodeDecodeError :
2947 except UnicodeDecodeError :
2948 if not py_only :
2948 if not py_only :
2949 with io_open(tgt,'r', encoding='latin1') as f :
2949 with io_open(tgt,'r', encoding='latin1') as f :
2950 return f.read()
2950 return f.read()
2951 raise ValueError(("'%s' seem to be unreadable.") % target)
2951 raise ValueError(("'%s' seem to be unreadable.") % target)
2952
2952
2953 try: # User namespace
2953 try: # User namespace
2954 codeobj = eval(target, self.user_ns)
2954 codeobj = eval(target, self.user_ns)
2955 except Exception:
2955 except Exception:
2956 raise ValueError(("'%s' was not found in history, as a file, url, "
2956 raise ValueError(("'%s' was not found in history, as a file, url, "
2957 "nor in the user namespace.") % target)
2957 "nor in the user namespace.") % target)
2958 if isinstance(codeobj, basestring):
2958 if isinstance(codeobj, basestring):
2959 return codeobj
2959 return codeobj
2960 elif isinstance(codeobj, Macro):
2960 elif isinstance(codeobj, Macro):
2961 return codeobj.value
2961 return codeobj.value
2962
2962
2963 raise TypeError("%s is neither a string nor a macro." % target,
2963 raise TypeError("%s is neither a string nor a macro." % target,
2964 codeobj)
2964 codeobj)
2965
2965
2966 #-------------------------------------------------------------------------
2966 #-------------------------------------------------------------------------
2967 # Things related to IPython exiting
2967 # Things related to IPython exiting
2968 #-------------------------------------------------------------------------
2968 #-------------------------------------------------------------------------
2969 def atexit_operations(self):
2969 def atexit_operations(self):
2970 """This will be executed at the time of exit.
2970 """This will be executed at the time of exit.
2971
2971
2972 Cleanup operations and saving of persistent data that is done
2972 Cleanup operations and saving of persistent data that is done
2973 unconditionally by IPython should be performed here.
2973 unconditionally by IPython should be performed here.
2974
2974
2975 For things that may depend on startup flags or platform specifics (such
2975 For things that may depend on startup flags or platform specifics (such
2976 as having readline or not), register a separate atexit function in the
2976 as having readline or not), register a separate atexit function in the
2977 code that has the appropriate information, rather than trying to
2977 code that has the appropriate information, rather than trying to
2978 clutter
2978 clutter
2979 """
2979 """
2980 # Close the history session (this stores the end time and line count)
2980 # Close the history session (this stores the end time and line count)
2981 # this must be *before* the tempfile cleanup, in case of temporary
2981 # this must be *before* the tempfile cleanup, in case of temporary
2982 # history db
2982 # history db
2983 self.history_manager.end_session()
2983 self.history_manager.end_session()
2984
2984
2985 # Cleanup all tempfiles left around
2985 # Cleanup all tempfiles left around
2986 for tfile in self.tempfiles:
2986 for tfile in self.tempfiles:
2987 try:
2987 try:
2988 os.unlink(tfile)
2988 os.unlink(tfile)
2989 except OSError:
2989 except OSError:
2990 pass
2990 pass
2991
2991
2992 # Clear all user namespaces to release all references cleanly.
2992 # Clear all user namespaces to release all references cleanly.
2993 self.reset(new_session=False)
2993 self.reset(new_session=False)
2994
2994
2995 # Run user hooks
2995 # Run user hooks
2996 self.hooks.shutdown_hook()
2996 self.hooks.shutdown_hook()
2997
2997
2998 def cleanup(self):
2998 def cleanup(self):
2999 self.restore_sys_module_state()
2999 self.restore_sys_module_state()
3000
3000
3001
3001
3002 class InteractiveShellABC(object):
3002 class InteractiveShellABC(object):
3003 """An abstract base class for InteractiveShell."""
3003 """An abstract base class for InteractiveShell."""
3004 __metaclass__ = abc.ABCMeta
3004 __metaclass__ = abc.ABCMeta
3005
3005
3006 InteractiveShellABC.register(InteractiveShell)
3006 InteractiveShellABC.register(InteractiveShell)
@@ -1,222 +1,241 b''
1 ''' A decorator-based method of constructing IPython magics with `argparse`
1 ''' A decorator-based method of constructing IPython magics with `argparse`
2 option handling.
2 option handling.
3
3
4 New magic functions can be defined like so::
4 New magic functions can be defined like so::
5
5
6 from IPython.core.magic_arguments import (argument, magic_arguments,
6 from IPython.core.magic_arguments import (argument, magic_arguments,
7 parse_argstring)
7 parse_argstring)
8
8
9 @magic_arguments()
9 @magic_arguments()
10 @argument('-o', '--option', help='An optional argument.')
10 @argument('-o', '--option', help='An optional argument.')
11 @argument('arg', type=int, help='An integer positional argument.')
11 @argument('arg', type=int, help='An integer positional argument.')
12 def magic_cool(self, arg):
12 def magic_cool(self, arg):
13 """ A really cool magic command.
13 """ A really cool magic command.
14
14
15 """
15 """
16 args = parse_argstring(magic_cool, arg)
16 args = parse_argstring(magic_cool, arg)
17 ...
17 ...
18
18
19 The `@magic_arguments` decorator marks the function as having argparse arguments.
19 The `@magic_arguments` decorator marks the function as having argparse arguments.
20 The `@argument` decorator adds an argument using the same syntax as argparse's
20 The `@argument` decorator adds an argument using the same syntax as argparse's
21 `add_argument()` method. More sophisticated uses may also require the
21 `add_argument()` method. More sophisticated uses may also require the
22 `@argument_group` or `@kwds` decorator to customize the formatting and the
22 `@argument_group` or `@kwds` decorator to customize the formatting and the
23 parsing.
23 parsing.
24
24
25 Help text for the magic is automatically generated from the docstring and the
25 Help text for the magic is automatically generated from the docstring and the
26 arguments::
26 arguments::
27
27
28 In[1]: %cool?
28 In[1]: %cool?
29 %cool [-o OPTION] arg
29 %cool [-o OPTION] arg
30
30
31 A really cool magic command.
31 A really cool magic command.
32
32
33 positional arguments:
33 positional arguments:
34 arg An integer positional argument.
34 arg An integer positional argument.
35
35
36 optional arguments:
36 optional arguments:
37 -o OPTION, --option OPTION
37 -o OPTION, --option OPTION
38 An optional argument.
38 An optional argument.
39
39
40 '''
40 '''
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Copyright (C) 2010-2011, IPython Development Team.
42 # Copyright (C) 2010-2011, IPython Development Team.
43 #
43 #
44 # Distributed under the terms of the Modified BSD License.
44 # Distributed under the terms of the Modified BSD License.
45 #
45 #
46 # The full license is in the file COPYING.txt, distributed with this software.
46 # The full license is in the file COPYING.txt, distributed with this software.
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 # Our own imports
49 # Our own imports
50 from IPython.external import argparse
50 from IPython.external import argparse
51 from IPython.core.error import UsageError
51 from IPython.core.error import UsageError
52 from IPython.utils.process import arg_split
52 from IPython.utils.process import arg_split
53 from IPython.utils.text import dedent
53 from IPython.utils.text import dedent
54
54
55 class MagicHelpFormatter(argparse.RawDescriptionHelpFormatter):
55 class MagicHelpFormatter(argparse.RawDescriptionHelpFormatter):
56 """ A HelpFormatter which dedents but otherwise preserves indentation.
56 """ A HelpFormatter which dedents but otherwise preserves indentation.
57 """
57 """
58 def _fill_text(self, text, width, indent):
58 def _fill_text(self, text, width, indent):
59 return argparse.RawDescriptionHelpFormatter._fill_text(self, dedent(text), width, indent)
59 return argparse.RawDescriptionHelpFormatter._fill_text(self, dedent(text), width, indent)
60
60
61 class MagicArgumentParser(argparse.ArgumentParser):
61 class MagicArgumentParser(argparse.ArgumentParser):
62 """ An ArgumentParser tweaked for use by IPython magics.
62 """ An ArgumentParser tweaked for use by IPython magics.
63 """
63 """
64 def __init__(self,
64 def __init__(self,
65 prog=None,
65 prog=None,
66 usage=None,
66 usage=None,
67 description=None,
67 description=None,
68 epilog=None,
68 epilog=None,
69 parents=None,
69 parents=None,
70 formatter_class=MagicHelpFormatter,
70 formatter_class=MagicHelpFormatter,
71 prefix_chars='-',
71 prefix_chars='-',
72 argument_default=None,
72 argument_default=None,
73 conflict_handler='error',
73 conflict_handler='error',
74 add_help=False):
74 add_help=False):
75 if parents is None:
75 if parents is None:
76 parents = []
76 parents = []
77 super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
77 super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
78 description=description, epilog=epilog,
78 description=description, epilog=epilog,
79 parents=parents, formatter_class=formatter_class,
79 parents=parents, formatter_class=formatter_class,
80 prefix_chars=prefix_chars, argument_default=argument_default,
80 prefix_chars=prefix_chars, argument_default=argument_default,
81 conflict_handler=conflict_handler, add_help=add_help)
81 conflict_handler=conflict_handler, add_help=add_help)
82
82
83 def error(self, message):
83 def error(self, message):
84 """ Raise a catchable error instead of exiting.
84 """ Raise a catchable error instead of exiting.
85 """
85 """
86 raise UsageError(message)
86 raise UsageError(message)
87
87
88 def parse_argstring(self, argstring):
88 def parse_argstring(self, argstring):
89 """ Split a string into an argument list and parse that argument list.
89 """ Split a string into an argument list and parse that argument list.
90 """
90 """
91 argv = arg_split(argstring)
91 argv = arg_split(argstring)
92 return self.parse_args(argv)
92 return self.parse_args(argv)
93
93
94
94
95 def construct_parser(magic_func):
95 def construct_parser(magic_func):
96 """ Construct an argument parser using the function decorations.
96 """ Construct an argument parser using the function decorations.
97 """
97 """
98 kwds = getattr(magic_func, 'argcmd_kwds', {})
98 kwds = getattr(magic_func, 'argcmd_kwds', {})
99 if 'description' not in kwds:
99 if 'description' not in kwds:
100 kwds['description'] = getattr(magic_func, '__doc__', None)
100 kwds['description'] = getattr(magic_func, '__doc__', None)
101 arg_name = real_name(magic_func)
101 arg_name = real_name(magic_func)
102 parser = MagicArgumentParser(arg_name, **kwds)
102 parser = MagicArgumentParser(arg_name, **kwds)
103 # Reverse the list of decorators in order to apply them in the
103 # Reverse the list of decorators in order to apply them in the
104 # order in which they appear in the source.
104 # order in which they appear in the source.
105 group = None
105 group = None
106 for deco in magic_func.decorators[::-1]:
106 for deco in magic_func.decorators[::-1]:
107 result = deco.add_to_parser(parser, group)
107 result = deco.add_to_parser(parser, group)
108 if result is not None:
108 if result is not None:
109 group = result
109 group = result
110
110
111 # Replace the starting 'usage: ' with IPython's %.
111 # Replace the starting 'usage: ' with IPython's %.
112 help_text = parser.format_help()
112 help_text = parser.format_help()
113 if help_text.startswith('usage: '):
113 if help_text.startswith('usage: '):
114 help_text = help_text.replace('usage: ', '%', 1)
114 help_text = help_text.replace('usage: ', '%', 1)
115 else:
115 else:
116 help_text = '%' + help_text
116 help_text = '%' + help_text
117
117
118 # Replace the magic function's docstring with the full help text.
118 # Replace the magic function's docstring with the full help text.
119 magic_func.__doc__ = help_text
119 magic_func.__doc__ = help_text
120
120
121 return parser
121 return parser
122
122
123
123
124 def parse_argstring(magic_func, argstring):
124 def parse_argstring(magic_func, argstring):
125 """ Parse the string of arguments for the given magic function.
125 """ Parse the string of arguments for the given magic function.
126 """
126 """
127 return magic_func.parser.parse_argstring(argstring)
127 return magic_func.parser.parse_argstring(argstring)
128
128
129
129
130 def real_name(magic_func):
130 def real_name(magic_func):
131 """ Find the real name of the magic.
131 """ Find the real name of the magic.
132 """
132 """
133 magic_name = magic_func.__name__
133 magic_name = magic_func.__name__
134 if magic_name.startswith('magic_'):
134 if magic_name.startswith('magic_'):
135 magic_name = magic_name[len('magic_'):]
135 magic_name = magic_name[len('magic_'):]
136 return getattr(magic_func, 'argcmd_name', magic_name)
136 return getattr(magic_func, 'argcmd_name', magic_name)
137
137
138
138
139 class ArgDecorator(object):
139 class ArgDecorator(object):
140 """ Base class for decorators to add ArgumentParser information to a method.
140 """ Base class for decorators to add ArgumentParser information to a method.
141 """
141 """
142
142
143 def __call__(self, func):
143 def __call__(self, func):
144 if not getattr(func, 'has_arguments', False):
144 if not getattr(func, 'has_arguments', False):
145 func.has_arguments = True
145 func.has_arguments = True
146 func.decorators = []
146 func.decorators = []
147 func.decorators.append(self)
147 func.decorators.append(self)
148 return func
148 return func
149
149
150 def add_to_parser(self, parser, group):
150 def add_to_parser(self, parser, group):
151 """ Add this object's information to the parser, if necessary.
151 """ Add this object's information to the parser, if necessary.
152 """
152 """
153 pass
153 pass
154
154
155
155
156 class magic_arguments(ArgDecorator):
156 class magic_arguments(ArgDecorator):
157 """ Mark the magic as having argparse arguments and possibly adjust the
157 """ Mark the magic as having argparse arguments and possibly adjust the
158 name.
158 name.
159 """
159 """
160
160
161 def __init__(self, name=None):
161 def __init__(self, name=None):
162 self.name = name
162 self.name = name
163
163
164 def __call__(self, func):
164 def __call__(self, func):
165 if not getattr(func, 'has_arguments', False):
165 if not getattr(func, 'has_arguments', False):
166 func.has_arguments = True
166 func.has_arguments = True
167 func.decorators = []
167 func.decorators = []
168 if self.name is not None:
168 if self.name is not None:
169 func.argcmd_name = self.name
169 func.argcmd_name = self.name
170 # This should be the first decorator in the list of decorators, thus the
170 # This should be the first decorator in the list of decorators, thus the
171 # last to execute. Build the parser.
171 # last to execute. Build the parser.
172 func.parser = construct_parser(func)
172 func.parser = construct_parser(func)
173 return func
173 return func
174
174
175
175
176 class argument(ArgDecorator):
176 class ArgMethodWrapper(ArgDecorator):
177 """ Store arguments and keywords to pass to add_argument().
177
178 """
179 Base class to define a wrapper for ArgumentParser method.
180
181 Child class must define either `_method_name` or `add_to_parser`.
178
182
179 Instances also serve to decorate command methods.
180 """
183 """
184
185 _method_name = None
186
181 def __init__(self, *args, **kwds):
187 def __init__(self, *args, **kwds):
182 self.args = args
188 self.args = args
183 self.kwds = kwds
189 self.kwds = kwds
184
190
185 def add_to_parser(self, parser, group):
191 def add_to_parser(self, parser, group):
186 """ Add this object's information to the parser.
192 """ Add this object's information to the parser.
187 """
193 """
188 if group is not None:
194 if group is not None:
189 parser = group
195 parser = group
190 parser.add_argument(*self.args, **self.kwds)
196 getattr(parser, self._method_name)(*self.args, **self.kwds)
191 return None
197 return None
192
198
193
199
194 class argument_group(ArgDecorator):
200 class argument(ArgMethodWrapper):
201 """ Store arguments and keywords to pass to add_argument().
202
203 Instances also serve to decorate command methods.
204 """
205 _method_name = 'add_argument'
206
207
208 class defaults(ArgMethodWrapper):
209 """ Store arguments and keywords to pass to set_defaults().
210
211 Instances also serve to decorate command methods.
212 """
213 _method_name = 'set_defaults'
214
215
216 class argument_group(ArgMethodWrapper):
195 """ Store arguments and keywords to pass to add_argument_group().
217 """ Store arguments and keywords to pass to add_argument_group().
196
218
197 Instances also serve to decorate command methods.
219 Instances also serve to decorate command methods.
198 """
220 """
199 def __init__(self, *args, **kwds):
200 self.args = args
201 self.kwds = kwds
202
221
203 def add_to_parser(self, parser, group):
222 def add_to_parser(self, parser, group):
204 """ Add this object's information to the parser.
223 """ Add this object's information to the parser.
205 """
224 """
206 return parser.add_argument_group(*self.args, **self.kwds)
225 return parser.add_argument_group(*self.args, **self.kwds)
207
226
208
227
209 class kwds(ArgDecorator):
228 class kwds(ArgDecorator):
210 """ Provide other keywords to the sub-parser constructor.
229 """ Provide other keywords to the sub-parser constructor.
211 """
230 """
212 def __init__(self, **kwds):
231 def __init__(self, **kwds):
213 self.kwds = kwds
232 self.kwds = kwds
214
233
215 def __call__(self, func):
234 def __call__(self, func):
216 func = super(kwds, self).__call__(func)
235 func = super(kwds, self).__call__(func)
217 func.argcmd_kwds = self.kwds
236 func.argcmd_kwds = self.kwds
218 return func
237 return func
219
238
220
239
221 __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
240 __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
222 'parse_argstring']
241 'parse_argstring']
@@ -1,612 +1,613 b''
1 """Implementation of basic magic functions.
1 """Implementation of basic magic functions.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012 The IPython Development Team.
4 # Copyright (c) 2012 The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 from __future__ import print_function
14 from __future__ import print_function
15
15
16 # Stdlib
16 # Stdlib
17 import io
17 import io
18 import sys
18 import sys
19 from pprint import pformat
19 from pprint import pformat
20
20
21 # Our own packages
21 # Our own packages
22 from IPython.core import magic_arguments
22 from IPython.core import magic_arguments
23 from IPython.core.error import UsageError
23 from IPython.core.error import UsageError
24 from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
24 from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
25 from IPython.utils.text import format_screen, dedent, indent
25 from IPython.utils.text import format_screen, dedent, indent
26 from IPython.core import magic_arguments, page
26 from IPython.core import magic_arguments, page
27 from IPython.testing.skipdoctest import skip_doctest
27 from IPython.testing.skipdoctest import skip_doctest
28 from IPython.utils.ipstruct import Struct
28 from IPython.utils.ipstruct import Struct
29 from IPython.utils.path import unquote_filename
29 from IPython.utils.path import unquote_filename
30 from IPython.utils.warn import warn, error
30 from IPython.utils.warn import warn, error
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Magics class implementation
33 # Magics class implementation
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 @magics_class
36 @magics_class
37 class BasicMagics(Magics):
37 class BasicMagics(Magics):
38 """Magics that provide central IPython functionality.
38 """Magics that provide central IPython functionality.
39
39
40 These are various magics that don't fit into specific categories but that
40 These are various magics that don't fit into specific categories but that
41 are all part of the base 'IPython experience'."""
41 are all part of the base 'IPython experience'."""
42
42
43 @magic_arguments.magic_arguments()
43 @magic_arguments.magic_arguments()
44 @magic_arguments.argument(
44 @magic_arguments.argument(
45 '-l', '--line', action='store_true',
45 '-l', '--line', action='store_true',
46 help="""Create a line magic alias."""
46 help="""Create a line magic alias."""
47 )
47 )
48 @magic_arguments.argument(
48 @magic_arguments.argument(
49 '-c', '--cell', action='store_true',
49 '-c', '--cell', action='store_true',
50 help="""Create a cell magic alias."""
50 help="""Create a cell magic alias."""
51 )
51 )
52 @magic_arguments.argument(
52 @magic_arguments.argument(
53 'name',
53 'name',
54 help="""Name of the magic to be created."""
54 help="""Name of the magic to be created."""
55 )
55 )
56 @magic_arguments.argument(
56 @magic_arguments.argument(
57 'target',
57 'target',
58 help="""Name of the existing line or cell magic."""
58 help="""Name of the existing line or cell magic."""
59 )
59 )
60 @line_magic
60 @line_magic
61 def alias_magic(self, line=''):
61 def alias_magic(self, line=''):
62 """Create an alias for an existing line or cell magic.
62 """Create an alias for an existing line or cell magic.
63
63
64 Examples
64 Examples
65 --------
65 --------
66 ::
66 ::
67 In [1]: %alias_magic t timeit
67 In [1]: %alias_magic t timeit
68 Created `%t` as an alias for `%timeit`.
68 Created `%t` as an alias for `%timeit`.
69 Created `%%t` as an alias for `%%timeit`.
69 Created `%%t` as an alias for `%%timeit`.
70
70
71 In [2]: %t -n1 pass
71 In [2]: %t -n1 pass
72 1 loops, best of 3: 954 ns per loop
72 1 loops, best of 3: 954 ns per loop
73
73
74 In [3]: %%t -n1
74 In [3]: %%t -n1
75 ...: pass
75 ...: pass
76 ...:
76 ...:
77 1 loops, best of 3: 954 ns per loop
77 1 loops, best of 3: 954 ns per loop
78
78
79 In [4]: %alias_magic --cell whereami pwd
79 In [4]: %alias_magic --cell whereami pwd
80 UsageError: Cell magic function `%%pwd` not found.
80 UsageError: Cell magic function `%%pwd` not found.
81 In [5]: %alias_magic --line whereami pwd
81 In [5]: %alias_magic --line whereami pwd
82 Created `%whereami` as an alias for `%pwd`.
82 Created `%whereami` as an alias for `%pwd`.
83
83
84 In [6]: %whereami
84 In [6]: %whereami
85 Out[6]: u'/home/testuser'
85 Out[6]: u'/home/testuser'
86 """
86 """
87 args = magic_arguments.parse_argstring(self.alias_magic, line)
87 args = magic_arguments.parse_argstring(self.alias_magic, line)
88 shell = self.shell
88 shell = self.shell
89 mman = self.shell.magics_manager
89 mman = self.shell.magics_manager
90 escs = ''.join(magic_escapes.values())
90 escs = ''.join(magic_escapes.values())
91
91
92 target = args.target.lstrip(escs)
92 target = args.target.lstrip(escs)
93 name = args.name.lstrip(escs)
93 name = args.name.lstrip(escs)
94
94
95 # Find the requested magics.
95 # Find the requested magics.
96 m_line = shell.find_magic(target, 'line')
96 m_line = shell.find_magic(target, 'line')
97 m_cell = shell.find_magic(target, 'cell')
97 m_cell = shell.find_magic(target, 'cell')
98 if args.line and m_line is None:
98 if args.line and m_line is None:
99 raise UsageError('Line magic function `%s%s` not found.' %
99 raise UsageError('Line magic function `%s%s` not found.' %
100 (magic_escapes['line'], target))
100 (magic_escapes['line'], target))
101 if args.cell and m_cell is None:
101 if args.cell and m_cell is None:
102 raise UsageError('Cell magic function `%s%s` not found.' %
102 raise UsageError('Cell magic function `%s%s` not found.' %
103 (magic_escapes['cell'], target))
103 (magic_escapes['cell'], target))
104
104
105 # If --line and --cell are not specified, default to the ones
105 # If --line and --cell are not specified, default to the ones
106 # that are available.
106 # that are available.
107 if not args.line and not args.cell:
107 if not args.line and not args.cell:
108 if not m_line and not m_cell:
108 if not m_line and not m_cell:
109 raise UsageError(
109 raise UsageError(
110 'No line or cell magic with name `%s` found.' % target
110 'No line or cell magic with name `%s` found.' % target
111 )
111 )
112 args.line = bool(m_line)
112 args.line = bool(m_line)
113 args.cell = bool(m_cell)
113 args.cell = bool(m_cell)
114
114
115 if args.line:
115 if args.line:
116 mman.register_alias(name, target, 'line')
116 mman.register_alias(name, target, 'line')
117 print('Created `%s%s` as an alias for `%s%s`.' % (
117 print('Created `%s%s` as an alias for `%s%s`.' % (
118 magic_escapes['line'], name,
118 magic_escapes['line'], name,
119 magic_escapes['line'], target))
119 magic_escapes['line'], target))
120
120
121 if args.cell:
121 if args.cell:
122 mman.register_alias(name, target, 'cell')
122 mman.register_alias(name, target, 'cell')
123 print('Created `%s%s` as an alias for `%s%s`.' % (
123 print('Created `%s%s` as an alias for `%s%s`.' % (
124 magic_escapes['cell'], name,
124 magic_escapes['cell'], name,
125 magic_escapes['cell'], target))
125 magic_escapes['cell'], target))
126
126
127 def _lsmagic(self):
127 def _lsmagic(self):
128 mesc = magic_escapes['line']
128 mesc = magic_escapes['line']
129 cesc = magic_escapes['cell']
129 cesc = magic_escapes['cell']
130 mman = self.shell.magics_manager
130 mman = self.shell.magics_manager
131 magics = mman.lsmagic()
131 magics = mman.lsmagic()
132 out = ['Available line magics:',
132 out = ['Available line magics:',
133 mesc + (' '+mesc).join(sorted(magics['line'])),
133 mesc + (' '+mesc).join(sorted(magics['line'])),
134 '',
134 '',
135 'Available cell magics:',
135 'Available cell magics:',
136 cesc + (' '+cesc).join(sorted(magics['cell'])),
136 cesc + (' '+cesc).join(sorted(magics['cell'])),
137 '',
137 '',
138 mman.auto_status()]
138 mman.auto_status()]
139 return '\n'.join(out)
139 return '\n'.join(out)
140
140
141 @line_magic
141 @line_magic
142 def lsmagic(self, parameter_s=''):
142 def lsmagic(self, parameter_s=''):
143 """List currently available magic functions."""
143 """List currently available magic functions."""
144 print(self._lsmagic())
144 print(self._lsmagic())
145
145
146 def _magic_docs(self, brief=False, rest=False):
146 def _magic_docs(self, brief=False, rest=False):
147 """Return docstrings from magic functions."""
147 """Return docstrings from magic functions."""
148 mman = self.shell.magics_manager
148 mman = self.shell.magics_manager
149 docs = mman.lsmagic_docs(brief, missing='No documentation')
149 docs = mman.lsmagic_docs(brief, missing='No documentation')
150
150
151 if rest:
151 if rest:
152 format_string = '**%s%s**::\n\n%s\n\n'
152 format_string = '**%s%s**::\n\n%s\n\n'
153 else:
153 else:
154 format_string = '%s%s:\n%s\n'
154 format_string = '%s%s:\n%s\n'
155
155
156 return ''.join(
156 return ''.join(
157 [format_string % (magic_escapes['line'], fname,
157 [format_string % (magic_escapes['line'], fname,
158 indent(dedent(fndoc)))
158 indent(dedent(fndoc)))
159 for fname, fndoc in sorted(docs['line'].items())]
159 for fname, fndoc in sorted(docs['line'].items())]
160 +
160 +
161 [format_string % (magic_escapes['cell'], fname,
161 [format_string % (magic_escapes['cell'], fname,
162 indent(dedent(fndoc)))
162 indent(dedent(fndoc)))
163 for fname, fndoc in sorted(docs['cell'].items())]
163 for fname, fndoc in sorted(docs['cell'].items())]
164 )
164 )
165
165
166 @line_magic
166 @line_magic
167 def magic(self, parameter_s=''):
167 def magic(self, parameter_s=''):
168 """Print information about the magic function system.
168 """Print information about the magic function system.
169
169
170 Supported formats: -latex, -brief, -rest
170 Supported formats: -latex, -brief, -rest
171 """
171 """
172
172
173 mode = ''
173 mode = ''
174 try:
174 try:
175 mode = parameter_s.split()[0][1:]
175 mode = parameter_s.split()[0][1:]
176 if mode == 'rest':
176 if mode == 'rest':
177 rest_docs = []
177 rest_docs = []
178 except IndexError:
178 except IndexError:
179 pass
179 pass
180
180
181 brief = (mode == 'brief')
181 brief = (mode == 'brief')
182 rest = (mode == 'rest')
182 rest = (mode == 'rest')
183 magic_docs = self._magic_docs(brief, rest)
183 magic_docs = self._magic_docs(brief, rest)
184
184
185 if mode == 'latex':
185 if mode == 'latex':
186 print(self.format_latex(magic_docs))
186 print(self.format_latex(magic_docs))
187 return
187 return
188 else:
188 else:
189 magic_docs = format_screen(magic_docs)
189 magic_docs = format_screen(magic_docs)
190
190
191 out = ["""
191 out = ["""
192 IPython's 'magic' functions
192 IPython's 'magic' functions
193 ===========================
193 ===========================
194
194
195 The magic function system provides a series of functions which allow you to
195 The magic function system provides a series of functions which allow you to
196 control the behavior of IPython itself, plus a lot of system-type
196 control the behavior of IPython itself, plus a lot of system-type
197 features. There are two kinds of magics, line-oriented and cell-oriented.
197 features. There are two kinds of magics, line-oriented and cell-oriented.
198
198
199 Line magics are prefixed with the % character and work much like OS
199 Line magics are prefixed with the % character and work much like OS
200 command-line calls: they get as an argument the rest of the line, where
200 command-line calls: they get as an argument the rest of the line, where
201 arguments are passed without parentheses or quotes. For example, this will
201 arguments are passed without parentheses or quotes. For example, this will
202 time the given statement::
202 time the given statement::
203
203
204 %timeit range(1000)
204 %timeit range(1000)
205
205
206 Cell magics are prefixed with a double %%, and they are functions that get as
206 Cell magics are prefixed with a double %%, and they are functions that get as
207 an argument not only the rest of the line, but also the lines below it in a
207 an argument not only the rest of the line, but also the lines below it in a
208 separate argument. These magics are called with two arguments: the rest of the
208 separate argument. These magics are called with two arguments: the rest of the
209 call line and the body of the cell, consisting of the lines below the first.
209 call line and the body of the cell, consisting of the lines below the first.
210 For example::
210 For example::
211
211
212 %%timeit x = numpy.random.randn((100, 100))
212 %%timeit x = numpy.random.randn((100, 100))
213 numpy.linalg.svd(x)
213 numpy.linalg.svd(x)
214
214
215 will time the execution of the numpy svd routine, running the assignment of x
215 will time the execution of the numpy svd routine, running the assignment of x
216 as part of the setup phase, which is not timed.
216 as part of the setup phase, which is not timed.
217
217
218 In a line-oriented client (the terminal or Qt console IPython), starting a new
218 In a line-oriented client (the terminal or Qt console IPython), starting a new
219 input with %% will automatically enter cell mode, and IPython will continue
219 input with %% will automatically enter cell mode, and IPython will continue
220 reading input until a blank line is given. In the notebook, simply type the
220 reading input until a blank line is given. In the notebook, simply type the
221 whole cell as one entity, but keep in mind that the %% escape can only be at
221 whole cell as one entity, but keep in mind that the %% escape can only be at
222 the very start of the cell.
222 the very start of the cell.
223
223
224 NOTE: If you have 'automagic' enabled (via the command line option or with the
224 NOTE: If you have 'automagic' enabled (via the command line option or with the
225 %automagic function), you don't need to type in the % explicitly for line
225 %automagic function), you don't need to type in the % explicitly for line
226 magics; cell magics always require an explicit '%%' escape. By default,
226 magics; cell magics always require an explicit '%%' escape. By default,
227 IPython ships with automagic on, so you should only rarely need the % escape.
227 IPython ships with automagic on, so you should only rarely need the % escape.
228
228
229 Example: typing '%cd mydir' (without the quotes) changes you working directory
229 Example: typing '%cd mydir' (without the quotes) changes you working directory
230 to 'mydir', if it exists.
230 to 'mydir', if it exists.
231
231
232 For a list of the available magic functions, use %lsmagic. For a description
232 For a list of the available magic functions, use %lsmagic. For a description
233 of any of them, type %magic_name?, e.g. '%cd?'.
233 of any of them, type %magic_name?, e.g. '%cd?'.
234
234
235 Currently the magic system has the following functions:""",
235 Currently the magic system has the following functions:""",
236 magic_docs,
236 magic_docs,
237 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
237 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
238 self._lsmagic(),
238 self._lsmagic(),
239 ]
239 ]
240 page.page('\n'.join(out))
240 page.page('\n'.join(out))
241
241
242
242
243 @line_magic
243 @line_magic
244 def page(self, parameter_s=''):
244 def page(self, parameter_s=''):
245 """Pretty print the object and display it through a pager.
245 """Pretty print the object and display it through a pager.
246
246
247 %page [options] OBJECT
247 %page [options] OBJECT
248
248
249 If no object is given, use _ (last output).
249 If no object is given, use _ (last output).
250
250
251 Options:
251 Options:
252
252
253 -r: page str(object), don't pretty-print it."""
253 -r: page str(object), don't pretty-print it."""
254
254
255 # After a function contributed by Olivier Aubert, slightly modified.
255 # After a function contributed by Olivier Aubert, slightly modified.
256
256
257 # Process options/args
257 # Process options/args
258 opts, args = self.parse_options(parameter_s, 'r')
258 opts, args = self.parse_options(parameter_s, 'r')
259 raw = 'r' in opts
259 raw = 'r' in opts
260
260
261 oname = args and args or '_'
261 oname = args and args or '_'
262 info = self.shell._ofind(oname)
262 info = self.shell._ofind(oname)
263 if info['found']:
263 if info['found']:
264 txt = (raw and str or pformat)( info['obj'] )
264 txt = (raw and str or pformat)( info['obj'] )
265 page.page(txt)
265 page.page(txt)
266 else:
266 else:
267 print('Object `%s` not found' % oname)
267 print('Object `%s` not found' % oname)
268
268
269 @line_magic
269 @line_magic
270 def profile(self, parameter_s=''):
270 def profile(self, parameter_s=''):
271 """Print your currently active IPython profile."""
271 """Print your currently active IPython profile."""
272 from IPython.core.application import BaseIPythonApplication
272 from IPython.core.application import BaseIPythonApplication
273 if BaseIPythonApplication.initialized():
273 if BaseIPythonApplication.initialized():
274 print(BaseIPythonApplication.instance().profile)
274 print(BaseIPythonApplication.instance().profile)
275 else:
275 else:
276 error("profile is an application-level value, but you don't appear to be in an IPython application")
276 error("profile is an application-level value, but you don't appear to be in an IPython application")
277
277
278 @line_magic
278 @line_magic
279 def pprint(self, parameter_s=''):
279 def pprint(self, parameter_s=''):
280 """Toggle pretty printing on/off."""
280 """Toggle pretty printing on/off."""
281 ptformatter = self.shell.display_formatter.formatters['text/plain']
281 ptformatter = self.shell.display_formatter.formatters['text/plain']
282 ptformatter.pprint = bool(1 - ptformatter.pprint)
282 ptformatter.pprint = bool(1 - ptformatter.pprint)
283 print('Pretty printing has been turned',
283 print('Pretty printing has been turned',
284 ['OFF','ON'][ptformatter.pprint])
284 ['OFF','ON'][ptformatter.pprint])
285
285
286 @line_magic
286 @line_magic
287 def colors(self, parameter_s=''):
287 def colors(self, parameter_s=''):
288 """Switch color scheme for prompts, info system and exception handlers.
288 """Switch color scheme for prompts, info system and exception handlers.
289
289
290 Currently implemented schemes: NoColor, Linux, LightBG.
290 Currently implemented schemes: NoColor, Linux, LightBG.
291
291
292 Color scheme names are not case-sensitive.
292 Color scheme names are not case-sensitive.
293
293
294 Examples
294 Examples
295 --------
295 --------
296 To get a plain black and white terminal::
296 To get a plain black and white terminal::
297
297
298 %colors nocolor
298 %colors nocolor
299 """
299 """
300 def color_switch_err(name):
300 def color_switch_err(name):
301 warn('Error changing %s color schemes.\n%s' %
301 warn('Error changing %s color schemes.\n%s' %
302 (name, sys.exc_info()[1]))
302 (name, sys.exc_info()[1]))
303
303
304
304
305 new_scheme = parameter_s.strip()
305 new_scheme = parameter_s.strip()
306 if not new_scheme:
306 if not new_scheme:
307 raise UsageError(
307 raise UsageError(
308 "%colors: you must specify a color scheme. See '%colors?'")
308 "%colors: you must specify a color scheme. See '%colors?'")
309 return
309 return
310 # local shortcut
310 # local shortcut
311 shell = self.shell
311 shell = self.shell
312
312
313 import IPython.utils.rlineimpl as readline
313 import IPython.utils.rlineimpl as readline
314
314
315 if not shell.colors_force and \
315 if not shell.colors_force and \
316 not readline.have_readline and sys.platform == "win32":
316 not readline.have_readline and \
317 (sys.platform == "win32" or sys.platform == "cli"):
317 msg = """\
318 msg = """\
318 Proper color support under MS Windows requires the pyreadline library.
319 Proper color support under MS Windows requires the pyreadline library.
319 You can find it at:
320 You can find it at:
320 http://ipython.org/pyreadline.html
321 http://ipython.org/pyreadline.html
321 Gary's readline needs the ctypes module, from:
322 Gary's readline needs the ctypes module, from:
322 http://starship.python.net/crew/theller/ctypes
323 http://starship.python.net/crew/theller/ctypes
323 (Note that ctypes is already part of Python versions 2.5 and newer).
324 (Note that ctypes is already part of Python versions 2.5 and newer).
324
325
325 Defaulting color scheme to 'NoColor'"""
326 Defaulting color scheme to 'NoColor'"""
326 new_scheme = 'NoColor'
327 new_scheme = 'NoColor'
327 warn(msg)
328 warn(msg)
328
329
329 # readline option is 0
330 # readline option is 0
330 if not shell.colors_force and not shell.has_readline:
331 if not shell.colors_force and not shell.has_readline:
331 new_scheme = 'NoColor'
332 new_scheme = 'NoColor'
332
333
333 # Set prompt colors
334 # Set prompt colors
334 try:
335 try:
335 shell.prompt_manager.color_scheme = new_scheme
336 shell.prompt_manager.color_scheme = new_scheme
336 except:
337 except:
337 color_switch_err('prompt')
338 color_switch_err('prompt')
338 else:
339 else:
339 shell.colors = \
340 shell.colors = \
340 shell.prompt_manager.color_scheme_table.active_scheme_name
341 shell.prompt_manager.color_scheme_table.active_scheme_name
341 # Set exception colors
342 # Set exception colors
342 try:
343 try:
343 shell.InteractiveTB.set_colors(scheme = new_scheme)
344 shell.InteractiveTB.set_colors(scheme = new_scheme)
344 shell.SyntaxTB.set_colors(scheme = new_scheme)
345 shell.SyntaxTB.set_colors(scheme = new_scheme)
345 except:
346 except:
346 color_switch_err('exception')
347 color_switch_err('exception')
347
348
348 # Set info (for 'object?') colors
349 # Set info (for 'object?') colors
349 if shell.color_info:
350 if shell.color_info:
350 try:
351 try:
351 shell.inspector.set_active_scheme(new_scheme)
352 shell.inspector.set_active_scheme(new_scheme)
352 except:
353 except:
353 color_switch_err('object inspector')
354 color_switch_err('object inspector')
354 else:
355 else:
355 shell.inspector.set_active_scheme('NoColor')
356 shell.inspector.set_active_scheme('NoColor')
356
357
357 @line_magic
358 @line_magic
358 def xmode(self, parameter_s=''):
359 def xmode(self, parameter_s=''):
359 """Switch modes for the exception handlers.
360 """Switch modes for the exception handlers.
360
361
361 Valid modes: Plain, Context and Verbose.
362 Valid modes: Plain, Context and Verbose.
362
363
363 If called without arguments, acts as a toggle."""
364 If called without arguments, acts as a toggle."""
364
365
365 def xmode_switch_err(name):
366 def xmode_switch_err(name):
366 warn('Error changing %s exception modes.\n%s' %
367 warn('Error changing %s exception modes.\n%s' %
367 (name,sys.exc_info()[1]))
368 (name,sys.exc_info()[1]))
368
369
369 shell = self.shell
370 shell = self.shell
370 new_mode = parameter_s.strip().capitalize()
371 new_mode = parameter_s.strip().capitalize()
371 try:
372 try:
372 shell.InteractiveTB.set_mode(mode=new_mode)
373 shell.InteractiveTB.set_mode(mode=new_mode)
373 print('Exception reporting mode:',shell.InteractiveTB.mode)
374 print('Exception reporting mode:',shell.InteractiveTB.mode)
374 except:
375 except:
375 xmode_switch_err('user')
376 xmode_switch_err('user')
376
377
377 @line_magic
378 @line_magic
378 def quickref(self,arg):
379 def quickref(self,arg):
379 """ Show a quick reference sheet """
380 """ Show a quick reference sheet """
380 from IPython.core.usage import quick_reference
381 from IPython.core.usage import quick_reference
381 qr = quick_reference + self._magic_docs(brief=True)
382 qr = quick_reference + self._magic_docs(brief=True)
382 page.page(qr)
383 page.page(qr)
383
384
384 @line_magic
385 @line_magic
385 def doctest_mode(self, parameter_s=''):
386 def doctest_mode(self, parameter_s=''):
386 """Toggle doctest mode on and off.
387 """Toggle doctest mode on and off.
387
388
388 This mode is intended to make IPython behave as much as possible like a
389 This mode is intended to make IPython behave as much as possible like a
389 plain Python shell, from the perspective of how its prompts, exceptions
390 plain Python shell, from the perspective of how its prompts, exceptions
390 and output look. This makes it easy to copy and paste parts of a
391 and output look. This makes it easy to copy and paste parts of a
391 session into doctests. It does so by:
392 session into doctests. It does so by:
392
393
393 - Changing the prompts to the classic ``>>>`` ones.
394 - Changing the prompts to the classic ``>>>`` ones.
394 - Changing the exception reporting mode to 'Plain'.
395 - Changing the exception reporting mode to 'Plain'.
395 - Disabling pretty-printing of output.
396 - Disabling pretty-printing of output.
396
397
397 Note that IPython also supports the pasting of code snippets that have
398 Note that IPython also supports the pasting of code snippets that have
398 leading '>>>' and '...' prompts in them. This means that you can paste
399 leading '>>>' and '...' prompts in them. This means that you can paste
399 doctests from files or docstrings (even if they have leading
400 doctests from files or docstrings (even if they have leading
400 whitespace), and the code will execute correctly. You can then use
401 whitespace), and the code will execute correctly. You can then use
401 '%history -t' to see the translated history; this will give you the
402 '%history -t' to see the translated history; this will give you the
402 input after removal of all the leading prompts and whitespace, which
403 input after removal of all the leading prompts and whitespace, which
403 can be pasted back into an editor.
404 can be pasted back into an editor.
404
405
405 With these features, you can switch into this mode easily whenever you
406 With these features, you can switch into this mode easily whenever you
406 need to do testing and changes to doctests, without having to leave
407 need to do testing and changes to doctests, without having to leave
407 your existing IPython session.
408 your existing IPython session.
408 """
409 """
409
410
410 # Shorthands
411 # Shorthands
411 shell = self.shell
412 shell = self.shell
412 pm = shell.prompt_manager
413 pm = shell.prompt_manager
413 meta = shell.meta
414 meta = shell.meta
414 disp_formatter = self.shell.display_formatter
415 disp_formatter = self.shell.display_formatter
415 ptformatter = disp_formatter.formatters['text/plain']
416 ptformatter = disp_formatter.formatters['text/plain']
416 # dstore is a data store kept in the instance metadata bag to track any
417 # dstore is a data store kept in the instance metadata bag to track any
417 # changes we make, so we can undo them later.
418 # changes we make, so we can undo them later.
418 dstore = meta.setdefault('doctest_mode',Struct())
419 dstore = meta.setdefault('doctest_mode',Struct())
419 save_dstore = dstore.setdefault
420 save_dstore = dstore.setdefault
420
421
421 # save a few values we'll need to recover later
422 # save a few values we'll need to recover later
422 mode = save_dstore('mode',False)
423 mode = save_dstore('mode',False)
423 save_dstore('rc_pprint',ptformatter.pprint)
424 save_dstore('rc_pprint',ptformatter.pprint)
424 save_dstore('xmode',shell.InteractiveTB.mode)
425 save_dstore('xmode',shell.InteractiveTB.mode)
425 save_dstore('rc_separate_out',shell.separate_out)
426 save_dstore('rc_separate_out',shell.separate_out)
426 save_dstore('rc_separate_out2',shell.separate_out2)
427 save_dstore('rc_separate_out2',shell.separate_out2)
427 save_dstore('rc_prompts_pad_left',pm.justify)
428 save_dstore('rc_prompts_pad_left',pm.justify)
428 save_dstore('rc_separate_in',shell.separate_in)
429 save_dstore('rc_separate_in',shell.separate_in)
429 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
430 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
430 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
431 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
431
432
432 if mode == False:
433 if mode == False:
433 # turn on
434 # turn on
434 pm.in_template = '>>> '
435 pm.in_template = '>>> '
435 pm.in2_template = '... '
436 pm.in2_template = '... '
436 pm.out_template = ''
437 pm.out_template = ''
437
438
438 # Prompt separators like plain python
439 # Prompt separators like plain python
439 shell.separate_in = ''
440 shell.separate_in = ''
440 shell.separate_out = ''
441 shell.separate_out = ''
441 shell.separate_out2 = ''
442 shell.separate_out2 = ''
442
443
443 pm.justify = False
444 pm.justify = False
444
445
445 ptformatter.pprint = False
446 ptformatter.pprint = False
446 disp_formatter.plain_text_only = True
447 disp_formatter.plain_text_only = True
447
448
448 shell.magic('xmode Plain')
449 shell.magic('xmode Plain')
449 else:
450 else:
450 # turn off
451 # turn off
451 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
452 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
452
453
453 shell.separate_in = dstore.rc_separate_in
454 shell.separate_in = dstore.rc_separate_in
454
455
455 shell.separate_out = dstore.rc_separate_out
456 shell.separate_out = dstore.rc_separate_out
456 shell.separate_out2 = dstore.rc_separate_out2
457 shell.separate_out2 = dstore.rc_separate_out2
457
458
458 pm.justify = dstore.rc_prompts_pad_left
459 pm.justify = dstore.rc_prompts_pad_left
459
460
460 ptformatter.pprint = dstore.rc_pprint
461 ptformatter.pprint = dstore.rc_pprint
461 disp_formatter.plain_text_only = dstore.rc_plain_text_only
462 disp_formatter.plain_text_only = dstore.rc_plain_text_only
462
463
463 shell.magic('xmode ' + dstore.xmode)
464 shell.magic('xmode ' + dstore.xmode)
464
465
465 # Store new mode and inform
466 # Store new mode and inform
466 dstore.mode = bool(1-int(mode))
467 dstore.mode = bool(1-int(mode))
467 mode_label = ['OFF','ON'][dstore.mode]
468 mode_label = ['OFF','ON'][dstore.mode]
468 print('Doctest mode is:', mode_label)
469 print('Doctest mode is:', mode_label)
469
470
470 @line_magic
471 @line_magic
471 def gui(self, parameter_s=''):
472 def gui(self, parameter_s=''):
472 """Enable or disable IPython GUI event loop integration.
473 """Enable or disable IPython GUI event loop integration.
473
474
474 %gui [GUINAME]
475 %gui [GUINAME]
475
476
476 This magic replaces IPython's threaded shells that were activated
477 This magic replaces IPython's threaded shells that were activated
477 using the (pylab/wthread/etc.) command line flags. GUI toolkits
478 using the (pylab/wthread/etc.) command line flags. GUI toolkits
478 can now be enabled at runtime and keyboard
479 can now be enabled at runtime and keyboard
479 interrupts should work without any problems. The following toolkits
480 interrupts should work without any problems. The following toolkits
480 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
481 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
481
482
482 %gui wx # enable wxPython event loop integration
483 %gui wx # enable wxPython event loop integration
483 %gui qt4|qt # enable PyQt4 event loop integration
484 %gui qt4|qt # enable PyQt4 event loop integration
484 %gui gtk # enable PyGTK event loop integration
485 %gui gtk # enable PyGTK event loop integration
485 %gui gtk3 # enable Gtk3 event loop integration
486 %gui gtk3 # enable Gtk3 event loop integration
486 %gui tk # enable Tk event loop integration
487 %gui tk # enable Tk event loop integration
487 %gui osx # enable Cocoa event loop integration
488 %gui osx # enable Cocoa event loop integration
488 # (requires %matplotlib 1.1)
489 # (requires %matplotlib 1.1)
489 %gui # disable all event loop integration
490 %gui # disable all event loop integration
490
491
491 WARNING: after any of these has been called you can simply create
492 WARNING: after any of these has been called you can simply create
492 an application object, but DO NOT start the event loop yourself, as
493 an application object, but DO NOT start the event loop yourself, as
493 we have already handled that.
494 we have already handled that.
494 """
495 """
495 opts, arg = self.parse_options(parameter_s, '')
496 opts, arg = self.parse_options(parameter_s, '')
496 if arg=='': arg = None
497 if arg=='': arg = None
497 try:
498 try:
498 return self.shell.enable_gui(arg)
499 return self.shell.enable_gui(arg)
499 except Exception as e:
500 except Exception as e:
500 # print simple error message, rather than traceback if we can't
501 # print simple error message, rather than traceback if we can't
501 # hook up the GUI
502 # hook up the GUI
502 error(str(e))
503 error(str(e))
503
504
504 @skip_doctest
505 @skip_doctest
505 @line_magic
506 @line_magic
506 def precision(self, s=''):
507 def precision(self, s=''):
507 """Set floating point precision for pretty printing.
508 """Set floating point precision for pretty printing.
508
509
509 Can set either integer precision or a format string.
510 Can set either integer precision or a format string.
510
511
511 If numpy has been imported and precision is an int,
512 If numpy has been imported and precision is an int,
512 numpy display precision will also be set, via ``numpy.set_printoptions``.
513 numpy display precision will also be set, via ``numpy.set_printoptions``.
513
514
514 If no argument is given, defaults will be restored.
515 If no argument is given, defaults will be restored.
515
516
516 Examples
517 Examples
517 --------
518 --------
518 ::
519 ::
519
520
520 In [1]: from math import pi
521 In [1]: from math import pi
521
522
522 In [2]: %precision 3
523 In [2]: %precision 3
523 Out[2]: u'%.3f'
524 Out[2]: u'%.3f'
524
525
525 In [3]: pi
526 In [3]: pi
526 Out[3]: 3.142
527 Out[3]: 3.142
527
528
528 In [4]: %precision %i
529 In [4]: %precision %i
529 Out[4]: u'%i'
530 Out[4]: u'%i'
530
531
531 In [5]: pi
532 In [5]: pi
532 Out[5]: 3
533 Out[5]: 3
533
534
534 In [6]: %precision %e
535 In [6]: %precision %e
535 Out[6]: u'%e'
536 Out[6]: u'%e'
536
537
537 In [7]: pi**10
538 In [7]: pi**10
538 Out[7]: 9.364805e+04
539 Out[7]: 9.364805e+04
539
540
540 In [8]: %precision
541 In [8]: %precision
541 Out[8]: u'%r'
542 Out[8]: u'%r'
542
543
543 In [9]: pi**10
544 In [9]: pi**10
544 Out[9]: 93648.047476082982
545 Out[9]: 93648.047476082982
545 """
546 """
546 ptformatter = self.shell.display_formatter.formatters['text/plain']
547 ptformatter = self.shell.display_formatter.formatters['text/plain']
547 ptformatter.float_precision = s
548 ptformatter.float_precision = s
548 return ptformatter.float_format
549 return ptformatter.float_format
549
550
550 @magic_arguments.magic_arguments()
551 @magic_arguments.magic_arguments()
551 @magic_arguments.argument(
552 @magic_arguments.argument(
552 '-e', '--export', action='store_true', default=False,
553 '-e', '--export', action='store_true', default=False,
553 help='Export IPython history as a notebook. The filename argument '
554 help='Export IPython history as a notebook. The filename argument '
554 'is used to specify the notebook name and format. For example '
555 'is used to specify the notebook name and format. For example '
555 'a filename of notebook.ipynb will result in a notebook name '
556 'a filename of notebook.ipynb will result in a notebook name '
556 'of "notebook" and a format of "xml". Likewise using a ".json" '
557 'of "notebook" and a format of "xml". Likewise using a ".json" '
557 'or ".py" file extension will write the notebook in the json '
558 'or ".py" file extension will write the notebook in the json '
558 'or py formats.'
559 'or py formats.'
559 )
560 )
560 @magic_arguments.argument(
561 @magic_arguments.argument(
561 '-f', '--format',
562 '-f', '--format',
562 help='Convert an existing IPython notebook to a new format. This option '
563 help='Convert an existing IPython notebook to a new format. This option '
563 'specifies the new format and can have the values: xml, json, py. '
564 'specifies the new format and can have the values: xml, json, py. '
564 'The target filename is chosen automatically based on the new '
565 'The target filename is chosen automatically based on the new '
565 'format. The filename argument gives the name of the source file.'
566 'format. The filename argument gives the name of the source file.'
566 )
567 )
567 @magic_arguments.argument(
568 @magic_arguments.argument(
568 'filename', type=unicode,
569 'filename', type=unicode,
569 help='Notebook name or filename'
570 help='Notebook name or filename'
570 )
571 )
571 @line_magic
572 @line_magic
572 def notebook(self, s):
573 def notebook(self, s):
573 """Export and convert IPython notebooks.
574 """Export and convert IPython notebooks.
574
575
575 This function can export the current IPython history to a notebook file
576 This function can export the current IPython history to a notebook file
576 or can convert an existing notebook file into a different format. For
577 or can convert an existing notebook file into a different format. For
577 example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb".
578 example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb".
578 To export the history to "foo.py" do "%notebook -e foo.py". To convert
579 To export the history to "foo.py" do "%notebook -e foo.py". To convert
579 "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible
580 "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible
580 formats include (json/ipynb, py).
581 formats include (json/ipynb, py).
581 """
582 """
582 args = magic_arguments.parse_argstring(self.notebook, s)
583 args = magic_arguments.parse_argstring(self.notebook, s)
583
584
584 from IPython.nbformat import current
585 from IPython.nbformat import current
585 args.filename = unquote_filename(args.filename)
586 args.filename = unquote_filename(args.filename)
586 if args.export:
587 if args.export:
587 fname, name, format = current.parse_filename(args.filename)
588 fname, name, format = current.parse_filename(args.filename)
588 cells = []
589 cells = []
589 hist = list(self.shell.history_manager.get_range())
590 hist = list(self.shell.history_manager.get_range())
590 for session, prompt_number, input in hist[:-1]:
591 for session, prompt_number, input in hist[:-1]:
591 cells.append(current.new_code_cell(prompt_number=prompt_number,
592 cells.append(current.new_code_cell(prompt_number=prompt_number,
592 input=input))
593 input=input))
593 worksheet = current.new_worksheet(cells=cells)
594 worksheet = current.new_worksheet(cells=cells)
594 nb = current.new_notebook(name=name,worksheets=[worksheet])
595 nb = current.new_notebook(name=name,worksheets=[worksheet])
595 with io.open(fname, 'w', encoding='utf-8') as f:
596 with io.open(fname, 'w', encoding='utf-8') as f:
596 current.write(nb, f, format);
597 current.write(nb, f, format);
597 elif args.format is not None:
598 elif args.format is not None:
598 old_fname, old_name, old_format = current.parse_filename(args.filename)
599 old_fname, old_name, old_format = current.parse_filename(args.filename)
599 new_format = args.format
600 new_format = args.format
600 if new_format == u'xml':
601 if new_format == u'xml':
601 raise ValueError('Notebooks cannot be written as xml.')
602 raise ValueError('Notebooks cannot be written as xml.')
602 elif new_format == u'ipynb' or new_format == u'json':
603 elif new_format == u'ipynb' or new_format == u'json':
603 new_fname = old_name + u'.ipynb'
604 new_fname = old_name + u'.ipynb'
604 new_format = u'json'
605 new_format = u'json'
605 elif new_format == u'py':
606 elif new_format == u'py':
606 new_fname = old_name + u'.py'
607 new_fname = old_name + u'.py'
607 else:
608 else:
608 raise ValueError('Invalid notebook format: %s' % new_format)
609 raise ValueError('Invalid notebook format: %s' % new_format)
609 with io.open(old_fname, 'r', encoding='utf-8') as f:
610 with io.open(old_fname, 'r', encoding='utf-8') as f:
610 nb = current.read(f, old_format)
611 nb = current.read(f, old_format)
611 with io.open(new_fname, 'w', encoding='utf-8') as f:
612 with io.open(new_fname, 'w', encoding='utf-8') as f:
612 current.write(nb, f, new_format)
613 current.write(nb, f, new_format)
@@ -1,1030 +1,1035 b''
1 """Implementation of execution-related magic functions.
1 """Implementation of execution-related magic functions.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012 The IPython Development Team.
4 # Copyright (c) 2012 The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # Stdlib
15 # Stdlib
16 import __builtin__ as builtin_mod
16 import __builtin__ as builtin_mod
17 import bdb
17 import bdb
18 import os
18 import os
19 import sys
19 import sys
20 import time
20 import time
21 from StringIO import StringIO
21 from StringIO import StringIO
22
22
23 # cProfile was added in Python2.5
23 # cProfile was added in Python2.5
24 try:
24 try:
25 import cProfile as profile
25 import cProfile as profile
26 import pstats
26 import pstats
27 except ImportError:
27 except ImportError:
28 # profile isn't bundled by default in Debian for license reasons
28 # profile isn't bundled by default in Debian for license reasons
29 try:
29 try:
30 import profile, pstats
30 import profile, pstats
31 except ImportError:
31 except ImportError:
32 profile = pstats = None
32 profile = pstats = None
33
33
34 # Our own packages
34 # Our own packages
35 from IPython.core import debugger, oinspect
35 from IPython.core import debugger, oinspect
36 from IPython.core import magic_arguments
36 from IPython.core import magic_arguments
37 from IPython.core import page
37 from IPython.core import page
38 from IPython.core.error import UsageError
38 from IPython.core.error import UsageError
39 from IPython.core.macro import Macro
39 from IPython.core.macro import Macro
40 from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic,
40 from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic,
41 line_cell_magic, on_off, needs_local_scope)
41 line_cell_magic, on_off, needs_local_scope)
42 from IPython.testing.skipdoctest import skip_doctest
42 from IPython.testing.skipdoctest import skip_doctest
43 from IPython.utils import py3compat
43 from IPython.utils import py3compat
44 from IPython.utils.io import capture_output
44 from IPython.utils.io import capture_output
45 from IPython.utils.ipstruct import Struct
45 from IPython.utils.ipstruct import Struct
46 from IPython.utils.module_paths import find_mod
46 from IPython.utils.module_paths import find_mod
47 from IPython.utils.path import get_py_filename, unquote_filename, shellglob
47 from IPython.utils.path import get_py_filename, unquote_filename, shellglob
48 from IPython.utils.timing import clock, clock2
48 from IPython.utils.timing import clock, clock2
49 from IPython.utils.warn import warn, error
49 from IPython.utils.warn import warn, error
50
50
51
51
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53 # Magic implementation classes
53 # Magic implementation classes
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55
55
56 @magics_class
56 @magics_class
57 class ExecutionMagics(Magics):
57 class ExecutionMagics(Magics):
58 """Magics related to code execution, debugging, profiling, etc.
58 """Magics related to code execution, debugging, profiling, etc.
59
59
60 """
60 """
61
61
62 def __init__(self, shell):
62 def __init__(self, shell):
63 super(ExecutionMagics, self).__init__(shell)
63 super(ExecutionMagics, self).__init__(shell)
64 if profile is None:
64 if profile is None:
65 self.prun = self.profile_missing_notice
65 self.prun = self.profile_missing_notice
66 # Default execution function used to actually run user code.
66 # Default execution function used to actually run user code.
67 self.default_runner = None
67 self.default_runner = None
68
68
69 def profile_missing_notice(self, *args, **kwargs):
69 def profile_missing_notice(self, *args, **kwargs):
70 error("""\
70 error("""\
71 The profile module could not be found. It has been removed from the standard
71 The profile module could not be found. It has been removed from the standard
72 python packages because of its non-free license. To use profiling, install the
72 python packages because of its non-free license. To use profiling, install the
73 python-profiler package from non-free.""")
73 python-profiler package from non-free.""")
74
74
75 @skip_doctest
75 @skip_doctest
76 @line_cell_magic
76 @line_cell_magic
77 def prun(self, parameter_s='', cell=None, user_mode=True,
77 def prun(self, parameter_s='', cell=None, user_mode=True,
78 opts=None,arg_lst=None,prog_ns=None):
78 opts=None,arg_lst=None,prog_ns=None):
79
79
80 """Run a statement through the python code profiler.
80 """Run a statement through the python code profiler.
81
81
82 Usage, in line mode:
82 Usage, in line mode:
83 %prun [options] statement
83 %prun [options] statement
84
84
85 Usage, in cell mode:
85 Usage, in cell mode:
86 %%prun [options] [statement]
86 %%prun [options] [statement]
87 code...
87 code...
88 code...
88 code...
89
89
90 In cell mode, the additional code lines are appended to the (possibly
90 In cell mode, the additional code lines are appended to the (possibly
91 empty) statement in the first line. Cell mode allows you to easily
91 empty) statement in the first line. Cell mode allows you to easily
92 profile multiline blocks without having to put them in a separate
92 profile multiline blocks without having to put them in a separate
93 function.
93 function.
94
94
95 The given statement (which doesn't require quote marks) is run via the
95 The given statement (which doesn't require quote marks) is run via the
96 python profiler in a manner similar to the profile.run() function.
96 python profiler in a manner similar to the profile.run() function.
97 Namespaces are internally managed to work correctly; profile.run
97 Namespaces are internally managed to work correctly; profile.run
98 cannot be used in IPython because it makes certain assumptions about
98 cannot be used in IPython because it makes certain assumptions about
99 namespaces which do not hold under IPython.
99 namespaces which do not hold under IPython.
100
100
101 Options:
101 Options:
102
102
103 -l <limit>: you can place restrictions on what or how much of the
103 -l <limit>: you can place restrictions on what or how much of the
104 profile gets printed. The limit value can be:
104 profile gets printed. The limit value can be:
105
105
106 * A string: only information for function names containing this string
106 * A string: only information for function names containing this string
107 is printed.
107 is printed.
108
108
109 * An integer: only these many lines are printed.
109 * An integer: only these many lines are printed.
110
110
111 * A float (between 0 and 1): this fraction of the report is printed
111 * A float (between 0 and 1): this fraction of the report is printed
112 (for example, use a limit of 0.4 to see the topmost 40% only).
112 (for example, use a limit of 0.4 to see the topmost 40% only).
113
113
114 You can combine several limits with repeated use of the option. For
114 You can combine several limits with repeated use of the option. For
115 example, '-l __init__ -l 5' will print only the topmost 5 lines of
115 example, '-l __init__ -l 5' will print only the topmost 5 lines of
116 information about class constructors.
116 information about class constructors.
117
117
118 -r: return the pstats.Stats object generated by the profiling. This
118 -r: return the pstats.Stats object generated by the profiling. This
119 object has all the information about the profile in it, and you can
119 object has all the information about the profile in it, and you can
120 later use it for further analysis or in other functions.
120 later use it for further analysis or in other functions.
121
121
122 -s <key>: sort profile by given key. You can provide more than one key
122 -s <key>: sort profile by given key. You can provide more than one key
123 by using the option several times: '-s key1 -s key2 -s key3...'. The
123 by using the option several times: '-s key1 -s key2 -s key3...'. The
124 default sorting key is 'time'.
124 default sorting key is 'time'.
125
125
126 The following is copied verbatim from the profile documentation
126 The following is copied verbatim from the profile documentation
127 referenced below:
127 referenced below:
128
128
129 When more than one key is provided, additional keys are used as
129 When more than one key is provided, additional keys are used as
130 secondary criteria when the there is equality in all keys selected
130 secondary criteria when the there is equality in all keys selected
131 before them.
131 before them.
132
132
133 Abbreviations can be used for any key names, as long as the
133 Abbreviations can be used for any key names, as long as the
134 abbreviation is unambiguous. The following are the keys currently
134 abbreviation is unambiguous. The following are the keys currently
135 defined:
135 defined:
136
136
137 Valid Arg Meaning
137 Valid Arg Meaning
138 "calls" call count
138 "calls" call count
139 "cumulative" cumulative time
139 "cumulative" cumulative time
140 "file" file name
140 "file" file name
141 "module" file name
141 "module" file name
142 "pcalls" primitive call count
142 "pcalls" primitive call count
143 "line" line number
143 "line" line number
144 "name" function name
144 "name" function name
145 "nfl" name/file/line
145 "nfl" name/file/line
146 "stdname" standard name
146 "stdname" standard name
147 "time" internal time
147 "time" internal time
148
148
149 Note that all sorts on statistics are in descending order (placing
149 Note that all sorts on statistics are in descending order (placing
150 most time consuming items first), where as name, file, and line number
150 most time consuming items first), where as name, file, and line number
151 searches are in ascending order (i.e., alphabetical). The subtle
151 searches are in ascending order (i.e., alphabetical). The subtle
152 distinction between "nfl" and "stdname" is that the standard name is a
152 distinction between "nfl" and "stdname" is that the standard name is a
153 sort of the name as printed, which means that the embedded line
153 sort of the name as printed, which means that the embedded line
154 numbers get compared in an odd way. For example, lines 3, 20, and 40
154 numbers get compared in an odd way. For example, lines 3, 20, and 40
155 would (if the file names were the same) appear in the string order
155 would (if the file names were the same) appear in the string order
156 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
156 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
157 line numbers. In fact, sort_stats("nfl") is the same as
157 line numbers. In fact, sort_stats("nfl") is the same as
158 sort_stats("name", "file", "line").
158 sort_stats("name", "file", "line").
159
159
160 -T <filename>: save profile results as shown on screen to a text
160 -T <filename>: save profile results as shown on screen to a text
161 file. The profile is still shown on screen.
161 file. The profile is still shown on screen.
162
162
163 -D <filename>: save (via dump_stats) profile statistics to given
163 -D <filename>: save (via dump_stats) profile statistics to given
164 filename. This data is in a format understood by the pstats module, and
164 filename. This data is in a format understood by the pstats module, and
165 is generated by a call to the dump_stats() method of profile
165 is generated by a call to the dump_stats() method of profile
166 objects. The profile is still shown on screen.
166 objects. The profile is still shown on screen.
167
167
168 -q: suppress output to the pager. Best used with -T and/or -D above.
168 -q: suppress output to the pager. Best used with -T and/or -D above.
169
169
170 If you want to run complete programs under the profiler's control, use
170 If you want to run complete programs under the profiler's control, use
171 '%run -p [prof_opts] filename.py [args to program]' where prof_opts
171 '%run -p [prof_opts] filename.py [args to program]' where prof_opts
172 contains profiler specific options as described here.
172 contains profiler specific options as described here.
173
173
174 You can read the complete documentation for the profile module with::
174 You can read the complete documentation for the profile module with::
175
175
176 In [1]: import profile; profile.help()
176 In [1]: import profile; profile.help()
177 """
177 """
178
178
179 opts_def = Struct(D=[''],l=[],s=['time'],T=[''])
179 opts_def = Struct(D=[''],l=[],s=['time'],T=[''])
180
180
181 if user_mode: # regular user call
181 if user_mode: # regular user call
182 opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q',
182 opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q',
183 list_all=True, posix=False)
183 list_all=True, posix=False)
184 namespace = self.shell.user_ns
184 namespace = self.shell.user_ns
185 if cell is not None:
185 if cell is not None:
186 arg_str += '\n' + cell
186 arg_str += '\n' + cell
187 else: # called to run a program by %run -p
187 else: # called to run a program by %run -p
188 try:
188 try:
189 filename = get_py_filename(arg_lst[0])
189 filename = get_py_filename(arg_lst[0])
190 except IOError as e:
190 except IOError as e:
191 try:
191 try:
192 msg = str(e)
192 msg = str(e)
193 except UnicodeError:
193 except UnicodeError:
194 msg = e.message
194 msg = e.message
195 error(msg)
195 error(msg)
196 return
196 return
197
197
198 arg_str = 'execfile(filename,prog_ns)'
198 arg_str = 'execfile(filename,prog_ns)'
199 namespace = {
199 namespace = {
200 'execfile': self.shell.safe_execfile,
200 'execfile': self.shell.safe_execfile,
201 'prog_ns': prog_ns,
201 'prog_ns': prog_ns,
202 'filename': filename
202 'filename': filename
203 }
203 }
204
204
205 opts.merge(opts_def)
205 opts.merge(opts_def)
206
206
207 prof = profile.Profile()
207 prof = profile.Profile()
208 try:
208 try:
209 prof = prof.runctx(arg_str,namespace,namespace)
209 prof = prof.runctx(arg_str,namespace,namespace)
210 sys_exit = ''
210 sys_exit = ''
211 except SystemExit:
211 except SystemExit:
212 sys_exit = """*** SystemExit exception caught in code being profiled."""
212 sys_exit = """*** SystemExit exception caught in code being profiled."""
213
213
214 stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s)
214 stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s)
215
215
216 lims = opts.l
216 lims = opts.l
217 if lims:
217 if lims:
218 lims = [] # rebuild lims with ints/floats/strings
218 lims = [] # rebuild lims with ints/floats/strings
219 for lim in opts.l:
219 for lim in opts.l:
220 try:
220 try:
221 lims.append(int(lim))
221 lims.append(int(lim))
222 except ValueError:
222 except ValueError:
223 try:
223 try:
224 lims.append(float(lim))
224 lims.append(float(lim))
225 except ValueError:
225 except ValueError:
226 lims.append(lim)
226 lims.append(lim)
227
227
228 # Trap output.
228 # Trap output.
229 stdout_trap = StringIO()
229 stdout_trap = StringIO()
230 stats_stream = stats.stream
230 stats_stream = stats.stream
231 try:
231 try:
232 stats.stream = stdout_trap
232 stats.stream = stdout_trap
233 stats.print_stats(*lims)
233 stats.print_stats(*lims)
234 finally:
234 finally:
235 stats.stream = stats_stream
235 stats.stream = stats_stream
236
236
237 output = stdout_trap.getvalue()
237 output = stdout_trap.getvalue()
238 output = output.rstrip()
238 output = output.rstrip()
239
239
240 if 'q' not in opts:
240 if 'q' not in opts:
241 page.page(output)
241 page.page(output)
242 print sys_exit,
242 print sys_exit,
243
243
244 dump_file = opts.D[0]
244 dump_file = opts.D[0]
245 text_file = opts.T[0]
245 text_file = opts.T[0]
246 if dump_file:
246 if dump_file:
247 dump_file = unquote_filename(dump_file)
247 dump_file = unquote_filename(dump_file)
248 prof.dump_stats(dump_file)
248 prof.dump_stats(dump_file)
249 print '\n*** Profile stats marshalled to file',\
249 print '\n*** Profile stats marshalled to file',\
250 repr(dump_file)+'.',sys_exit
250 repr(dump_file)+'.',sys_exit
251 if text_file:
251 if text_file:
252 text_file = unquote_filename(text_file)
252 text_file = unquote_filename(text_file)
253 pfile = open(text_file,'w')
253 pfile = open(text_file,'w')
254 pfile.write(output)
254 pfile.write(output)
255 pfile.close()
255 pfile.close()
256 print '\n*** Profile printout saved to text file',\
256 print '\n*** Profile printout saved to text file',\
257 repr(text_file)+'.',sys_exit
257 repr(text_file)+'.',sys_exit
258
258
259 if 'r' in opts:
259 if 'r' in opts:
260 return stats
260 return stats
261 else:
261 else:
262 return None
262 return None
263
263
264 @line_magic
264 @line_magic
265 def pdb(self, parameter_s=''):
265 def pdb(self, parameter_s=''):
266 """Control the automatic calling of the pdb interactive debugger.
266 """Control the automatic calling of the pdb interactive debugger.
267
267
268 Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without
268 Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without
269 argument it works as a toggle.
269 argument it works as a toggle.
270
270
271 When an exception is triggered, IPython can optionally call the
271 When an exception is triggered, IPython can optionally call the
272 interactive pdb debugger after the traceback printout. %pdb toggles
272 interactive pdb debugger after the traceback printout. %pdb toggles
273 this feature on and off.
273 this feature on and off.
274
274
275 The initial state of this feature is set in your configuration
275 The initial state of this feature is set in your configuration
276 file (the option is ``InteractiveShell.pdb``).
276 file (the option is ``InteractiveShell.pdb``).
277
277
278 If you want to just activate the debugger AFTER an exception has fired,
278 If you want to just activate the debugger AFTER an exception has fired,
279 without having to type '%pdb on' and rerunning your code, you can use
279 without having to type '%pdb on' and rerunning your code, you can use
280 the %debug magic."""
280 the %debug magic."""
281
281
282 par = parameter_s.strip().lower()
282 par = parameter_s.strip().lower()
283
283
284 if par:
284 if par:
285 try:
285 try:
286 new_pdb = {'off':0,'0':0,'on':1,'1':1}[par]
286 new_pdb = {'off':0,'0':0,'on':1,'1':1}[par]
287 except KeyError:
287 except KeyError:
288 print ('Incorrect argument. Use on/1, off/0, '
288 print ('Incorrect argument. Use on/1, off/0, '
289 'or nothing for a toggle.')
289 'or nothing for a toggle.')
290 return
290 return
291 else:
291 else:
292 # toggle
292 # toggle
293 new_pdb = not self.shell.call_pdb
293 new_pdb = not self.shell.call_pdb
294
294
295 # set on the shell
295 # set on the shell
296 self.shell.call_pdb = new_pdb
296 self.shell.call_pdb = new_pdb
297 print 'Automatic pdb calling has been turned',on_off(new_pdb)
297 print 'Automatic pdb calling has been turned',on_off(new_pdb)
298
298
299 @line_magic
299 @line_magic
300 def debug(self, parameter_s=''):
300 def debug(self, parameter_s=''):
301 """Activate the interactive debugger in post-mortem mode.
301 """Activate the interactive debugger in post-mortem mode.
302
302
303 If an exception has just occurred, this lets you inspect its stack
303 If an exception has just occurred, this lets you inspect its stack
304 frames interactively. Note that this will always work only on the last
304 frames interactively. Note that this will always work only on the last
305 traceback that occurred, so you must call this quickly after an
305 traceback that occurred, so you must call this quickly after an
306 exception that you wish to inspect has fired, because if another one
306 exception that you wish to inspect has fired, because if another one
307 occurs, it clobbers the previous one.
307 occurs, it clobbers the previous one.
308
308
309 If you want IPython to automatically do this on every exception, see
309 If you want IPython to automatically do this on every exception, see
310 the %pdb magic for more details.
310 the %pdb magic for more details.
311 """
311 """
312 self.shell.debugger(force=True)
312 self.shell.debugger(force=True)
313
313
314 @line_magic
314 @line_magic
315 def tb(self, s):
315 def tb(self, s):
316 """Print the last traceback with the currently active exception mode.
316 """Print the last traceback with the currently active exception mode.
317
317
318 See %xmode for changing exception reporting modes."""
318 See %xmode for changing exception reporting modes."""
319 self.shell.showtraceback()
319 self.shell.showtraceback()
320
320
321 @skip_doctest
321 @skip_doctest
322 @line_magic
322 @line_magic
323 def run(self, parameter_s='', runner=None,
323 def run(self, parameter_s='', runner=None,
324 file_finder=get_py_filename):
324 file_finder=get_py_filename):
325 """Run the named file inside IPython as a program.
325 """Run the named file inside IPython as a program.
326
326
327 Usage:\\
327 Usage:\\
328 %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options] -G] file [args]
328 %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options] -G] file [args]
329
329
330 Parameters after the filename are passed as command-line arguments to
330 Parameters after the filename are passed as command-line arguments to
331 the program (put in sys.argv). Then, control returns to IPython's
331 the program (put in sys.argv). Then, control returns to IPython's
332 prompt.
332 prompt.
333
333
334 This is similar to running at a system prompt:\\
334 This is similar to running at a system prompt:\\
335 $ python file args\\
335 $ python file args\\
336 but with the advantage of giving you IPython's tracebacks, and of
336 but with the advantage of giving you IPython's tracebacks, and of
337 loading all variables into your interactive namespace for further use
337 loading all variables into your interactive namespace for further use
338 (unless -p is used, see below).
338 (unless -p is used, see below).
339
339
340 The file is executed in a namespace initially consisting only of
340 The file is executed in a namespace initially consisting only of
341 __name__=='__main__' and sys.argv constructed as indicated. It thus
341 __name__=='__main__' and sys.argv constructed as indicated. It thus
342 sees its environment as if it were being run as a stand-alone program
342 sees its environment as if it were being run as a stand-alone program
343 (except for sharing global objects such as previously imported
343 (except for sharing global objects such as previously imported
344 modules). But after execution, the IPython interactive namespace gets
344 modules). But after execution, the IPython interactive namespace gets
345 updated with all variables defined in the program (except for __name__
345 updated with all variables defined in the program (except for __name__
346 and sys.argv). This allows for very convenient loading of code for
346 and sys.argv). This allows for very convenient loading of code for
347 interactive work, while giving each program a 'clean sheet' to run in.
347 interactive work, while giving each program a 'clean sheet' to run in.
348
348
349 Arguments are expanded using shell-like glob match. Patterns
349 Arguments are expanded using shell-like glob match. Patterns
350 '*', '?', '[seq]' and '[!seq]' can be used. Additionally,
350 '*', '?', '[seq]' and '[!seq]' can be used. Additionally,
351 tilde '~' will be expanded into user's home directory. Unlike
351 tilde '~' will be expanded into user's home directory. Unlike
352 real shells, quotation does not suppress expansions. Use
352 real shells, quotation does not suppress expansions. Use
353 *two* back slashes (e.g., '\\\\*') to suppress expansions.
353 *two* back slashes (e.g., '\\\\*') to suppress expansions.
354 To completely disable these expansions, you can use -G flag.
354 To completely disable these expansions, you can use -G flag.
355
355
356 Options:
356 Options:
357
357
358 -n: __name__ is NOT set to '__main__', but to the running file's name
358 -n: __name__ is NOT set to '__main__', but to the running file's name
359 without extension (as python does under import). This allows running
359 without extension (as python does under import). This allows running
360 scripts and reloading the definitions in them without calling code
360 scripts and reloading the definitions in them without calling code
361 protected by an ' if __name__ == "__main__" ' clause.
361 protected by an ' if __name__ == "__main__" ' clause.
362
362
363 -i: run the file in IPython's namespace instead of an empty one. This
363 -i: run the file in IPython's namespace instead of an empty one. This
364 is useful if you are experimenting with code written in a text editor
364 is useful if you are experimenting with code written in a text editor
365 which depends on variables defined interactively.
365 which depends on variables defined interactively.
366
366
367 -e: ignore sys.exit() calls or SystemExit exceptions in the script
367 -e: ignore sys.exit() calls or SystemExit exceptions in the script
368 being run. This is particularly useful if IPython is being used to
368 being run. This is particularly useful if IPython is being used to
369 run unittests, which always exit with a sys.exit() call. In such
369 run unittests, which always exit with a sys.exit() call. In such
370 cases you are interested in the output of the test results, not in
370 cases you are interested in the output of the test results, not in
371 seeing a traceback of the unittest module.
371 seeing a traceback of the unittest module.
372
372
373 -t: print timing information at the end of the run. IPython will give
373 -t: print timing information at the end of the run. IPython will give
374 you an estimated CPU time consumption for your script, which under
374 you an estimated CPU time consumption for your script, which under
375 Unix uses the resource module to avoid the wraparound problems of
375 Unix uses the resource module to avoid the wraparound problems of
376 time.clock(). Under Unix, an estimate of time spent on system tasks
376 time.clock(). Under Unix, an estimate of time spent on system tasks
377 is also given (for Windows platforms this is reported as 0.0).
377 is also given (for Windows platforms this is reported as 0.0).
378
378
379 If -t is given, an additional -N<N> option can be given, where <N>
379 If -t is given, an additional -N<N> option can be given, where <N>
380 must be an integer indicating how many times you want the script to
380 must be an integer indicating how many times you want the script to
381 run. The final timing report will include total and per run results.
381 run. The final timing report will include total and per run results.
382
382
383 For example (testing the script uniq_stable.py)::
383 For example (testing the script uniq_stable.py)::
384
384
385 In [1]: run -t uniq_stable
385 In [1]: run -t uniq_stable
386
386
387 IPython CPU timings (estimated):\\
387 IPython CPU timings (estimated):\\
388 User : 0.19597 s.\\
388 User : 0.19597 s.\\
389 System: 0.0 s.\\
389 System: 0.0 s.\\
390
390
391 In [2]: run -t -N5 uniq_stable
391 In [2]: run -t -N5 uniq_stable
392
392
393 IPython CPU timings (estimated):\\
393 IPython CPU timings (estimated):\\
394 Total runs performed: 5\\
394 Total runs performed: 5\\
395 Times : Total Per run\\
395 Times : Total Per run\\
396 User : 0.910862 s, 0.1821724 s.\\
396 User : 0.910862 s, 0.1821724 s.\\
397 System: 0.0 s, 0.0 s.
397 System: 0.0 s, 0.0 s.
398
398
399 -d: run your program under the control of pdb, the Python debugger.
399 -d: run your program under the control of pdb, the Python debugger.
400 This allows you to execute your program step by step, watch variables,
400 This allows you to execute your program step by step, watch variables,
401 etc. Internally, what IPython does is similar to calling:
401 etc. Internally, what IPython does is similar to calling:
402
402
403 pdb.run('execfile("YOURFILENAME")')
403 pdb.run('execfile("YOURFILENAME")')
404
404
405 with a breakpoint set on line 1 of your file. You can change the line
405 with a breakpoint set on line 1 of your file. You can change the line
406 number for this automatic breakpoint to be <N> by using the -bN option
406 number for this automatic breakpoint to be <N> by using the -bN option
407 (where N must be an integer). For example::
407 (where N must be an integer). For example::
408
408
409 %run -d -b40 myscript
409 %run -d -b40 myscript
410
410
411 will set the first breakpoint at line 40 in myscript.py. Note that
411 will set the first breakpoint at line 40 in myscript.py. Note that
412 the first breakpoint must be set on a line which actually does
412 the first breakpoint must be set on a line which actually does
413 something (not a comment or docstring) for it to stop execution.
413 something (not a comment or docstring) for it to stop execution.
414
414
415 When the pdb debugger starts, you will see a (Pdb) prompt. You must
415 When the pdb debugger starts, you will see a (Pdb) prompt. You must
416 first enter 'c' (without quotes) to start execution up to the first
416 first enter 'c' (without quotes) to start execution up to the first
417 breakpoint.
417 breakpoint.
418
418
419 Entering 'help' gives information about the use of the debugger. You
419 Entering 'help' gives information about the use of the debugger. You
420 can easily see pdb's full documentation with "import pdb;pdb.help()"
420 can easily see pdb's full documentation with "import pdb;pdb.help()"
421 at a prompt.
421 at a prompt.
422
422
423 -p: run program under the control of the Python profiler module (which
423 -p: run program under the control of the Python profiler module (which
424 prints a detailed report of execution times, function calls, etc).
424 prints a detailed report of execution times, function calls, etc).
425
425
426 You can pass other options after -p which affect the behavior of the
426 You can pass other options after -p which affect the behavior of the
427 profiler itself. See the docs for %prun for details.
427 profiler itself. See the docs for %prun for details.
428
428
429 In this mode, the program's variables do NOT propagate back to the
429 In this mode, the program's variables do NOT propagate back to the
430 IPython interactive namespace (because they remain in the namespace
430 IPython interactive namespace (because they remain in the namespace
431 where the profiler executes them).
431 where the profiler executes them).
432
432
433 Internally this triggers a call to %prun, see its documentation for
433 Internally this triggers a call to %prun, see its documentation for
434 details on the options available specifically for profiling.
434 details on the options available specifically for profiling.
435
435
436 There is one special usage for which the text above doesn't apply:
436 There is one special usage for which the text above doesn't apply:
437 if the filename ends with .ipy, the file is run as ipython script,
437 if the filename ends with .ipy, the file is run as ipython script,
438 just as if the commands were written on IPython prompt.
438 just as if the commands were written on IPython prompt.
439
439
440 -m: specify module name to load instead of script path. Similar to
440 -m: specify module name to load instead of script path. Similar to
441 the -m option for the python interpreter. Use this option last if you
441 the -m option for the python interpreter. Use this option last if you
442 want to combine with other %run options. Unlike the python interpreter
442 want to combine with other %run options. Unlike the python interpreter
443 only source modules are allowed no .pyc or .pyo files.
443 only source modules are allowed no .pyc or .pyo files.
444 For example::
444 For example::
445
445
446 %run -m example
446 %run -m example
447
447
448 will run the example module.
448 will run the example module.
449
449
450 -G: disable shell-like glob expansion of arguments.
450 -G: disable shell-like glob expansion of arguments.
451
451
452 """
452 """
453
453
454 # get arguments and set sys.argv for program to be run.
454 # get arguments and set sys.argv for program to be run.
455 opts, arg_lst = self.parse_options(parameter_s,
455 opts, arg_lst = self.parse_options(parameter_s,
456 'nidtN:b:pD:l:rs:T:em:G',
456 'nidtN:b:pD:l:rs:T:em:G',
457 mode='list', list_all=1)
457 mode='list', list_all=1)
458 if "m" in opts:
458 if "m" in opts:
459 modulename = opts["m"][0]
459 modulename = opts["m"][0]
460 modpath = find_mod(modulename)
460 modpath = find_mod(modulename)
461 if modpath is None:
461 if modpath is None:
462 warn('%r is not a valid modulename on sys.path'%modulename)
462 warn('%r is not a valid modulename on sys.path'%modulename)
463 return
463 return
464 arg_lst = [modpath] + arg_lst
464 arg_lst = [modpath] + arg_lst
465 try:
465 try:
466 filename = file_finder(arg_lst[0])
466 filename = file_finder(arg_lst[0])
467 except IndexError:
467 except IndexError:
468 warn('you must provide at least a filename.')
468 warn('you must provide at least a filename.')
469 print '\n%run:\n', oinspect.getdoc(self.run)
469 print '\n%run:\n', oinspect.getdoc(self.run)
470 return
470 return
471 except IOError as e:
471 except IOError as e:
472 try:
472 try:
473 msg = str(e)
473 msg = str(e)
474 except UnicodeError:
474 except UnicodeError:
475 msg = e.message
475 msg = e.message
476 error(msg)
476 error(msg)
477 return
477 return
478
478
479 if filename.lower().endswith('.ipy'):
479 if filename.lower().endswith('.ipy'):
480 self.shell.safe_execfile_ipy(filename)
480 self.shell.safe_execfile_ipy(filename)
481 return
481 return
482
482
483 # Control the response to exit() calls made by the script being run
483 # Control the response to exit() calls made by the script being run
484 exit_ignore = 'e' in opts
484 exit_ignore = 'e' in opts
485
485
486 # Make sure that the running script gets a proper sys.argv as if it
486 # Make sure that the running script gets a proper sys.argv as if it
487 # were run from a system shell.
487 # were run from a system shell.
488 save_argv = sys.argv # save it for later restoring
488 save_argv = sys.argv # save it for later restoring
489
489
490 if 'G' in opts:
490 if 'G' in opts:
491 args = arg_lst[1:]
491 args = arg_lst[1:]
492 else:
492 else:
493 # tilde and glob expansion
493 # tilde and glob expansion
494 args = shellglob(map(os.path.expanduser, arg_lst[1:]))
494 args = shellglob(map(os.path.expanduser, arg_lst[1:]))
495
495
496 sys.argv = [filename] + args # put in the proper filename
496 sys.argv = [filename] + args # put in the proper filename
497 # protect sys.argv from potential unicode strings on Python 2:
497 # protect sys.argv from potential unicode strings on Python 2:
498 if not py3compat.PY3:
498 if not py3compat.PY3:
499 sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
499 sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
500
500
501 if 'i' in opts:
501 if 'i' in opts:
502 # Run in user's interactive namespace
502 # Run in user's interactive namespace
503 prog_ns = self.shell.user_ns
503 prog_ns = self.shell.user_ns
504 __name__save = self.shell.user_ns['__name__']
504 __name__save = self.shell.user_ns['__name__']
505 prog_ns['__name__'] = '__main__'
505 prog_ns['__name__'] = '__main__'
506 main_mod = self.shell.new_main_mod(prog_ns)
506 main_mod = self.shell.new_main_mod(prog_ns)
507 else:
507 else:
508 # Run in a fresh, empty namespace
508 # Run in a fresh, empty namespace
509 if 'n' in opts:
509 if 'n' in opts:
510 name = os.path.splitext(os.path.basename(filename))[0]
510 name = os.path.splitext(os.path.basename(filename))[0]
511 else:
511 else:
512 name = '__main__'
512 name = '__main__'
513
513
514 main_mod = self.shell.new_main_mod()
514 main_mod = self.shell.new_main_mod()
515 prog_ns = main_mod.__dict__
515 prog_ns = main_mod.__dict__
516 prog_ns['__name__'] = name
516 prog_ns['__name__'] = name
517
517
518 # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
518 # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
519 # set the __file__ global in the script's namespace
519 # set the __file__ global in the script's namespace
520 prog_ns['__file__'] = filename
520 prog_ns['__file__'] = filename
521
521
522 # pickle fix. See interactiveshell for an explanation. But we need to
522 # pickle fix. See interactiveshell for an explanation. But we need to
523 # make sure that, if we overwrite __main__, we replace it at the end
523 # make sure that, if we overwrite __main__, we replace it at the end
524 main_mod_name = prog_ns['__name__']
524 main_mod_name = prog_ns['__name__']
525
525
526 if main_mod_name == '__main__':
526 if main_mod_name == '__main__':
527 restore_main = sys.modules['__main__']
527 restore_main = sys.modules['__main__']
528 else:
528 else:
529 restore_main = False
529 restore_main = False
530
530
531 # This needs to be undone at the end to prevent holding references to
531 # This needs to be undone at the end to prevent holding references to
532 # every single object ever created.
532 # every single object ever created.
533 sys.modules[main_mod_name] = main_mod
533 sys.modules[main_mod_name] = main_mod
534
534
535 try:
535 try:
536 stats = None
536 stats = None
537 with self.shell.readline_no_record:
537 with self.shell.readline_no_record:
538 if 'p' in opts:
538 if 'p' in opts:
539 stats = self.prun('', None, False, opts, arg_lst, prog_ns)
539 stats = self.prun('', None, False, opts, arg_lst, prog_ns)
540 else:
540 else:
541 if 'd' in opts:
541 if 'd' in opts:
542 deb = debugger.Pdb(self.shell.colors)
542 deb = debugger.Pdb(self.shell.colors)
543 # reset Breakpoint state, which is moronically kept
543 # reset Breakpoint state, which is moronically kept
544 # in a class
544 # in a class
545 bdb.Breakpoint.next = 1
545 bdb.Breakpoint.next = 1
546 bdb.Breakpoint.bplist = {}
546 bdb.Breakpoint.bplist = {}
547 bdb.Breakpoint.bpbynumber = [None]
547 bdb.Breakpoint.bpbynumber = [None]
548 # Set an initial breakpoint to stop execution
548 # Set an initial breakpoint to stop execution
549 maxtries = 10
549 maxtries = 10
550 bp = int(opts.get('b', [1])[0])
550 bp = int(opts.get('b', [1])[0])
551 checkline = deb.checkline(filename, bp)
551 checkline = deb.checkline(filename, bp)
552 if not checkline:
552 if not checkline:
553 for bp in range(bp + 1, bp + maxtries + 1):
553 for bp in range(bp + 1, bp + maxtries + 1):
554 if deb.checkline(filename, bp):
554 if deb.checkline(filename, bp):
555 break
555 break
556 else:
556 else:
557 msg = ("\nI failed to find a valid line to set "
557 msg = ("\nI failed to find a valid line to set "
558 "a breakpoint\n"
558 "a breakpoint\n"
559 "after trying up to line: %s.\n"
559 "after trying up to line: %s.\n"
560 "Please set a valid breakpoint manually "
560 "Please set a valid breakpoint manually "
561 "with the -b option." % bp)
561 "with the -b option." % bp)
562 error(msg)
562 error(msg)
563 return
563 return
564 # if we find a good linenumber, set the breakpoint
564 # if we find a good linenumber, set the breakpoint
565 deb.do_break('%s:%s' % (filename, bp))
565 deb.do_break('%s:%s' % (filename, bp))
566
567 # Mimic Pdb._runscript(...)
568 deb._wait_for_mainpyfile = True
569 deb.mainpyfile = deb.canonic(filename)
570
566 # Start file run
571 # Start file run
567 print "NOTE: Enter 'c' at the",
572 print "NOTE: Enter 'c' at the",
568 print "%s prompt to start your script." % deb.prompt
573 print "%s prompt to start your script." % deb.prompt
569 ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns}
574 ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns}
570 try:
575 try:
571 #save filename so it can be used by methods on the deb object
576 #save filename so it can be used by methods on the deb object
572 deb._exec_filename = filename
577 deb._exec_filename = filename
573 deb.run('execfile("%s", prog_ns)' % filename, ns)
578 deb.run('execfile("%s", prog_ns)' % filename, ns)
574
579
575 except:
580 except:
576 etype, value, tb = sys.exc_info()
581 etype, value, tb = sys.exc_info()
577 # Skip three frames in the traceback: the %run one,
582 # Skip three frames in the traceback: the %run one,
578 # one inside bdb.py, and the command-line typed by the
583 # one inside bdb.py, and the command-line typed by the
579 # user (run by exec in pdb itself).
584 # user (run by exec in pdb itself).
580 self.shell.InteractiveTB(etype, value, tb, tb_offset=3)
585 self.shell.InteractiveTB(etype, value, tb, tb_offset=3)
581 else:
586 else:
582 if runner is None:
587 if runner is None:
583 runner = self.default_runner
588 runner = self.default_runner
584 if runner is None:
589 if runner is None:
585 runner = self.shell.safe_execfile
590 runner = self.shell.safe_execfile
586 if 't' in opts:
591 if 't' in opts:
587 # timed execution
592 # timed execution
588 try:
593 try:
589 nruns = int(opts['N'][0])
594 nruns = int(opts['N'][0])
590 if nruns < 1:
595 if nruns < 1:
591 error('Number of runs must be >=1')
596 error('Number of runs must be >=1')
592 return
597 return
593 except (KeyError):
598 except (KeyError):
594 nruns = 1
599 nruns = 1
595 twall0 = time.time()
600 twall0 = time.time()
596 if nruns == 1:
601 if nruns == 1:
597 t0 = clock2()
602 t0 = clock2()
598 runner(filename, prog_ns, prog_ns,
603 runner(filename, prog_ns, prog_ns,
599 exit_ignore=exit_ignore)
604 exit_ignore=exit_ignore)
600 t1 = clock2()
605 t1 = clock2()
601 t_usr = t1[0] - t0[0]
606 t_usr = t1[0] - t0[0]
602 t_sys = t1[1] - t0[1]
607 t_sys = t1[1] - t0[1]
603 print "\nIPython CPU timings (estimated):"
608 print "\nIPython CPU timings (estimated):"
604 print " User : %10.2f s." % t_usr
609 print " User : %10.2f s." % t_usr
605 print " System : %10.2f s." % t_sys
610 print " System : %10.2f s." % t_sys
606 else:
611 else:
607 runs = range(nruns)
612 runs = range(nruns)
608 t0 = clock2()
613 t0 = clock2()
609 for nr in runs:
614 for nr in runs:
610 runner(filename, prog_ns, prog_ns,
615 runner(filename, prog_ns, prog_ns,
611 exit_ignore=exit_ignore)
616 exit_ignore=exit_ignore)
612 t1 = clock2()
617 t1 = clock2()
613 t_usr = t1[0] - t0[0]
618 t_usr = t1[0] - t0[0]
614 t_sys = t1[1] - t0[1]
619 t_sys = t1[1] - t0[1]
615 print "\nIPython CPU timings (estimated):"
620 print "\nIPython CPU timings (estimated):"
616 print "Total runs performed:", nruns
621 print "Total runs performed:", nruns
617 print " Times : %10.2f %10.2f" % ('Total', 'Per run')
622 print " Times : %10.2f %10.2f" % ('Total', 'Per run')
618 print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns)
623 print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns)
619 print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns)
624 print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns)
620 twall1 = time.time()
625 twall1 = time.time()
621 print "Wall time: %10.2f s." % (twall1 - twall0)
626 print "Wall time: %10.2f s." % (twall1 - twall0)
622
627
623 else:
628 else:
624 # regular execution
629 # regular execution
625 runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore)
630 runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore)
626
631
627 if 'i' in opts:
632 if 'i' in opts:
628 self.shell.user_ns['__name__'] = __name__save
633 self.shell.user_ns['__name__'] = __name__save
629 else:
634 else:
630 # The shell MUST hold a reference to prog_ns so after %run
635 # The shell MUST hold a reference to prog_ns so after %run
631 # exits, the python deletion mechanism doesn't zero it out
636 # exits, the python deletion mechanism doesn't zero it out
632 # (leaving dangling references).
637 # (leaving dangling references).
633 self.shell.cache_main_mod(prog_ns, filename)
638 self.shell.cache_main_mod(prog_ns, filename)
634 # update IPython interactive namespace
639 # update IPython interactive namespace
635
640
636 # Some forms of read errors on the file may mean the
641 # Some forms of read errors on the file may mean the
637 # __name__ key was never set; using pop we don't have to
642 # __name__ key was never set; using pop we don't have to
638 # worry about a possible KeyError.
643 # worry about a possible KeyError.
639 prog_ns.pop('__name__', None)
644 prog_ns.pop('__name__', None)
640
645
641 self.shell.user_ns.update(prog_ns)
646 self.shell.user_ns.update(prog_ns)
642 finally:
647 finally:
643 # It's a bit of a mystery why, but __builtins__ can change from
648 # It's a bit of a mystery why, but __builtins__ can change from
644 # being a module to becoming a dict missing some key data after
649 # being a module to becoming a dict missing some key data after
645 # %run. As best I can see, this is NOT something IPython is doing
650 # %run. As best I can see, this is NOT something IPython is doing
646 # at all, and similar problems have been reported before:
651 # at all, and similar problems have been reported before:
647 # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
652 # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
648 # Since this seems to be done by the interpreter itself, the best
653 # Since this seems to be done by the interpreter itself, the best
649 # we can do is to at least restore __builtins__ for the user on
654 # we can do is to at least restore __builtins__ for the user on
650 # exit.
655 # exit.
651 self.shell.user_ns['__builtins__'] = builtin_mod
656 self.shell.user_ns['__builtins__'] = builtin_mod
652
657
653 # Ensure key global structures are restored
658 # Ensure key global structures are restored
654 sys.argv = save_argv
659 sys.argv = save_argv
655 if restore_main:
660 if restore_main:
656 sys.modules['__main__'] = restore_main
661 sys.modules['__main__'] = restore_main
657 else:
662 else:
658 # Remove from sys.modules the reference to main_mod we'd
663 # Remove from sys.modules the reference to main_mod we'd
659 # added. Otherwise it will trap references to objects
664 # added. Otherwise it will trap references to objects
660 # contained therein.
665 # contained therein.
661 del sys.modules[main_mod_name]
666 del sys.modules[main_mod_name]
662
667
663 return stats
668 return stats
664
669
665 @skip_doctest
670 @skip_doctest
666 @line_cell_magic
671 @line_cell_magic
667 def timeit(self, line='', cell=None):
672 def timeit(self, line='', cell=None):
668 """Time execution of a Python statement or expression
673 """Time execution of a Python statement or expression
669
674
670 Usage, in line mode:
675 Usage, in line mode:
671 %timeit [-n<N> -r<R> [-t|-c]] statement
676 %timeit [-n<N> -r<R> [-t|-c]] statement
672 or in cell mode:
677 or in cell mode:
673 %%timeit [-n<N> -r<R> [-t|-c]] setup_code
678 %%timeit [-n<N> -r<R> [-t|-c]] setup_code
674 code
679 code
675 code...
680 code...
676
681
677 Time execution of a Python statement or expression using the timeit
682 Time execution of a Python statement or expression using the timeit
678 module. This function can be used both as a line and cell magic:
683 module. This function can be used both as a line and cell magic:
679
684
680 - In line mode you can time a single-line statement (though multiple
685 - In line mode you can time a single-line statement (though multiple
681 ones can be chained with using semicolons).
686 ones can be chained with using semicolons).
682
687
683 - In cell mode, the statement in the first line is used as setup code
688 - In cell mode, the statement in the first line is used as setup code
684 (executed but not timed) and the body of the cell is timed. The cell
689 (executed but not timed) and the body of the cell is timed. The cell
685 body has access to any variables created in the setup code.
690 body has access to any variables created in the setup code.
686
691
687 Options:
692 Options:
688 -n<N>: execute the given statement <N> times in a loop. If this value
693 -n<N>: execute the given statement <N> times in a loop. If this value
689 is not given, a fitting value is chosen.
694 is not given, a fitting value is chosen.
690
695
691 -r<R>: repeat the loop iteration <R> times and take the best result.
696 -r<R>: repeat the loop iteration <R> times and take the best result.
692 Default: 3
697 Default: 3
693
698
694 -t: use time.time to measure the time, which is the default on Unix.
699 -t: use time.time to measure the time, which is the default on Unix.
695 This function measures wall time.
700 This function measures wall time.
696
701
697 -c: use time.clock to measure the time, which is the default on
702 -c: use time.clock to measure the time, which is the default on
698 Windows and measures wall time. On Unix, resource.getrusage is used
703 Windows and measures wall time. On Unix, resource.getrusage is used
699 instead and returns the CPU user time.
704 instead and returns the CPU user time.
700
705
701 -p<P>: use a precision of <P> digits to display the timing result.
706 -p<P>: use a precision of <P> digits to display the timing result.
702 Default: 3
707 Default: 3
703
708
704
709
705 Examples
710 Examples
706 --------
711 --------
707 ::
712 ::
708
713
709 In [1]: %timeit pass
714 In [1]: %timeit pass
710 10000000 loops, best of 3: 53.3 ns per loop
715 10000000 loops, best of 3: 53.3 ns per loop
711
716
712 In [2]: u = None
717 In [2]: u = None
713
718
714 In [3]: %timeit u is None
719 In [3]: %timeit u is None
715 10000000 loops, best of 3: 184 ns per loop
720 10000000 loops, best of 3: 184 ns per loop
716
721
717 In [4]: %timeit -r 4 u == None
722 In [4]: %timeit -r 4 u == None
718 1000000 loops, best of 4: 242 ns per loop
723 1000000 loops, best of 4: 242 ns per loop
719
724
720 In [5]: import time
725 In [5]: import time
721
726
722 In [6]: %timeit -n1 time.sleep(2)
727 In [6]: %timeit -n1 time.sleep(2)
723 1 loops, best of 3: 2 s per loop
728 1 loops, best of 3: 2 s per loop
724
729
725
730
726 The times reported by %timeit will be slightly higher than those
731 The times reported by %timeit will be slightly higher than those
727 reported by the timeit.py script when variables are accessed. This is
732 reported by the timeit.py script when variables are accessed. This is
728 due to the fact that %timeit executes the statement in the namespace
733 due to the fact that %timeit executes the statement in the namespace
729 of the shell, compared with timeit.py, which uses a single setup
734 of the shell, compared with timeit.py, which uses a single setup
730 statement to import function or create variables. Generally, the bias
735 statement to import function or create variables. Generally, the bias
731 does not matter as long as results from timeit.py are not mixed with
736 does not matter as long as results from timeit.py are not mixed with
732 those from %timeit."""
737 those from %timeit."""
733
738
734 import timeit
739 import timeit
735 import math
740 import math
736
741
737 # XXX: Unfortunately the unicode 'micro' symbol can cause problems in
742 # XXX: Unfortunately the unicode 'micro' symbol can cause problems in
738 # certain terminals. Until we figure out a robust way of
743 # certain terminals. Until we figure out a robust way of
739 # auto-detecting if the terminal can deal with it, use plain 'us' for
744 # auto-detecting if the terminal can deal with it, use plain 'us' for
740 # microseconds. I am really NOT happy about disabling the proper
745 # microseconds. I am really NOT happy about disabling the proper
741 # 'micro' prefix, but crashing is worse... If anyone knows what the
746 # 'micro' prefix, but crashing is worse... If anyone knows what the
742 # right solution for this is, I'm all ears...
747 # right solution for this is, I'm all ears...
743 #
748 #
744 # Note: using
749 # Note: using
745 #
750 #
746 # s = u'\xb5'
751 # s = u'\xb5'
747 # s.encode(sys.getdefaultencoding())
752 # s.encode(sys.getdefaultencoding())
748 #
753 #
749 # is not sufficient, as I've seen terminals where that fails but
754 # is not sufficient, as I've seen terminals where that fails but
750 # print s
755 # print s
751 #
756 #
752 # succeeds
757 # succeeds
753 #
758 #
754 # See bug: https://bugs.launchpad.net/ipython/+bug/348466
759 # See bug: https://bugs.launchpad.net/ipython/+bug/348466
755
760
756 #units = [u"s", u"ms",u'\xb5',"ns"]
761 #units = [u"s", u"ms",u'\xb5',"ns"]
757 units = [u"s", u"ms",u'us',"ns"]
762 units = [u"s", u"ms",u'us',"ns"]
758
763
759 scaling = [1, 1e3, 1e6, 1e9]
764 scaling = [1, 1e3, 1e6, 1e9]
760
765
761 opts, stmt = self.parse_options(line,'n:r:tcp:',
766 opts, stmt = self.parse_options(line,'n:r:tcp:',
762 posix=False, strict=False)
767 posix=False, strict=False)
763 if stmt == "" and cell is None:
768 if stmt == "" and cell is None:
764 return
769 return
765 timefunc = timeit.default_timer
770 timefunc = timeit.default_timer
766 number = int(getattr(opts, "n", 0))
771 number = int(getattr(opts, "n", 0))
767 repeat = int(getattr(opts, "r", timeit.default_repeat))
772 repeat = int(getattr(opts, "r", timeit.default_repeat))
768 precision = int(getattr(opts, "p", 3))
773 precision = int(getattr(opts, "p", 3))
769 if hasattr(opts, "t"):
774 if hasattr(opts, "t"):
770 timefunc = time.time
775 timefunc = time.time
771 if hasattr(opts, "c"):
776 if hasattr(opts, "c"):
772 timefunc = clock
777 timefunc = clock
773
778
774 timer = timeit.Timer(timer=timefunc)
779 timer = timeit.Timer(timer=timefunc)
775 # this code has tight coupling to the inner workings of timeit.Timer,
780 # this code has tight coupling to the inner workings of timeit.Timer,
776 # but is there a better way to achieve that the code stmt has access
781 # but is there a better way to achieve that the code stmt has access
777 # to the shell namespace?
782 # to the shell namespace?
778 transform = self.shell.input_splitter.transform_cell
783 transform = self.shell.input_splitter.transform_cell
779 if cell is None:
784 if cell is None:
780 # called as line magic
785 # called as line magic
781 setup = 'pass'
786 setup = 'pass'
782 stmt = timeit.reindent(transform(stmt), 8)
787 stmt = timeit.reindent(transform(stmt), 8)
783 else:
788 else:
784 setup = timeit.reindent(transform(stmt), 4)
789 setup = timeit.reindent(transform(stmt), 4)
785 stmt = timeit.reindent(transform(cell), 8)
790 stmt = timeit.reindent(transform(cell), 8)
786
791
787 # From Python 3.3, this template uses new-style string formatting.
792 # From Python 3.3, this template uses new-style string formatting.
788 if sys.version_info >= (3, 3):
793 if sys.version_info >= (3, 3):
789 src = timeit.template.format(stmt=stmt, setup=setup)
794 src = timeit.template.format(stmt=stmt, setup=setup)
790 else:
795 else:
791 src = timeit.template % dict(stmt=stmt, setup=setup)
796 src = timeit.template % dict(stmt=stmt, setup=setup)
792
797
793 # Track compilation time so it can be reported if too long
798 # Track compilation time so it can be reported if too long
794 # Minimum time above which compilation time will be reported
799 # Minimum time above which compilation time will be reported
795 tc_min = 0.1
800 tc_min = 0.1
796
801
797 t0 = clock()
802 t0 = clock()
798 code = compile(src, "<magic-timeit>", "exec")
803 code = compile(src, "<magic-timeit>", "exec")
799 tc = clock()-t0
804 tc = clock()-t0
800
805
801 ns = {}
806 ns = {}
802 exec code in self.shell.user_ns, ns
807 exec code in self.shell.user_ns, ns
803 timer.inner = ns["inner"]
808 timer.inner = ns["inner"]
804
809
805 if number == 0:
810 if number == 0:
806 # determine number so that 0.2 <= total time < 2.0
811 # determine number so that 0.2 <= total time < 2.0
807 number = 1
812 number = 1
808 for i in range(1, 10):
813 for i in range(1, 10):
809 if timer.timeit(number) >= 0.2:
814 if timer.timeit(number) >= 0.2:
810 break
815 break
811 number *= 10
816 number *= 10
812
817
813 best = min(timer.repeat(repeat, number)) / number
818 best = min(timer.repeat(repeat, number)) / number
814
819
815 if best > 0.0 and best < 1000.0:
820 if best > 0.0 and best < 1000.0:
816 order = min(-int(math.floor(math.log10(best)) // 3), 3)
821 order = min(-int(math.floor(math.log10(best)) // 3), 3)
817 elif best >= 1000.0:
822 elif best >= 1000.0:
818 order = 0
823 order = 0
819 else:
824 else:
820 order = 3
825 order = 3
821 print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat,
826 print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat,
822 precision,
827 precision,
823 best * scaling[order],
828 best * scaling[order],
824 units[order])
829 units[order])
825 if tc > tc_min:
830 if tc > tc_min:
826 print "Compiler time: %.2f s" % tc
831 print "Compiler time: %.2f s" % tc
827
832
828 @skip_doctest
833 @skip_doctest
829 @needs_local_scope
834 @needs_local_scope
830 @line_magic
835 @line_magic
831 def time(self,parameter_s, local_ns=None):
836 def time(self,parameter_s, local_ns=None):
832 """Time execution of a Python statement or expression.
837 """Time execution of a Python statement or expression.
833
838
834 The CPU and wall clock times are printed, and the value of the
839 The CPU and wall clock times are printed, and the value of the
835 expression (if any) is returned. Note that under Win32, system time
840 expression (if any) is returned. Note that under Win32, system time
836 is always reported as 0, since it can not be measured.
841 is always reported as 0, since it can not be measured.
837
842
838 This function provides very basic timing functionality. In Python
843 This function provides very basic timing functionality. In Python
839 2.3, the timeit module offers more control and sophistication, so this
844 2.3, the timeit module offers more control and sophistication, so this
840 could be rewritten to use it (patches welcome).
845 could be rewritten to use it (patches welcome).
841
846
842 Examples
847 Examples
843 --------
848 --------
844 ::
849 ::
845
850
846 In [1]: time 2**128
851 In [1]: time 2**128
847 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
852 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
848 Wall time: 0.00
853 Wall time: 0.00
849 Out[1]: 340282366920938463463374607431768211456L
854 Out[1]: 340282366920938463463374607431768211456L
850
855
851 In [2]: n = 1000000
856 In [2]: n = 1000000
852
857
853 In [3]: time sum(range(n))
858 In [3]: time sum(range(n))
854 CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s
859 CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s
855 Wall time: 1.37
860 Wall time: 1.37
856 Out[3]: 499999500000L
861 Out[3]: 499999500000L
857
862
858 In [4]: time print 'hello world'
863 In [4]: time print 'hello world'
859 hello world
864 hello world
860 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
865 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
861 Wall time: 0.00
866 Wall time: 0.00
862
867
863 Note that the time needed by Python to compile the given expression
868 Note that the time needed by Python to compile the given expression
864 will be reported if it is more than 0.1s. In this example, the
869 will be reported if it is more than 0.1s. In this example, the
865 actual exponentiation is done by Python at compilation time, so while
870 actual exponentiation is done by Python at compilation time, so while
866 the expression can take a noticeable amount of time to compute, that
871 the expression can take a noticeable amount of time to compute, that
867 time is purely due to the compilation:
872 time is purely due to the compilation:
868
873
869 In [5]: time 3**9999;
874 In [5]: time 3**9999;
870 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
875 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
871 Wall time: 0.00 s
876 Wall time: 0.00 s
872
877
873 In [6]: time 3**999999;
878 In [6]: time 3**999999;
874 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
879 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
875 Wall time: 0.00 s
880 Wall time: 0.00 s
876 Compiler : 0.78 s
881 Compiler : 0.78 s
877 """
882 """
878
883
879 # fail immediately if the given expression can't be compiled
884 # fail immediately if the given expression can't be compiled
880
885
881 expr = self.shell.prefilter(parameter_s,False)
886 expr = self.shell.prefilter(parameter_s,False)
882
887
883 # Minimum time above which compilation time will be reported
888 # Minimum time above which compilation time will be reported
884 tc_min = 0.1
889 tc_min = 0.1
885
890
886 try:
891 try:
887 mode = 'eval'
892 mode = 'eval'
888 t0 = clock()
893 t0 = clock()
889 code = compile(expr,'<timed eval>',mode)
894 code = compile(expr,'<timed eval>',mode)
890 tc = clock()-t0
895 tc = clock()-t0
891 except SyntaxError:
896 except SyntaxError:
892 mode = 'exec'
897 mode = 'exec'
893 t0 = clock()
898 t0 = clock()
894 code = compile(expr,'<timed exec>',mode)
899 code = compile(expr,'<timed exec>',mode)
895 tc = clock()-t0
900 tc = clock()-t0
896 # skew measurement as little as possible
901 # skew measurement as little as possible
897 glob = self.shell.user_ns
902 glob = self.shell.user_ns
898 wtime = time.time
903 wtime = time.time
899 # time execution
904 # time execution
900 wall_st = wtime()
905 wall_st = wtime()
901 if mode=='eval':
906 if mode=='eval':
902 st = clock2()
907 st = clock2()
903 out = eval(code, glob, local_ns)
908 out = eval(code, glob, local_ns)
904 end = clock2()
909 end = clock2()
905 else:
910 else:
906 st = clock2()
911 st = clock2()
907 exec code in glob, local_ns
912 exec code in glob, local_ns
908 end = clock2()
913 end = clock2()
909 out = None
914 out = None
910 wall_end = wtime()
915 wall_end = wtime()
911 # Compute actual times and report
916 # Compute actual times and report
912 wall_time = wall_end-wall_st
917 wall_time = wall_end-wall_st
913 cpu_user = end[0]-st[0]
918 cpu_user = end[0]-st[0]
914 cpu_sys = end[1]-st[1]
919 cpu_sys = end[1]-st[1]
915 cpu_tot = cpu_user+cpu_sys
920 cpu_tot = cpu_user+cpu_sys
916 print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \
921 print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \
917 (cpu_user,cpu_sys,cpu_tot)
922 (cpu_user,cpu_sys,cpu_tot)
918 print "Wall time: %.2f s" % wall_time
923 print "Wall time: %.2f s" % wall_time
919 if tc > tc_min:
924 if tc > tc_min:
920 print "Compiler : %.2f s" % tc
925 print "Compiler : %.2f s" % tc
921 return out
926 return out
922
927
923 @skip_doctest
928 @skip_doctest
924 @line_magic
929 @line_magic
925 def macro(self, parameter_s=''):
930 def macro(self, parameter_s=''):
926 """Define a macro for future re-execution. It accepts ranges of history,
931 """Define a macro for future re-execution. It accepts ranges of history,
927 filenames or string objects.
932 filenames or string objects.
928
933
929 Usage:\\
934 Usage:\\
930 %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
935 %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
931
936
932 Options:
937 Options:
933
938
934 -r: use 'raw' input. By default, the 'processed' history is used,
939 -r: use 'raw' input. By default, the 'processed' history is used,
935 so that magics are loaded in their transformed version to valid
940 so that magics are loaded in their transformed version to valid
936 Python. If this option is given, the raw input as typed as the
941 Python. If this option is given, the raw input as typed as the
937 command line is used instead.
942 command line is used instead.
938
943
939 This will define a global variable called `name` which is a string
944 This will define a global variable called `name` which is a string
940 made of joining the slices and lines you specify (n1,n2,... numbers
945 made of joining the slices and lines you specify (n1,n2,... numbers
941 above) from your input history into a single string. This variable
946 above) from your input history into a single string. This variable
942 acts like an automatic function which re-executes those lines as if
947 acts like an automatic function which re-executes those lines as if
943 you had typed them. You just type 'name' at the prompt and the code
948 you had typed them. You just type 'name' at the prompt and the code
944 executes.
949 executes.
945
950
946 The syntax for indicating input ranges is described in %history.
951 The syntax for indicating input ranges is described in %history.
947
952
948 Note: as a 'hidden' feature, you can also use traditional python slice
953 Note: as a 'hidden' feature, you can also use traditional python slice
949 notation, where N:M means numbers N through M-1.
954 notation, where N:M means numbers N through M-1.
950
955
951 For example, if your history contains (%hist prints it)::
956 For example, if your history contains (%hist prints it)::
952
957
953 44: x=1
958 44: x=1
954 45: y=3
959 45: y=3
955 46: z=x+y
960 46: z=x+y
956 47: print x
961 47: print x
957 48: a=5
962 48: a=5
958 49: print 'x',x,'y',y
963 49: print 'x',x,'y',y
959
964
960 you can create a macro with lines 44 through 47 (included) and line 49
965 you can create a macro with lines 44 through 47 (included) and line 49
961 called my_macro with::
966 called my_macro with::
962
967
963 In [55]: %macro my_macro 44-47 49
968 In [55]: %macro my_macro 44-47 49
964
969
965 Now, typing `my_macro` (without quotes) will re-execute all this code
970 Now, typing `my_macro` (without quotes) will re-execute all this code
966 in one pass.
971 in one pass.
967
972
968 You don't need to give the line-numbers in order, and any given line
973 You don't need to give the line-numbers in order, and any given line
969 number can appear multiple times. You can assemble macros with any
974 number can appear multiple times. You can assemble macros with any
970 lines from your input history in any order.
975 lines from your input history in any order.
971
976
972 The macro is a simple object which holds its value in an attribute,
977 The macro is a simple object which holds its value in an attribute,
973 but IPython's display system checks for macros and executes them as
978 but IPython's display system checks for macros and executes them as
974 code instead of printing them when you type their name.
979 code instead of printing them when you type their name.
975
980
976 You can view a macro's contents by explicitly printing it with::
981 You can view a macro's contents by explicitly printing it with::
977
982
978 print macro_name
983 print macro_name
979
984
980 """
985 """
981 opts,args = self.parse_options(parameter_s,'r',mode='list')
986 opts,args = self.parse_options(parameter_s,'r',mode='list')
982 if not args: # List existing macros
987 if not args: # List existing macros
983 return sorted(k for k,v in self.shell.user_ns.iteritems() if\
988 return sorted(k for k,v in self.shell.user_ns.iteritems() if\
984 isinstance(v, Macro))
989 isinstance(v, Macro))
985 if len(args) == 1:
990 if len(args) == 1:
986 raise UsageError(
991 raise UsageError(
987 "%macro insufficient args; usage '%macro name n1-n2 n3-4...")
992 "%macro insufficient args; usage '%macro name n1-n2 n3-4...")
988 name, codefrom = args[0], " ".join(args[1:])
993 name, codefrom = args[0], " ".join(args[1:])
989
994
990 #print 'rng',ranges # dbg
995 #print 'rng',ranges # dbg
991 try:
996 try:
992 lines = self.shell.find_user_code(codefrom, 'r' in opts)
997 lines = self.shell.find_user_code(codefrom, 'r' in opts)
993 except (ValueError, TypeError) as e:
998 except (ValueError, TypeError) as e:
994 print e.args[0]
999 print e.args[0]
995 return
1000 return
996 macro = Macro(lines)
1001 macro = Macro(lines)
997 self.shell.define_macro(name, macro)
1002 self.shell.define_macro(name, macro)
998 print 'Macro `%s` created. To execute, type its name (without quotes).' % name
1003 print 'Macro `%s` created. To execute, type its name (without quotes).' % name
999 print '=== Macro contents: ==='
1004 print '=== Macro contents: ==='
1000 print macro,
1005 print macro,
1001
1006
1002 @magic_arguments.magic_arguments()
1007 @magic_arguments.magic_arguments()
1003 @magic_arguments.argument('output', type=str, default='', nargs='?',
1008 @magic_arguments.argument('output', type=str, default='', nargs='?',
1004 help="""The name of the variable in which to store output.
1009 help="""The name of the variable in which to store output.
1005 This is a utils.io.CapturedIO object with stdout/err attributes
1010 This is a utils.io.CapturedIO object with stdout/err attributes
1006 for the text of the captured output.
1011 for the text of the captured output.
1007
1012
1008 CapturedOutput also has a show() method for displaying the output,
1013 CapturedOutput also has a show() method for displaying the output,
1009 and __call__ as well, so you can use that to quickly display the
1014 and __call__ as well, so you can use that to quickly display the
1010 output.
1015 output.
1011
1016
1012 If unspecified, captured output is discarded.
1017 If unspecified, captured output is discarded.
1013 """
1018 """
1014 )
1019 )
1015 @magic_arguments.argument('--no-stderr', action="store_true",
1020 @magic_arguments.argument('--no-stderr', action="store_true",
1016 help="""Don't capture stderr."""
1021 help="""Don't capture stderr."""
1017 )
1022 )
1018 @magic_arguments.argument('--no-stdout', action="store_true",
1023 @magic_arguments.argument('--no-stdout', action="store_true",
1019 help="""Don't capture stdout."""
1024 help="""Don't capture stdout."""
1020 )
1025 )
1021 @cell_magic
1026 @cell_magic
1022 def capture(self, line, cell):
1027 def capture(self, line, cell):
1023 """run the cell, capturing stdout/err"""
1028 """run the cell, capturing stdout/err"""
1024 args = magic_arguments.parse_argstring(self.capture, line)
1029 args = magic_arguments.parse_argstring(self.capture, line)
1025 out = not args.no_stdout
1030 out = not args.no_stdout
1026 err = not args.no_stderr
1031 err = not args.no_stderr
1027 with capture_output(out, err) as io:
1032 with capture_output(out, err) as io:
1028 self.shell.run_cell(cell)
1033 self.shell.run_cell(cell)
1029 if args.output:
1034 if args.output:
1030 self.shell.user_ns[args.output] = io
1035 self.shell.user_ns[args.output] = io
@@ -1,76 +1,92 b''
1 """Implementation of magic functions for the extension machinery.
1 """Implementation of magic functions for the extension machinery.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012 The IPython Development Team.
4 # Copyright (c) 2012 The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # Stdlib
15 # Stdlib
16 import os
16 import os
17
17
18 # Our own packages
18 # Our own packages
19 from IPython.core.error import UsageError
19 from IPython.core.error import UsageError
20 from IPython.core.magic import Magics, magics_class, line_magic
20 from IPython.core.magic import Magics, magics_class, line_magic
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Magic implementation classes
23 # Magic implementation classes
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 @magics_class
26 @magics_class
27 class ExtensionMagics(Magics):
27 class ExtensionMagics(Magics):
28 """Magics to manage the IPython extensions system."""
28 """Magics to manage the IPython extensions system."""
29
29
30 @line_magic
30 @line_magic
31 def install_ext(self, parameter_s=''):
31 def install_ext(self, parameter_s=''):
32 """Download and install an extension from a URL, e.g.::
32 """Download and install an extension from a URL, e.g.::
33
33
34 %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py
34 %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py
35
35
36 The URL should point to an importable Python module - either a .py file
36 The URL should point to an importable Python module - either a .py file
37 or a .zip file.
37 or a .zip file.
38
38
39 Parameters:
39 Parameters:
40
40
41 -n filename : Specify a name for the file, rather than taking it from
41 -n filename : Specify a name for the file, rather than taking it from
42 the URL.
42 the URL.
43 """
43 """
44 opts, args = self.parse_options(parameter_s, 'n:')
44 opts, args = self.parse_options(parameter_s, 'n:')
45 try:
45 try:
46 filename = self.shell.extension_manager.install_extension(args,
46 filename = self.shell.extension_manager.install_extension(args,
47 opts.get('n'))
47 opts.get('n'))
48 except ValueError as e:
48 except ValueError as e:
49 print e
49 print e
50 return
50 return
51
51
52 filename = os.path.basename(filename)
52 filename = os.path.basename(filename)
53 print "Installed %s. To use it, type:" % filename
53 print "Installed %s. To use it, type:" % filename
54 print " %%load_ext %s" % os.path.splitext(filename)[0]
54 print " %%load_ext %s" % os.path.splitext(filename)[0]
55
55
56
56
57 @line_magic
57 @line_magic
58 def load_ext(self, module_str):
58 def load_ext(self, module_str):
59 """Load an IPython extension by its module name."""
59 """Load an IPython extension by its module name."""
60 if not module_str:
60 if not module_str:
61 raise UsageError('Missing module name.')
61 raise UsageError('Missing module name.')
62 return self.shell.extension_manager.load_extension(module_str)
62 res = self.shell.extension_manager.load_extension(module_str)
63
64 if res == 'already loaded':
65 print "The %s extension is already loaded. To reload it, use:" % module_str
66 print " %reload_ext", module_str
67 elif res == 'no load function':
68 print "The %s module is not an IPython extension." % module_str
63
69
64 @line_magic
70 @line_magic
65 def unload_ext(self, module_str):
71 def unload_ext(self, module_str):
66 """Unload an IPython extension by its module name."""
72 """Unload an IPython extension by its module name.
73
74 Not all extensions can be unloaded, only those which define an
75 ``unload_ipython_extension`` function.
76 """
67 if not module_str:
77 if not module_str:
68 raise UsageError('Missing module name.')
78 raise UsageError('Missing module name.')
69 self.shell.extension_manager.unload_extension(module_str)
79
80 res = self.shell.extension_manager.unload_extension(module_str)
81
82 if res == 'no unload function':
83 print "The %s extension doesn't define how to unload it." % module_str
84 elif res == "not loaded":
85 print "The %s extension is not loaded." % module_str
70
86
71 @line_magic
87 @line_magic
72 def reload_ext(self, module_str):
88 def reload_ext(self, module_str):
73 """Reload an IPython extension by its module name."""
89 """Reload an IPython extension by its module name."""
74 if not module_str:
90 if not module_str:
75 raise UsageError('Missing module name.')
91 raise UsageError('Missing module name.')
76 self.shell.extension_manager.reload_extension(module_str)
92 self.shell.extension_manager.reload_extension(module_str)
@@ -1,289 +1,307 b''
1 """Implementation of magic functions related to History.
1 """Implementation of magic functions related to History.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012, IPython Development Team.
4 # Copyright (c) 2012, IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 from __future__ import print_function
14 from __future__ import print_function
15
15
16 # Stdlib
16 # Stdlib
17 import os
17 import os
18 from io import open as io_open
18 from io import open as io_open
19 from IPython.external.argparse import Action
19
20
20 # Our own packages
21 # Our own packages
21 from IPython.core.error import StdinNotImplementedError
22 from IPython.core.error import StdinNotImplementedError
22 from IPython.core.magic import Magics, magics_class, line_magic
23 from IPython.core.magic import Magics, magics_class, line_magic
24 from IPython.core.magic_arguments import (argument, magic_arguments,
25 parse_argstring)
23 from IPython.testing.skipdoctest import skip_doctest
26 from IPython.testing.skipdoctest import skip_doctest
24 from IPython.utils import io
27 from IPython.utils import io
25
28
26 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
27 # Magics class implementation
30 # Magics class implementation
28 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
29
32
33
34 _unspecified = object()
35
36
30 @magics_class
37 @magics_class
31 class HistoryMagics(Magics):
38 class HistoryMagics(Magics):
32
39
40 @magic_arguments()
41 @argument(
42 '-n', dest='print_nums', action='store_true', default=False,
43 help="""
44 print line numbers for each input.
45 This feature is only available if numbered prompts are in use.
46 """)
47 @argument(
48 '-o', dest='get_output', action='store_true', default=False,
49 help="also print outputs for each input.")
50 @argument(
51 '-p', dest='pyprompts', action='store_true', default=False,
52 help="""
53 print classic '>>>' python prompts before each input.
54 This is useful for making documentation, and in conjunction
55 with -o, for producing doctest-ready output.
56 """)
57 @argument(
58 '-t', dest='raw', action='store_false', default=True,
59 help="""
60 print the 'translated' history, as IPython understands it.
61 IPython filters your input and converts it all into valid Python
62 source before executing it (things like magics or aliases are turned
63 into function calls, for example). With this option, you'll see the
64 native history instead of the user-entered version: '%%cd /' will be
65 seen as 'get_ipython().magic("%%cd /")' instead of '%%cd /'.
66 """)
67 @argument(
68 '-f', dest='filename',
69 help="""
70 FILENAME: instead of printing the output to the screen, redirect
71 it to the given file. The file is always overwritten, though *when
72 it can*, IPython asks for confirmation first. In particular, running
73 the command 'history -f FILENAME' from the IPython Notebook
74 interface will replace FILENAME even if it already exists *without*
75 confirmation.
76 """)
77 @argument(
78 '-g', dest='pattern', nargs='*', default=None,
79 help="""
80 treat the arg as a glob pattern to search for in (full) history.
81 This includes the saved history (almost all commands ever written).
82 The pattern may contain '?' to match one unknown character and '*'
83 to match any number of unknown characters. Use '%%hist -g' to show
84 full saved history (may be very long).
85 """)
86 @argument(
87 '-l', dest='limit', type=int, nargs='?', default=_unspecified,
88 help="""
89 get the last n lines from all sessions. Specify n as a single
90 arg, or the default is the last 10 lines.
91 """)
92 @argument('range', nargs='*')
33 @skip_doctest
93 @skip_doctest
34 @line_magic
94 @line_magic
35 def history(self, parameter_s = ''):
95 def history(self, parameter_s = ''):
36 """Print input history (_i<n> variables), with most recent last.
96 """Print input history (_i<n> variables), with most recent last.
37
97
38 %history [-o -p -t -n] [-f filename] [range | -g pattern | -l number]
39
40 By default, input history is printed without line numbers so it can be
98 By default, input history is printed without line numbers so it can be
41 directly pasted into an editor. Use -n to show them.
99 directly pasted into an editor. Use -n to show them.
42
100
43 By default, all input history from the current session is displayed.
101 By default, all input history from the current session is displayed.
44 Ranges of history can be indicated using the syntax:
102 Ranges of history can be indicated using the syntax:
45 4 : Line 4, current session
103 4 : Line 4, current session
46 4-6 : Lines 4-6, current session
104 4-6 : Lines 4-6, current session
47 243/1-5: Lines 1-5, session 243
105 243/1-5: Lines 1-5, session 243
48 ~2/7 : Line 7, session 2 before current
106 ~2/7 : Line 7, session 2 before current
49 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
107 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
50 of 6 sessions ago.
108 of 6 sessions ago.
51 Multiple ranges can be entered, separated by spaces
109 Multiple ranges can be entered, separated by spaces
52
110
53 The same syntax is used by %macro, %save, %edit, %rerun
111 The same syntax is used by %macro, %save, %edit, %rerun
54
112
55 Options:
56
57 -n: print line numbers for each input.
58 This feature is only available if numbered prompts are in use.
59
60 -o: also print outputs for each input.
61
62 -p: print classic '>>>' python prompts before each input. This is
63 useful for making documentation, and in conjunction with -o, for
64 producing doctest-ready output.
65
66 -r: (default) print the 'raw' history, i.e. the actual commands you
67 typed.
68
69 -t: print the 'translated' history, as IPython understands it.
70 IPython filters your input and converts it all into valid Python
71 source before executing it (things like magics or aliases are turned
72 into function calls, for example). With this option, you'll see the
73 native history instead of the user-entered version: '%cd /' will be
74 seen as 'get_ipython().magic("%cd /")' instead of '%cd /'.
75
76 -g: treat the arg as a pattern to grep for in (full) history.
77 This includes the saved history (almost all commands ever written).
78 The pattern may contain '?' to match one unknown character and '*'
79 to match any number of unknown characters. Use '%hist -g' to show
80 full saved history (may be very long).
81
82 -l: get the last n lines from all sessions. Specify n as a single
83 arg, or the default is the last 10 lines.
84
85 -f FILENAME: instead of printing the output to the screen, redirect
86 it to the given file. The file is always overwritten, though *when
87 it can*, IPython asks for confirmation first. In particular, running
88 the command 'history -f FILENAME' from the IPython Notebook
89 interface will replace FILENAME even if it already exists *without*
90 confirmation.
91
92 Examples
113 Examples
93 --------
114 --------
94 ::
115 ::
95
116
96 In [6]: %history -n 4-6
117 In [6]: %history -n 4-6
97 4:a = 12
118 4:a = 12
98 5:print a**2
119 5:print a**2
99 6:%history -n 4-6
120 6:%history -n 4-6
100
121
101 """
122 """
102
123
103 if not self.shell.displayhook.do_full_cache:
124 args = parse_argstring(self.history, parameter_s)
104 print('This feature is only available if numbered prompts '
105 'are in use.')
106 return
107 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
108
125
109 # For brevity
126 # For brevity
110 history_manager = self.shell.history_manager
127 history_manager = self.shell.history_manager
111
128
112 def _format_lineno(session, line):
129 def _format_lineno(session, line):
113 """Helper function to format line numbers properly."""
130 """Helper function to format line numbers properly."""
114 if session in (0, history_manager.session_number):
131 if session in (0, history_manager.session_number):
115 return str(line)
132 return str(line)
116 return "%s/%s" % (session, line)
133 return "%s/%s" % (session, line)
117
134
118 # Check if output to specific file was requested.
135 # Check if output to specific file was requested.
119 try:
136 outfname = args.filename
120 outfname = opts['f']
137 if not outfname:
121 except KeyError:
122 outfile = io.stdout # default
138 outfile = io.stdout # default
123 # We don't want to close stdout at the end!
139 # We don't want to close stdout at the end!
124 close_at_end = False
140 close_at_end = False
125 else:
141 else:
126 if os.path.exists(outfname):
142 if os.path.exists(outfname):
127 try:
143 try:
128 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
144 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
129 except StdinNotImplementedError:
145 except StdinNotImplementedError:
130 ans = True
146 ans = True
131 if not ans:
147 if not ans:
132 print('Aborting.')
148 print('Aborting.')
133 return
149 return
134 print("Overwriting file.")
150 print("Overwriting file.")
135 outfile = io_open(outfname, 'w', encoding='utf-8')
151 outfile = io_open(outfname, 'w', encoding='utf-8')
136 close_at_end = True
152 close_at_end = True
137
153
138 print_nums = 'n' in opts
154 print_nums = args.print_nums
139 get_output = 'o' in opts
155 get_output = args.get_output
140 pyprompts = 'p' in opts
156 pyprompts = args.pyprompts
141 # Raw history is the default
157 raw = args.raw
142 raw = not('t' in opts)
143
158
144 pattern = None
159 pattern = None
160 limit = None if args.limit is _unspecified else args.limit
145
161
146 if 'g' in opts: # Glob search
162 if args.pattern is not None:
147 pattern = "*" + args + "*" if args else "*"
163 if args.pattern:
148 hist = history_manager.search(pattern, raw=raw, output=get_output)
164 pattern = "*" + " ".join(args.pattern) + "*"
165 else:
166 pattern = "*"
167 hist = history_manager.search(pattern, raw=raw, output=get_output,
168 n=limit)
149 print_nums = True
169 print_nums = True
150 elif 'l' in opts: # Get 'tail'
170 elif args.limit is not _unspecified:
151 try:
171 n = 10 if limit is None else limit
152 n = int(args)
153 except (ValueError, IndexError):
154 n = 10
155 hist = history_manager.get_tail(n, raw=raw, output=get_output)
172 hist = history_manager.get_tail(n, raw=raw, output=get_output)
156 else:
173 else:
157 if args: # Get history by ranges
174 if args.range: # Get history by ranges
158 hist = history_manager.get_range_by_str(args, raw, get_output)
175 hist = history_manager.get_range_by_str(" ".join(args.range),
176 raw, get_output)
159 else: # Just get history for the current session
177 else: # Just get history for the current session
160 hist = history_manager.get_range(raw=raw, output=get_output)
178 hist = history_manager.get_range(raw=raw, output=get_output)
161
179
162 # We could be displaying the entire history, so let's not try to pull
180 # We could be displaying the entire history, so let's not try to pull
163 # it into a list in memory. Anything that needs more space will just
181 # it into a list in memory. Anything that needs more space will just
164 # misalign.
182 # misalign.
165 width = 4
183 width = 4
166
184
167 for session, lineno, inline in hist:
185 for session, lineno, inline in hist:
168 # Print user history with tabs expanded to 4 spaces. The GUI
186 # Print user history with tabs expanded to 4 spaces. The GUI
169 # clients use hard tabs for easier usability in auto-indented code,
187 # clients use hard tabs for easier usability in auto-indented code,
170 # but we want to produce PEP-8 compliant history for safe pasting
188 # but we want to produce PEP-8 compliant history for safe pasting
171 # into an editor.
189 # into an editor.
172 if get_output:
190 if get_output:
173 inline, output = inline
191 inline, output = inline
174 inline = inline.expandtabs(4).rstrip()
192 inline = inline.expandtabs(4).rstrip()
175
193
176 multiline = "\n" in inline
194 multiline = "\n" in inline
177 line_sep = '\n' if multiline else ' '
195 line_sep = '\n' if multiline else ' '
178 if print_nums:
196 if print_nums:
179 print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
197 print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
180 line_sep), file=outfile, end=u'')
198 line_sep), file=outfile, end=u'')
181 if pyprompts:
199 if pyprompts:
182 print(u">>> ", end=u"", file=outfile)
200 print(u">>> ", end=u"", file=outfile)
183 if multiline:
201 if multiline:
184 inline = "\n... ".join(inline.splitlines()) + "\n..."
202 inline = "\n... ".join(inline.splitlines()) + "\n..."
185 print(inline, file=outfile)
203 print(inline, file=outfile)
186 if get_output and output:
204 if get_output and output:
187 print(output, file=outfile)
205 print(output, file=outfile)
188
206
189 if close_at_end:
207 if close_at_end:
190 outfile.close()
208 outfile.close()
191
209
192 @line_magic
210 @line_magic
193 def recall(self, arg):
211 def recall(self, arg):
194 r"""Repeat a command, or get command to input line for editing.
212 r"""Repeat a command, or get command to input line for editing.
195
213
196 %recall and %rep are equivalent.
214 %recall and %rep are equivalent.
197
215
198 - %recall (no arguments):
216 - %recall (no arguments):
199
217
200 Place a string version of last computation result (stored in the
218 Place a string version of last computation result (stored in the
201 special '_' variable) to the next input prompt. Allows you to create
219 special '_' variable) to the next input prompt. Allows you to create
202 elaborate command lines without using copy-paste::
220 elaborate command lines without using copy-paste::
203
221
204 In[1]: l = ["hei", "vaan"]
222 In[1]: l = ["hei", "vaan"]
205 In[2]: "".join(l)
223 In[2]: "".join(l)
206 Out[2]: heivaan
224 Out[2]: heivaan
207 In[3]: %recall
225 In[3]: %recall
208 In[4]: heivaan_ <== cursor blinking
226 In[4]: heivaan_ <== cursor blinking
209
227
210 %recall 45
228 %recall 45
211
229
212 Place history line 45 on the next input prompt. Use %hist to find
230 Place history line 45 on the next input prompt. Use %hist to find
213 out the number.
231 out the number.
214
232
215 %recall 1-4
233 %recall 1-4
216
234
217 Combine the specified lines into one cell, and place it on the next
235 Combine the specified lines into one cell, and place it on the next
218 input prompt. See %history for the slice syntax.
236 input prompt. See %history for the slice syntax.
219
237
220 %recall foo+bar
238 %recall foo+bar
221
239
222 If foo+bar can be evaluated in the user namespace, the result is
240 If foo+bar can be evaluated in the user namespace, the result is
223 placed at the next input prompt. Otherwise, the history is searched
241 placed at the next input prompt. Otherwise, the history is searched
224 for lines which contain that substring, and the most recent one is
242 for lines which contain that substring, and the most recent one is
225 placed at the next input prompt.
243 placed at the next input prompt.
226 """
244 """
227 if not arg: # Last output
245 if not arg: # Last output
228 self.shell.set_next_input(str(self.shell.user_ns["_"]))
246 self.shell.set_next_input(str(self.shell.user_ns["_"]))
229 return
247 return
230 # Get history range
248 # Get history range
231 histlines = self.shell.history_manager.get_range_by_str(arg)
249 histlines = self.shell.history_manager.get_range_by_str(arg)
232 cmd = "\n".join(x[2] for x in histlines)
250 cmd = "\n".join(x[2] for x in histlines)
233 if cmd:
251 if cmd:
234 self.shell.set_next_input(cmd.rstrip())
252 self.shell.set_next_input(cmd.rstrip())
235 return
253 return
236
254
237 try: # Variable in user namespace
255 try: # Variable in user namespace
238 cmd = str(eval(arg, self.shell.user_ns))
256 cmd = str(eval(arg, self.shell.user_ns))
239 except Exception: # Search for term in history
257 except Exception: # Search for term in history
240 histlines = self.shell.history_manager.search("*"+arg+"*")
258 histlines = self.shell.history_manager.search("*"+arg+"*")
241 for h in reversed([x[2] for x in histlines]):
259 for h in reversed([x[2] for x in histlines]):
242 if 'recall' in h or 'rep' in h:
260 if 'recall' in h or 'rep' in h:
243 continue
261 continue
244 self.shell.set_next_input(h.rstrip())
262 self.shell.set_next_input(h.rstrip())
245 return
263 return
246 else:
264 else:
247 self.shell.set_next_input(cmd.rstrip())
265 self.shell.set_next_input(cmd.rstrip())
248 print("Couldn't evaluate or find in history:", arg)
266 print("Couldn't evaluate or find in history:", arg)
249
267
250 @line_magic
268 @line_magic
251 def rerun(self, parameter_s=''):
269 def rerun(self, parameter_s=''):
252 """Re-run previous input
270 """Re-run previous input
253
271
254 By default, you can specify ranges of input history to be repeated
272 By default, you can specify ranges of input history to be repeated
255 (as with %history). With no arguments, it will repeat the last line.
273 (as with %history). With no arguments, it will repeat the last line.
256
274
257 Options:
275 Options:
258
276
259 -l <n> : Repeat the last n lines of input, not including the
277 -l <n> : Repeat the last n lines of input, not including the
260 current command.
278 current command.
261
279
262 -g foo : Repeat the most recent line which contains foo
280 -g foo : Repeat the most recent line which contains foo
263 """
281 """
264 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
282 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
265 if "l" in opts: # Last n lines
283 if "l" in opts: # Last n lines
266 n = int(opts['l'])
284 n = int(opts['l'])
267 hist = self.shell.history_manager.get_tail(n)
285 hist = self.shell.history_manager.get_tail(n)
268 elif "g" in opts: # Search
286 elif "g" in opts: # Search
269 p = "*"+opts['g']+"*"
287 p = "*"+opts['g']+"*"
270 hist = list(self.shell.history_manager.search(p))
288 hist = list(self.shell.history_manager.search(p))
271 for l in reversed(hist):
289 for l in reversed(hist):
272 if "rerun" not in l[2]:
290 if "rerun" not in l[2]:
273 hist = [l] # The last match which isn't a %rerun
291 hist = [l] # The last match which isn't a %rerun
274 break
292 break
275 else:
293 else:
276 hist = [] # No matches except %rerun
294 hist = [] # No matches except %rerun
277 elif args: # Specify history ranges
295 elif args: # Specify history ranges
278 hist = self.shell.history_manager.get_range_by_str(args)
296 hist = self.shell.history_manager.get_range_by_str(args)
279 else: # Last line
297 else: # Last line
280 hist = self.shell.history_manager.get_tail(1)
298 hist = self.shell.history_manager.get_tail(1)
281 hist = [x[2] for x in hist]
299 hist = [x[2] for x in hist]
282 if not hist:
300 if not hist:
283 print("No lines in history match specification")
301 print("No lines in history match specification")
284 return
302 return
285 histlines = "\n".join(hist)
303 histlines = "\n".join(hist)
286 print("=== Executing: ===")
304 print("=== Executing: ===")
287 print(histlines)
305 print(histlines)
288 print("=== Output: ===")
306 print("=== Output: ===")
289 self.shell.run_cell("\n".join(hist), store_history=False)
307 self.shell.run_cell("\n".join(hist), store_history=False)
@@ -1,703 +1,703 b''
1 """Implementation of namespace-related magic functions.
1 """Implementation of namespace-related magic functions.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012 The IPython Development Team.
4 # Copyright (c) 2012 The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # Stdlib
15 # Stdlib
16 import gc
16 import gc
17 import re
17 import re
18 import sys
18 import sys
19
19
20 # Our own packages
20 # Our own packages
21 from IPython.core import page
21 from IPython.core import page
22 from IPython.core.error import StdinNotImplementedError, UsageError
22 from IPython.core.error import StdinNotImplementedError, UsageError
23 from IPython.core.magic import Magics, magics_class, line_magic
23 from IPython.core.magic import Magics, magics_class, line_magic
24 from IPython.testing.skipdoctest import skip_doctest
24 from IPython.testing.skipdoctest import skip_doctest
25 from IPython.utils.encoding import DEFAULT_ENCODING
25 from IPython.utils.encoding import DEFAULT_ENCODING
26 from IPython.utils.openpy import read_py_file
26 from IPython.utils.openpy import read_py_file
27 from IPython.utils.path import get_py_filename
27 from IPython.utils.path import get_py_filename
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Magic implementation classes
30 # Magic implementation classes
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 @magics_class
33 @magics_class
34 class NamespaceMagics(Magics):
34 class NamespaceMagics(Magics):
35 """Magics to manage various aspects of the user's namespace.
35 """Magics to manage various aspects of the user's namespace.
36
36
37 These include listing variables, introspecting into them, etc.
37 These include listing variables, introspecting into them, etc.
38 """
38 """
39
39
40 @line_magic
40 @line_magic
41 def pinfo(self, parameter_s='', namespaces=None):
41 def pinfo(self, parameter_s='', namespaces=None):
42 """Provide detailed information about an object.
42 """Provide detailed information about an object.
43
43
44 '%pinfo object' is just a synonym for object? or ?object."""
44 '%pinfo object' is just a synonym for object? or ?object."""
45
45
46 #print 'pinfo par: <%s>' % parameter_s # dbg
46 #print 'pinfo par: <%s>' % parameter_s # dbg
47 # detail_level: 0 -> obj? , 1 -> obj??
47 # detail_level: 0 -> obj? , 1 -> obj??
48 detail_level = 0
48 detail_level = 0
49 # We need to detect if we got called as 'pinfo pinfo foo', which can
49 # We need to detect if we got called as 'pinfo pinfo foo', which can
50 # happen if the user types 'pinfo foo?' at the cmd line.
50 # happen if the user types 'pinfo foo?' at the cmd line.
51 pinfo,qmark1,oname,qmark2 = \
51 pinfo,qmark1,oname,qmark2 = \
52 re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups()
52 re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups()
53 if pinfo or qmark1 or qmark2:
53 if pinfo or qmark1 or qmark2:
54 detail_level = 1
54 detail_level = 1
55 if "*" in oname:
55 if "*" in oname:
56 self.psearch(oname)
56 self.psearch(oname)
57 else:
57 else:
58 self.shell._inspect('pinfo', oname, detail_level=detail_level,
58 self.shell._inspect('pinfo', oname, detail_level=detail_level,
59 namespaces=namespaces)
59 namespaces=namespaces)
60
60
61 @line_magic
61 @line_magic
62 def pinfo2(self, parameter_s='', namespaces=None):
62 def pinfo2(self, parameter_s='', namespaces=None):
63 """Provide extra detailed information about an object.
63 """Provide extra detailed information about an object.
64
64
65 '%pinfo2 object' is just a synonym for object?? or ??object."""
65 '%pinfo2 object' is just a synonym for object?? or ??object."""
66 self.shell._inspect('pinfo', parameter_s, detail_level=1,
66 self.shell._inspect('pinfo', parameter_s, detail_level=1,
67 namespaces=namespaces)
67 namespaces=namespaces)
68
68
69 @skip_doctest
69 @skip_doctest
70 @line_magic
70 @line_magic
71 def pdef(self, parameter_s='', namespaces=None):
71 def pdef(self, parameter_s='', namespaces=None):
72 """Print the definition header for any callable object.
72 """Print the call signature for any callable object.
73
73
74 If the object is a class, print the constructor information.
74 If the object is a class, print the constructor information.
75
75
76 Examples
76 Examples
77 --------
77 --------
78 ::
78 ::
79
79
80 In [3]: %pdef urllib.urlopen
80 In [3]: %pdef urllib.urlopen
81 urllib.urlopen(url, data=None, proxies=None)
81 urllib.urlopen(url, data=None, proxies=None)
82 """
82 """
83 self.shell._inspect('pdef',parameter_s, namespaces)
83 self.shell._inspect('pdef',parameter_s, namespaces)
84
84
85 @line_magic
85 @line_magic
86 def pdoc(self, parameter_s='', namespaces=None):
86 def pdoc(self, parameter_s='', namespaces=None):
87 """Print the docstring for an object.
87 """Print the docstring for an object.
88
88
89 If the given object is a class, it will print both the class and the
89 If the given object is a class, it will print both the class and the
90 constructor docstrings."""
90 constructor docstrings."""
91 self.shell._inspect('pdoc',parameter_s, namespaces)
91 self.shell._inspect('pdoc',parameter_s, namespaces)
92
92
93 @line_magic
93 @line_magic
94 def psource(self, parameter_s='', namespaces=None):
94 def psource(self, parameter_s='', namespaces=None):
95 """Print (or run through pager) the source code for an object."""
95 """Print (or run through pager) the source code for an object."""
96 if not parameter_s:
96 if not parameter_s:
97 raise UsageError('Missing object name.')
97 raise UsageError('Missing object name.')
98 self.shell._inspect('psource',parameter_s, namespaces)
98 self.shell._inspect('psource',parameter_s, namespaces)
99
99
100 @line_magic
100 @line_magic
101 def pfile(self, parameter_s=''):
101 def pfile(self, parameter_s='', namespaces=None):
102 """Print (or run through pager) the file where an object is defined.
102 """Print (or run through pager) the file where an object is defined.
103
103
104 The file opens at the line where the object definition begins. IPython
104 The file opens at the line where the object definition begins. IPython
105 will honor the environment variable PAGER if set, and otherwise will
105 will honor the environment variable PAGER if set, and otherwise will
106 do its best to print the file in a convenient form.
106 do its best to print the file in a convenient form.
107
107
108 If the given argument is not an object currently defined, IPython will
108 If the given argument is not an object currently defined, IPython will
109 try to interpret it as a filename (automatically adding a .py extension
109 try to interpret it as a filename (automatically adding a .py extension
110 if needed). You can thus use %pfile as a syntax highlighting code
110 if needed). You can thus use %pfile as a syntax highlighting code
111 viewer."""
111 viewer."""
112
112
113 # first interpret argument as an object name
113 # first interpret argument as an object name
114 out = self.shell._inspect('pfile',parameter_s)
114 out = self.shell._inspect('pfile',parameter_s, namespaces)
115 # if not, try the input as a filename
115 # if not, try the input as a filename
116 if out == 'not found':
116 if out == 'not found':
117 try:
117 try:
118 filename = get_py_filename(parameter_s)
118 filename = get_py_filename(parameter_s)
119 except IOError as msg:
119 except IOError as msg:
120 print msg
120 print msg
121 return
121 return
122 page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False)))
122 page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False)))
123
123
124 @line_magic
124 @line_magic
125 def psearch(self, parameter_s=''):
125 def psearch(self, parameter_s=''):
126 """Search for object in namespaces by wildcard.
126 """Search for object in namespaces by wildcard.
127
127
128 %psearch [options] PATTERN [OBJECT TYPE]
128 %psearch [options] PATTERN [OBJECT TYPE]
129
129
130 Note: ? can be used as a synonym for %psearch, at the beginning or at
130 Note: ? can be used as a synonym for %psearch, at the beginning or at
131 the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the
131 the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the
132 rest of the command line must be unchanged (options come first), so
132 rest of the command line must be unchanged (options come first), so
133 for example the following forms are equivalent
133 for example the following forms are equivalent
134
134
135 %psearch -i a* function
135 %psearch -i a* function
136 -i a* function?
136 -i a* function?
137 ?-i a* function
137 ?-i a* function
138
138
139 Arguments:
139 Arguments:
140
140
141 PATTERN
141 PATTERN
142
142
143 where PATTERN is a string containing * as a wildcard similar to its
143 where PATTERN is a string containing * as a wildcard similar to its
144 use in a shell. The pattern is matched in all namespaces on the
144 use in a shell. The pattern is matched in all namespaces on the
145 search path. By default objects starting with a single _ are not
145 search path. By default objects starting with a single _ are not
146 matched, many IPython generated objects have a single
146 matched, many IPython generated objects have a single
147 underscore. The default is case insensitive matching. Matching is
147 underscore. The default is case insensitive matching. Matching is
148 also done on the attributes of objects and not only on the objects
148 also done on the attributes of objects and not only on the objects
149 in a module.
149 in a module.
150
150
151 [OBJECT TYPE]
151 [OBJECT TYPE]
152
152
153 Is the name of a python type from the types module. The name is
153 Is the name of a python type from the types module. The name is
154 given in lowercase without the ending type, ex. StringType is
154 given in lowercase without the ending type, ex. StringType is
155 written string. By adding a type here only objects matching the
155 written string. By adding a type here only objects matching the
156 given type are matched. Using all here makes the pattern match all
156 given type are matched. Using all here makes the pattern match all
157 types (this is the default).
157 types (this is the default).
158
158
159 Options:
159 Options:
160
160
161 -a: makes the pattern match even objects whose names start with a
161 -a: makes the pattern match even objects whose names start with a
162 single underscore. These names are normally omitted from the
162 single underscore. These names are normally omitted from the
163 search.
163 search.
164
164
165 -i/-c: make the pattern case insensitive/sensitive. If neither of
165 -i/-c: make the pattern case insensitive/sensitive. If neither of
166 these options are given, the default is read from your configuration
166 these options are given, the default is read from your configuration
167 file, with the option ``InteractiveShell.wildcards_case_sensitive``.
167 file, with the option ``InteractiveShell.wildcards_case_sensitive``.
168 If this option is not specified in your configuration file, IPython's
168 If this option is not specified in your configuration file, IPython's
169 internal default is to do a case sensitive search.
169 internal default is to do a case sensitive search.
170
170
171 -e/-s NAMESPACE: exclude/search a given namespace. The pattern you
171 -e/-s NAMESPACE: exclude/search a given namespace. The pattern you
172 specify can be searched in any of the following namespaces:
172 specify can be searched in any of the following namespaces:
173 'builtin', 'user', 'user_global','internal', 'alias', where
173 'builtin', 'user', 'user_global','internal', 'alias', where
174 'builtin' and 'user' are the search defaults. Note that you should
174 'builtin' and 'user' are the search defaults. Note that you should
175 not use quotes when specifying namespaces.
175 not use quotes when specifying namespaces.
176
176
177 'Builtin' contains the python module builtin, 'user' contains all
177 'Builtin' contains the python module builtin, 'user' contains all
178 user data, 'alias' only contain the shell aliases and no python
178 user data, 'alias' only contain the shell aliases and no python
179 objects, 'internal' contains objects used by IPython. The
179 objects, 'internal' contains objects used by IPython. The
180 'user_global' namespace is only used by embedded IPython instances,
180 'user_global' namespace is only used by embedded IPython instances,
181 and it contains module-level globals. You can add namespaces to the
181 and it contains module-level globals. You can add namespaces to the
182 search with -s or exclude them with -e (these options can be given
182 search with -s or exclude them with -e (these options can be given
183 more than once).
183 more than once).
184
184
185 Examples
185 Examples
186 --------
186 --------
187 ::
187 ::
188
188
189 %psearch a* -> objects beginning with an a
189 %psearch a* -> objects beginning with an a
190 %psearch -e builtin a* -> objects NOT in the builtin space starting in a
190 %psearch -e builtin a* -> objects NOT in the builtin space starting in a
191 %psearch a* function -> all functions beginning with an a
191 %psearch a* function -> all functions beginning with an a
192 %psearch re.e* -> objects beginning with an e in module re
192 %psearch re.e* -> objects beginning with an e in module re
193 %psearch r*.e* -> objects that start with e in modules starting in r
193 %psearch r*.e* -> objects that start with e in modules starting in r
194 %psearch r*.* string -> all strings in modules beginning with r
194 %psearch r*.* string -> all strings in modules beginning with r
195
195
196 Case sensitive search::
196 Case sensitive search::
197
197
198 %psearch -c a* list all object beginning with lower case a
198 %psearch -c a* list all object beginning with lower case a
199
199
200 Show objects beginning with a single _::
200 Show objects beginning with a single _::
201
201
202 %psearch -a _* list objects beginning with a single underscore
202 %psearch -a _* list objects beginning with a single underscore
203 """
203 """
204 try:
204 try:
205 parameter_s.encode('ascii')
205 parameter_s.encode('ascii')
206 except UnicodeEncodeError:
206 except UnicodeEncodeError:
207 print 'Python identifiers can only contain ascii characters.'
207 print 'Python identifiers can only contain ascii characters.'
208 return
208 return
209
209
210 # default namespaces to be searched
210 # default namespaces to be searched
211 def_search = ['user_local', 'user_global', 'builtin']
211 def_search = ['user_local', 'user_global', 'builtin']
212
212
213 # Process options/args
213 # Process options/args
214 opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True)
214 opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True)
215 opt = opts.get
215 opt = opts.get
216 shell = self.shell
216 shell = self.shell
217 psearch = shell.inspector.psearch
217 psearch = shell.inspector.psearch
218
218
219 # select case options
219 # select case options
220 if 'i' in opts:
220 if 'i' in opts:
221 ignore_case = True
221 ignore_case = True
222 elif 'c' in opts:
222 elif 'c' in opts:
223 ignore_case = False
223 ignore_case = False
224 else:
224 else:
225 ignore_case = not shell.wildcards_case_sensitive
225 ignore_case = not shell.wildcards_case_sensitive
226
226
227 # Build list of namespaces to search from user options
227 # Build list of namespaces to search from user options
228 def_search.extend(opt('s',[]))
228 def_search.extend(opt('s',[]))
229 ns_exclude = ns_exclude=opt('e',[])
229 ns_exclude = ns_exclude=opt('e',[])
230 ns_search = [nm for nm in def_search if nm not in ns_exclude]
230 ns_search = [nm for nm in def_search if nm not in ns_exclude]
231
231
232 # Call the actual search
232 # Call the actual search
233 try:
233 try:
234 psearch(args,shell.ns_table,ns_search,
234 psearch(args,shell.ns_table,ns_search,
235 show_all=opt('a'),ignore_case=ignore_case)
235 show_all=opt('a'),ignore_case=ignore_case)
236 except:
236 except:
237 shell.showtraceback()
237 shell.showtraceback()
238
238
239 @skip_doctest
239 @skip_doctest
240 @line_magic
240 @line_magic
241 def who_ls(self, parameter_s=''):
241 def who_ls(self, parameter_s=''):
242 """Return a sorted list of all interactive variables.
242 """Return a sorted list of all interactive variables.
243
243
244 If arguments are given, only variables of types matching these
244 If arguments are given, only variables of types matching these
245 arguments are returned.
245 arguments are returned.
246
246
247 Examples
247 Examples
248 --------
248 --------
249
249
250 Define two variables and list them with who_ls::
250 Define two variables and list them with who_ls::
251
251
252 In [1]: alpha = 123
252 In [1]: alpha = 123
253
253
254 In [2]: beta = 'test'
254 In [2]: beta = 'test'
255
255
256 In [3]: %who_ls
256 In [3]: %who_ls
257 Out[3]: ['alpha', 'beta']
257 Out[3]: ['alpha', 'beta']
258
258
259 In [4]: %who_ls int
259 In [4]: %who_ls int
260 Out[4]: ['alpha']
260 Out[4]: ['alpha']
261
261
262 In [5]: %who_ls str
262 In [5]: %who_ls str
263 Out[5]: ['beta']
263 Out[5]: ['beta']
264 """
264 """
265
265
266 user_ns = self.shell.user_ns
266 user_ns = self.shell.user_ns
267 user_ns_hidden = self.shell.user_ns_hidden
267 user_ns_hidden = self.shell.user_ns_hidden
268 out = [ i for i in user_ns
268 out = [ i for i in user_ns
269 if not i.startswith('_') \
269 if not i.startswith('_') \
270 and not i in user_ns_hidden ]
270 and not i in user_ns_hidden ]
271
271
272 typelist = parameter_s.split()
272 typelist = parameter_s.split()
273 if typelist:
273 if typelist:
274 typeset = set(typelist)
274 typeset = set(typelist)
275 out = [i for i in out if type(user_ns[i]).__name__ in typeset]
275 out = [i for i in out if type(user_ns[i]).__name__ in typeset]
276
276
277 out.sort()
277 out.sort()
278 return out
278 return out
279
279
280 @skip_doctest
280 @skip_doctest
281 @line_magic
281 @line_magic
282 def who(self, parameter_s=''):
282 def who(self, parameter_s=''):
283 """Print all interactive variables, with some minimal formatting.
283 """Print all interactive variables, with some minimal formatting.
284
284
285 If any arguments are given, only variables whose type matches one of
285 If any arguments are given, only variables whose type matches one of
286 these are printed. For example::
286 these are printed. For example::
287
287
288 %who function str
288 %who function str
289
289
290 will only list functions and strings, excluding all other types of
290 will only list functions and strings, excluding all other types of
291 variables. To find the proper type names, simply use type(var) at a
291 variables. To find the proper type names, simply use type(var) at a
292 command line to see how python prints type names. For example:
292 command line to see how python prints type names. For example:
293
293
294 ::
294 ::
295
295
296 In [1]: type('hello')\\
296 In [1]: type('hello')\\
297 Out[1]: <type 'str'>
297 Out[1]: <type 'str'>
298
298
299 indicates that the type name for strings is 'str'.
299 indicates that the type name for strings is 'str'.
300
300
301 ``%who`` always excludes executed names loaded through your configuration
301 ``%who`` always excludes executed names loaded through your configuration
302 file and things which are internal to IPython.
302 file and things which are internal to IPython.
303
303
304 This is deliberate, as typically you may load many modules and the
304 This is deliberate, as typically you may load many modules and the
305 purpose of %who is to show you only what you've manually defined.
305 purpose of %who is to show you only what you've manually defined.
306
306
307 Examples
307 Examples
308 --------
308 --------
309
309
310 Define two variables and list them with who::
310 Define two variables and list them with who::
311
311
312 In [1]: alpha = 123
312 In [1]: alpha = 123
313
313
314 In [2]: beta = 'test'
314 In [2]: beta = 'test'
315
315
316 In [3]: %who
316 In [3]: %who
317 alpha beta
317 alpha beta
318
318
319 In [4]: %who int
319 In [4]: %who int
320 alpha
320 alpha
321
321
322 In [5]: %who str
322 In [5]: %who str
323 beta
323 beta
324 """
324 """
325
325
326 varlist = self.who_ls(parameter_s)
326 varlist = self.who_ls(parameter_s)
327 if not varlist:
327 if not varlist:
328 if parameter_s:
328 if parameter_s:
329 print 'No variables match your requested type.'
329 print 'No variables match your requested type.'
330 else:
330 else:
331 print 'Interactive namespace is empty.'
331 print 'Interactive namespace is empty.'
332 return
332 return
333
333
334 # if we have variables, move on...
334 # if we have variables, move on...
335 count = 0
335 count = 0
336 for i in varlist:
336 for i in varlist:
337 print i+'\t',
337 print i+'\t',
338 count += 1
338 count += 1
339 if count > 8:
339 if count > 8:
340 count = 0
340 count = 0
341 print
341 print
342 print
342 print
343
343
344 @skip_doctest
344 @skip_doctest
345 @line_magic
345 @line_magic
346 def whos(self, parameter_s=''):
346 def whos(self, parameter_s=''):
347 """Like %who, but gives some extra information about each variable.
347 """Like %who, but gives some extra information about each variable.
348
348
349 The same type filtering of %who can be applied here.
349 The same type filtering of %who can be applied here.
350
350
351 For all variables, the type is printed. Additionally it prints:
351 For all variables, the type is printed. Additionally it prints:
352
352
353 - For {},[],(): their length.
353 - For {},[],(): their length.
354
354
355 - For numpy arrays, a summary with shape, number of
355 - For numpy arrays, a summary with shape, number of
356 elements, typecode and size in memory.
356 elements, typecode and size in memory.
357
357
358 - Everything else: a string representation, snipping their middle if
358 - Everything else: a string representation, snipping their middle if
359 too long.
359 too long.
360
360
361 Examples
361 Examples
362 --------
362 --------
363
363
364 Define two variables and list them with whos::
364 Define two variables and list them with whos::
365
365
366 In [1]: alpha = 123
366 In [1]: alpha = 123
367
367
368 In [2]: beta = 'test'
368 In [2]: beta = 'test'
369
369
370 In [3]: %whos
370 In [3]: %whos
371 Variable Type Data/Info
371 Variable Type Data/Info
372 --------------------------------
372 --------------------------------
373 alpha int 123
373 alpha int 123
374 beta str test
374 beta str test
375 """
375 """
376
376
377 varnames = self.who_ls(parameter_s)
377 varnames = self.who_ls(parameter_s)
378 if not varnames:
378 if not varnames:
379 if parameter_s:
379 if parameter_s:
380 print 'No variables match your requested type.'
380 print 'No variables match your requested type.'
381 else:
381 else:
382 print 'Interactive namespace is empty.'
382 print 'Interactive namespace is empty.'
383 return
383 return
384
384
385 # if we have variables, move on...
385 # if we have variables, move on...
386
386
387 # for these types, show len() instead of data:
387 # for these types, show len() instead of data:
388 seq_types = ['dict', 'list', 'tuple']
388 seq_types = ['dict', 'list', 'tuple']
389
389
390 # for numpy arrays, display summary info
390 # for numpy arrays, display summary info
391 ndarray_type = None
391 ndarray_type = None
392 if 'numpy' in sys.modules:
392 if 'numpy' in sys.modules:
393 try:
393 try:
394 from numpy import ndarray
394 from numpy import ndarray
395 except ImportError:
395 except ImportError:
396 pass
396 pass
397 else:
397 else:
398 ndarray_type = ndarray.__name__
398 ndarray_type = ndarray.__name__
399
399
400 # Find all variable names and types so we can figure out column sizes
400 # Find all variable names and types so we can figure out column sizes
401 def get_vars(i):
401 def get_vars(i):
402 return self.shell.user_ns[i]
402 return self.shell.user_ns[i]
403
403
404 # some types are well known and can be shorter
404 # some types are well known and can be shorter
405 abbrevs = {'IPython.core.macro.Macro' : 'Macro'}
405 abbrevs = {'IPython.core.macro.Macro' : 'Macro'}
406 def type_name(v):
406 def type_name(v):
407 tn = type(v).__name__
407 tn = type(v).__name__
408 return abbrevs.get(tn,tn)
408 return abbrevs.get(tn,tn)
409
409
410 varlist = map(get_vars,varnames)
410 varlist = map(get_vars,varnames)
411
411
412 typelist = []
412 typelist = []
413 for vv in varlist:
413 for vv in varlist:
414 tt = type_name(vv)
414 tt = type_name(vv)
415
415
416 if tt=='instance':
416 if tt=='instance':
417 typelist.append( abbrevs.get(str(vv.__class__),
417 typelist.append( abbrevs.get(str(vv.__class__),
418 str(vv.__class__)))
418 str(vv.__class__)))
419 else:
419 else:
420 typelist.append(tt)
420 typelist.append(tt)
421
421
422 # column labels and # of spaces as separator
422 # column labels and # of spaces as separator
423 varlabel = 'Variable'
423 varlabel = 'Variable'
424 typelabel = 'Type'
424 typelabel = 'Type'
425 datalabel = 'Data/Info'
425 datalabel = 'Data/Info'
426 colsep = 3
426 colsep = 3
427 # variable format strings
427 # variable format strings
428 vformat = "{0:<{varwidth}}{1:<{typewidth}}"
428 vformat = "{0:<{varwidth}}{1:<{typewidth}}"
429 aformat = "%s: %s elems, type `%s`, %s bytes"
429 aformat = "%s: %s elems, type `%s`, %s bytes"
430 # find the size of the columns to format the output nicely
430 # find the size of the columns to format the output nicely
431 varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep
431 varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep
432 typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep
432 typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep
433 # table header
433 # table header
434 print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \
434 print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \
435 ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1)
435 ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1)
436 # and the table itself
436 # and the table itself
437 kb = 1024
437 kb = 1024
438 Mb = 1048576 # kb**2
438 Mb = 1048576 # kb**2
439 for vname,var,vtype in zip(varnames,varlist,typelist):
439 for vname,var,vtype in zip(varnames,varlist,typelist):
440 print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth),
440 print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth),
441 if vtype in seq_types:
441 if vtype in seq_types:
442 print "n="+str(len(var))
442 print "n="+str(len(var))
443 elif vtype == ndarray_type:
443 elif vtype == ndarray_type:
444 vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1]
444 vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1]
445 if vtype==ndarray_type:
445 if vtype==ndarray_type:
446 # numpy
446 # numpy
447 vsize = var.size
447 vsize = var.size
448 vbytes = vsize*var.itemsize
448 vbytes = vsize*var.itemsize
449 vdtype = var.dtype
449 vdtype = var.dtype
450
450
451 if vbytes < 100000:
451 if vbytes < 100000:
452 print aformat % (vshape, vsize, vdtype, vbytes)
452 print aformat % (vshape, vsize, vdtype, vbytes)
453 else:
453 else:
454 print aformat % (vshape, vsize, vdtype, vbytes),
454 print aformat % (vshape, vsize, vdtype, vbytes),
455 if vbytes < Mb:
455 if vbytes < Mb:
456 print '(%s kb)' % (vbytes/kb,)
456 print '(%s kb)' % (vbytes/kb,)
457 else:
457 else:
458 print '(%s Mb)' % (vbytes/Mb,)
458 print '(%s Mb)' % (vbytes/Mb,)
459 else:
459 else:
460 try:
460 try:
461 vstr = str(var)
461 vstr = str(var)
462 except UnicodeEncodeError:
462 except UnicodeEncodeError:
463 vstr = unicode(var).encode(DEFAULT_ENCODING,
463 vstr = unicode(var).encode(DEFAULT_ENCODING,
464 'backslashreplace')
464 'backslashreplace')
465 except:
465 except:
466 vstr = "<object with id %d (str() failed)>" % id(var)
466 vstr = "<object with id %d (str() failed)>" % id(var)
467 vstr = vstr.replace('\n', '\\n')
467 vstr = vstr.replace('\n', '\\n')
468 if len(vstr) < 50:
468 if len(vstr) < 50:
469 print vstr
469 print vstr
470 else:
470 else:
471 print vstr[:25] + "<...>" + vstr[-25:]
471 print vstr[:25] + "<...>" + vstr[-25:]
472
472
473 @line_magic
473 @line_magic
474 def reset(self, parameter_s=''):
474 def reset(self, parameter_s=''):
475 """Resets the namespace by removing all names defined by the user, if
475 """Resets the namespace by removing all names defined by the user, if
476 called without arguments, or by removing some types of objects, such
476 called without arguments, or by removing some types of objects, such
477 as everything currently in IPython's In[] and Out[] containers (see
477 as everything currently in IPython's In[] and Out[] containers (see
478 the parameters for details).
478 the parameters for details).
479
479
480 Parameters
480 Parameters
481 ----------
481 ----------
482 -f : force reset without asking for confirmation.
482 -f : force reset without asking for confirmation.
483
483
484 -s : 'Soft' reset: Only clears your namespace, leaving history intact.
484 -s : 'Soft' reset: Only clears your namespace, leaving history intact.
485 References to objects may be kept. By default (without this option),
485 References to objects may be kept. By default (without this option),
486 we do a 'hard' reset, giving you a new session and removing all
486 we do a 'hard' reset, giving you a new session and removing all
487 references to objects from the current session.
487 references to objects from the current session.
488
488
489 in : reset input history
489 in : reset input history
490
490
491 out : reset output history
491 out : reset output history
492
492
493 dhist : reset directory history
493 dhist : reset directory history
494
494
495 array : reset only variables that are NumPy arrays
495 array : reset only variables that are NumPy arrays
496
496
497 See Also
497 See Also
498 --------
498 --------
499 magic_reset_selective : invoked as ``%reset_selective``
499 magic_reset_selective : invoked as ``%reset_selective``
500
500
501 Examples
501 Examples
502 --------
502 --------
503 ::
503 ::
504
504
505 In [6]: a = 1
505 In [6]: a = 1
506
506
507 In [7]: a
507 In [7]: a
508 Out[7]: 1
508 Out[7]: 1
509
509
510 In [8]: 'a' in _ip.user_ns
510 In [8]: 'a' in _ip.user_ns
511 Out[8]: True
511 Out[8]: True
512
512
513 In [9]: %reset -f
513 In [9]: %reset -f
514
514
515 In [1]: 'a' in _ip.user_ns
515 In [1]: 'a' in _ip.user_ns
516 Out[1]: False
516 Out[1]: False
517
517
518 In [2]: %reset -f in
518 In [2]: %reset -f in
519 Flushing input history
519 Flushing input history
520
520
521 In [3]: %reset -f dhist in
521 In [3]: %reset -f dhist in
522 Flushing directory history
522 Flushing directory history
523 Flushing input history
523 Flushing input history
524
524
525 Notes
525 Notes
526 -----
526 -----
527 Calling this magic from clients that do not implement standard input,
527 Calling this magic from clients that do not implement standard input,
528 such as the ipython notebook interface, will reset the namespace
528 such as the ipython notebook interface, will reset the namespace
529 without confirmation.
529 without confirmation.
530 """
530 """
531 opts, args = self.parse_options(parameter_s,'sf', mode='list')
531 opts, args = self.parse_options(parameter_s,'sf', mode='list')
532 if 'f' in opts:
532 if 'f' in opts:
533 ans = True
533 ans = True
534 else:
534 else:
535 try:
535 try:
536 ans = self.shell.ask_yes_no(
536 ans = self.shell.ask_yes_no(
537 "Once deleted, variables cannot be recovered. Proceed (y/[n])?",
537 "Once deleted, variables cannot be recovered. Proceed (y/[n])?",
538 default='n')
538 default='n')
539 except StdinNotImplementedError:
539 except StdinNotImplementedError:
540 ans = True
540 ans = True
541 if not ans:
541 if not ans:
542 print 'Nothing done.'
542 print 'Nothing done.'
543 return
543 return
544
544
545 if 's' in opts: # Soft reset
545 if 's' in opts: # Soft reset
546 user_ns = self.shell.user_ns
546 user_ns = self.shell.user_ns
547 for i in self.who_ls():
547 for i in self.who_ls():
548 del(user_ns[i])
548 del(user_ns[i])
549 elif len(args) == 0: # Hard reset
549 elif len(args) == 0: # Hard reset
550 self.shell.reset(new_session = False)
550 self.shell.reset(new_session = False)
551
551
552 # reset in/out/dhist/array: previously extensinions/clearcmd.py
552 # reset in/out/dhist/array: previously extensinions/clearcmd.py
553 ip = self.shell
553 ip = self.shell
554 user_ns = self.shell.user_ns # local lookup, heavily used
554 user_ns = self.shell.user_ns # local lookup, heavily used
555
555
556 for target in args:
556 for target in args:
557 target = target.lower() # make matches case insensitive
557 target = target.lower() # make matches case insensitive
558 if target == 'out':
558 if target == 'out':
559 print "Flushing output cache (%d entries)" % len(user_ns['_oh'])
559 print "Flushing output cache (%d entries)" % len(user_ns['_oh'])
560 self.shell.displayhook.flush()
560 self.shell.displayhook.flush()
561
561
562 elif target == 'in':
562 elif target == 'in':
563 print "Flushing input history"
563 print "Flushing input history"
564 pc = self.shell.displayhook.prompt_count + 1
564 pc = self.shell.displayhook.prompt_count + 1
565 for n in range(1, pc):
565 for n in range(1, pc):
566 key = '_i'+repr(n)
566 key = '_i'+repr(n)
567 user_ns.pop(key,None)
567 user_ns.pop(key,None)
568 user_ns.update(dict(_i=u'',_ii=u'',_iii=u''))
568 user_ns.update(dict(_i=u'',_ii=u'',_iii=u''))
569 hm = ip.history_manager
569 hm = ip.history_manager
570 # don't delete these, as %save and %macro depending on the
570 # don't delete these, as %save and %macro depending on the
571 # length of these lists to be preserved
571 # length of these lists to be preserved
572 hm.input_hist_parsed[:] = [''] * pc
572 hm.input_hist_parsed[:] = [''] * pc
573 hm.input_hist_raw[:] = [''] * pc
573 hm.input_hist_raw[:] = [''] * pc
574 # hm has internal machinery for _i,_ii,_iii, clear it out
574 # hm has internal machinery for _i,_ii,_iii, clear it out
575 hm._i = hm._ii = hm._iii = hm._i00 = u''
575 hm._i = hm._ii = hm._iii = hm._i00 = u''
576
576
577 elif target == 'array':
577 elif target == 'array':
578 # Support cleaning up numpy arrays
578 # Support cleaning up numpy arrays
579 try:
579 try:
580 from numpy import ndarray
580 from numpy import ndarray
581 # This must be done with items and not iteritems because
581 # This must be done with items and not iteritems because
582 # we're going to modify the dict in-place.
582 # we're going to modify the dict in-place.
583 for x,val in user_ns.items():
583 for x,val in user_ns.items():
584 if isinstance(val,ndarray):
584 if isinstance(val,ndarray):
585 del user_ns[x]
585 del user_ns[x]
586 except ImportError:
586 except ImportError:
587 print "reset array only works if Numpy is available."
587 print "reset array only works if Numpy is available."
588
588
589 elif target == 'dhist':
589 elif target == 'dhist':
590 print "Flushing directory history"
590 print "Flushing directory history"
591 del user_ns['_dh'][:]
591 del user_ns['_dh'][:]
592
592
593 else:
593 else:
594 print "Don't know how to reset ",
594 print "Don't know how to reset ",
595 print target + ", please run `%reset?` for details"
595 print target + ", please run `%reset?` for details"
596
596
597 gc.collect()
597 gc.collect()
598
598
599 @line_magic
599 @line_magic
600 def reset_selective(self, parameter_s=''):
600 def reset_selective(self, parameter_s=''):
601 """Resets the namespace by removing names defined by the user.
601 """Resets the namespace by removing names defined by the user.
602
602
603 Input/Output history are left around in case you need them.
603 Input/Output history are left around in case you need them.
604
604
605 %reset_selective [-f] regex
605 %reset_selective [-f] regex
606
606
607 No action is taken if regex is not included
607 No action is taken if regex is not included
608
608
609 Options
609 Options
610 -f : force reset without asking for confirmation.
610 -f : force reset without asking for confirmation.
611
611
612 See Also
612 See Also
613 --------
613 --------
614 magic_reset : invoked as ``%reset``
614 magic_reset : invoked as ``%reset``
615
615
616 Examples
616 Examples
617 --------
617 --------
618
618
619 We first fully reset the namespace so your output looks identical to
619 We first fully reset the namespace so your output looks identical to
620 this example for pedagogical reasons; in practice you do not need a
620 this example for pedagogical reasons; in practice you do not need a
621 full reset::
621 full reset::
622
622
623 In [1]: %reset -f
623 In [1]: %reset -f
624
624
625 Now, with a clean namespace we can make a few variables and use
625 Now, with a clean namespace we can make a few variables and use
626 ``%reset_selective`` to only delete names that match our regexp::
626 ``%reset_selective`` to only delete names that match our regexp::
627
627
628 In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8
628 In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8
629
629
630 In [3]: who_ls
630 In [3]: who_ls
631 Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c']
631 Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c']
632
632
633 In [4]: %reset_selective -f b[2-3]m
633 In [4]: %reset_selective -f b[2-3]m
634
634
635 In [5]: who_ls
635 In [5]: who_ls
636 Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
636 Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
637
637
638 In [6]: %reset_selective -f d
638 In [6]: %reset_selective -f d
639
639
640 In [7]: who_ls
640 In [7]: who_ls
641 Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
641 Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
642
642
643 In [8]: %reset_selective -f c
643 In [8]: %reset_selective -f c
644
644
645 In [9]: who_ls
645 In [9]: who_ls
646 Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m']
646 Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m']
647
647
648 In [10]: %reset_selective -f b
648 In [10]: %reset_selective -f b
649
649
650 In [11]: who_ls
650 In [11]: who_ls
651 Out[11]: ['a']
651 Out[11]: ['a']
652
652
653 Notes
653 Notes
654 -----
654 -----
655 Calling this magic from clients that do not implement standard input,
655 Calling this magic from clients that do not implement standard input,
656 such as the ipython notebook interface, will reset the namespace
656 such as the ipython notebook interface, will reset the namespace
657 without confirmation.
657 without confirmation.
658 """
658 """
659
659
660 opts, regex = self.parse_options(parameter_s,'f')
660 opts, regex = self.parse_options(parameter_s,'f')
661
661
662 if 'f' in opts:
662 if 'f' in opts:
663 ans = True
663 ans = True
664 else:
664 else:
665 try:
665 try:
666 ans = self.shell.ask_yes_no(
666 ans = self.shell.ask_yes_no(
667 "Once deleted, variables cannot be recovered. Proceed (y/[n])? ",
667 "Once deleted, variables cannot be recovered. Proceed (y/[n])? ",
668 default='n')
668 default='n')
669 except StdinNotImplementedError:
669 except StdinNotImplementedError:
670 ans = True
670 ans = True
671 if not ans:
671 if not ans:
672 print 'Nothing done.'
672 print 'Nothing done.'
673 return
673 return
674 user_ns = self.shell.user_ns
674 user_ns = self.shell.user_ns
675 if not regex:
675 if not regex:
676 print 'No regex pattern specified. Nothing done.'
676 print 'No regex pattern specified. Nothing done.'
677 return
677 return
678 else:
678 else:
679 try:
679 try:
680 m = re.compile(regex)
680 m = re.compile(regex)
681 except TypeError:
681 except TypeError:
682 raise TypeError('regex must be a string or compiled pattern')
682 raise TypeError('regex must be a string or compiled pattern')
683 for i in self.who_ls():
683 for i in self.who_ls():
684 if m.search(i):
684 if m.search(i):
685 del(user_ns[i])
685 del(user_ns[i])
686
686
687 @line_magic
687 @line_magic
688 def xdel(self, parameter_s=''):
688 def xdel(self, parameter_s=''):
689 """Delete a variable, trying to clear it from anywhere that
689 """Delete a variable, trying to clear it from anywhere that
690 IPython's machinery has references to it. By default, this uses
690 IPython's machinery has references to it. By default, this uses
691 the identity of the named object in the user namespace to remove
691 the identity of the named object in the user namespace to remove
692 references held under other names. The object is also removed
692 references held under other names. The object is also removed
693 from the output history.
693 from the output history.
694
694
695 Options
695 Options
696 -n : Delete the specified name from all namespaces, without
696 -n : Delete the specified name from all namespaces, without
697 checking their identity.
697 checking their identity.
698 """
698 """
699 opts, varname = self.parse_options(parameter_s,'n')
699 opts, varname = self.parse_options(parameter_s,'n')
700 try:
700 try:
701 self.shell.del_var(varname, ('n' in opts))
701 self.shell.del_var(varname, ('n' in opts))
702 except (NameError, ValueError) as e:
702 except (NameError, ValueError) as e:
703 print type(e).__name__ +": "+ str(e)
703 print type(e).__name__ +": "+ str(e)
@@ -1,873 +1,873 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for inspecting Python objects.
2 """Tools for inspecting Python objects.
3
3
4 Uses syntax highlighting for presenting the various information elements.
4 Uses syntax highlighting for presenting the various information elements.
5
5
6 Similar in spirit to the inspect module, but all calls take a name argument to
6 Similar in spirit to the inspect module, but all calls take a name argument to
7 reference the name under which an object is being read.
7 reference the name under which an object is being read.
8 """
8 """
9
9
10 #*****************************************************************************
10 #*****************************************************************************
11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #*****************************************************************************
15 #*****************************************************************************
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 __all__ = ['Inspector','InspectColors']
18 __all__ = ['Inspector','InspectColors']
19
19
20 # stdlib modules
20 # stdlib modules
21 import __builtin__
21 import __builtin__
22 import inspect
22 import inspect
23 import linecache
23 import linecache
24 import os
24 import os
25 import sys
25 import sys
26 import types
26 import types
27 import io as stdlib_io
27 import io as stdlib_io
28
28
29 from collections import namedtuple
29 from collections import namedtuple
30 try:
30 try:
31 from itertools import izip_longest
31 from itertools import izip_longest
32 except ImportError:
32 except ImportError:
33 from itertools import zip_longest as izip_longest
33 from itertools import zip_longest as izip_longest
34
34
35 # IPython's own
35 # IPython's own
36 from IPython.core import page
36 from IPython.core import page
37 from IPython.testing.skipdoctest import skip_doctest_py3
37 from IPython.testing.skipdoctest import skip_doctest_py3
38 from IPython.utils import PyColorize
38 from IPython.utils import PyColorize
39 from IPython.utils import io
39 from IPython.utils import io
40 from IPython.utils import openpy
40 from IPython.utils import openpy
41 from IPython.utils import py3compat
41 from IPython.utils import py3compat
42 from IPython.utils.text import indent
42 from IPython.utils.text import indent
43 from IPython.utils.wildcard import list_namespace
43 from IPython.utils.wildcard import list_namespace
44 from IPython.utils.coloransi import *
44 from IPython.utils.coloransi import *
45 from IPython.utils.py3compat import cast_unicode
45 from IPython.utils.py3compat import cast_unicode
46
46
47 #****************************************************************************
47 #****************************************************************************
48 # Builtin color schemes
48 # Builtin color schemes
49
49
50 Colors = TermColors # just a shorthand
50 Colors = TermColors # just a shorthand
51
51
52 # Build a few color schemes
52 # Build a few color schemes
53 NoColor = ColorScheme(
53 NoColor = ColorScheme(
54 'NoColor',{
54 'NoColor',{
55 'header' : Colors.NoColor,
55 'header' : Colors.NoColor,
56 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
56 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
57 } )
57 } )
58
58
59 LinuxColors = ColorScheme(
59 LinuxColors = ColorScheme(
60 'Linux',{
60 'Linux',{
61 'header' : Colors.LightRed,
61 'header' : Colors.LightRed,
62 'normal' : Colors.Normal # color off (usu. Colors.Normal)
62 'normal' : Colors.Normal # color off (usu. Colors.Normal)
63 } )
63 } )
64
64
65 LightBGColors = ColorScheme(
65 LightBGColors = ColorScheme(
66 'LightBG',{
66 'LightBG',{
67 'header' : Colors.Red,
67 'header' : Colors.Red,
68 'normal' : Colors.Normal # color off (usu. Colors.Normal)
68 'normal' : Colors.Normal # color off (usu. Colors.Normal)
69 } )
69 } )
70
70
71 # Build table of color schemes (needed by the parser)
71 # Build table of color schemes (needed by the parser)
72 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
72 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
73 'Linux')
73 'Linux')
74
74
75 #****************************************************************************
75 #****************************************************************************
76 # Auxiliary functions and objects
76 # Auxiliary functions and objects
77
77
78 # See the messaging spec for the definition of all these fields. This list
78 # See the messaging spec for the definition of all these fields. This list
79 # effectively defines the order of display
79 # effectively defines the order of display
80 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
80 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
81 'length', 'file', 'definition', 'docstring', 'source',
81 'length', 'file', 'definition', 'docstring', 'source',
82 'init_definition', 'class_docstring', 'init_docstring',
82 'init_definition', 'class_docstring', 'init_docstring',
83 'call_def', 'call_docstring',
83 'call_def', 'call_docstring',
84 # These won't be printed but will be used to determine how to
84 # These won't be printed but will be used to determine how to
85 # format the object
85 # format the object
86 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
86 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
87 ]
87 ]
88
88
89
89
90 def object_info(**kw):
90 def object_info(**kw):
91 """Make an object info dict with all fields present."""
91 """Make an object info dict with all fields present."""
92 infodict = dict(izip_longest(info_fields, [None]))
92 infodict = dict(izip_longest(info_fields, [None]))
93 infodict.update(kw)
93 infodict.update(kw)
94 return infodict
94 return infodict
95
95
96
96
97 def get_encoding(obj):
97 def get_encoding(obj):
98 """Get encoding for python source file defining obj
98 """Get encoding for python source file defining obj
99
99
100 Returns None if obj is not defined in a sourcefile.
100 Returns None if obj is not defined in a sourcefile.
101 """
101 """
102 ofile = find_file(obj)
102 ofile = find_file(obj)
103 # run contents of file through pager starting at line where the object
103 # run contents of file through pager starting at line where the object
104 # is defined, as long as the file isn't binary and is actually on the
104 # is defined, as long as the file isn't binary and is actually on the
105 # filesystem.
105 # filesystem.
106 if ofile is None:
106 if ofile is None:
107 return None
107 return None
108 elif ofile.endswith(('.so', '.dll', '.pyd')):
108 elif ofile.endswith(('.so', '.dll', '.pyd')):
109 return None
109 return None
110 elif not os.path.isfile(ofile):
110 elif not os.path.isfile(ofile):
111 return None
111 return None
112 else:
112 else:
113 # Print only text files, not extension binaries. Note that
113 # Print only text files, not extension binaries. Note that
114 # getsourcelines returns lineno with 1-offset and page() uses
114 # getsourcelines returns lineno with 1-offset and page() uses
115 # 0-offset, so we must adjust.
115 # 0-offset, so we must adjust.
116 buffer = stdlib_io.open(ofile, 'rb') # Tweaked to use io.open for Python 2
116 buffer = stdlib_io.open(ofile, 'rb') # Tweaked to use io.open for Python 2
117 encoding, lines = openpy.detect_encoding(buffer.readline)
117 encoding, lines = openpy.detect_encoding(buffer.readline)
118 return encoding
118 return encoding
119
119
120 def getdoc(obj):
120 def getdoc(obj):
121 """Stable wrapper around inspect.getdoc.
121 """Stable wrapper around inspect.getdoc.
122
122
123 This can't crash because of attribute problems.
123 This can't crash because of attribute problems.
124
124
125 It also attempts to call a getdoc() method on the given object. This
125 It also attempts to call a getdoc() method on the given object. This
126 allows objects which provide their docstrings via non-standard mechanisms
126 allows objects which provide their docstrings via non-standard mechanisms
127 (like Pyro proxies) to still be inspected by ipython's ? system."""
127 (like Pyro proxies) to still be inspected by ipython's ? system."""
128 # Allow objects to offer customized documentation via a getdoc method:
128 # Allow objects to offer customized documentation via a getdoc method:
129 try:
129 try:
130 ds = obj.getdoc()
130 ds = obj.getdoc()
131 except Exception:
131 except Exception:
132 pass
132 pass
133 else:
133 else:
134 # if we get extra info, we add it to the normal docstring.
134 # if we get extra info, we add it to the normal docstring.
135 if isinstance(ds, basestring):
135 if isinstance(ds, basestring):
136 return inspect.cleandoc(ds)
136 return inspect.cleandoc(ds)
137
137
138 try:
138 try:
139 docstr = inspect.getdoc(obj)
139 docstr = inspect.getdoc(obj)
140 encoding = get_encoding(obj)
140 encoding = get_encoding(obj)
141 return py3compat.cast_unicode(docstr, encoding=encoding)
141 return py3compat.cast_unicode(docstr, encoding=encoding)
142 except Exception:
142 except Exception:
143 # Harden against an inspect failure, which can occur with
143 # Harden against an inspect failure, which can occur with
144 # SWIG-wrapped extensions.
144 # SWIG-wrapped extensions.
145 raise
145 raise
146 return None
146 return None
147
147
148
148
149 def getsource(obj,is_binary=False):
149 def getsource(obj,is_binary=False):
150 """Wrapper around inspect.getsource.
150 """Wrapper around inspect.getsource.
151
151
152 This can be modified by other projects to provide customized source
152 This can be modified by other projects to provide customized source
153 extraction.
153 extraction.
154
154
155 Inputs:
155 Inputs:
156
156
157 - obj: an object whose source code we will attempt to extract.
157 - obj: an object whose source code we will attempt to extract.
158
158
159 Optional inputs:
159 Optional inputs:
160
160
161 - is_binary: whether the object is known to come from a binary source.
161 - is_binary: whether the object is known to come from a binary source.
162 This implementation will skip returning any output for binary objects, but
162 This implementation will skip returning any output for binary objects, but
163 custom extractors may know how to meaningfully process them."""
163 custom extractors may know how to meaningfully process them."""
164
164
165 if is_binary:
165 if is_binary:
166 return None
166 return None
167 else:
167 else:
168 # get source if obj was decorated with @decorator
168 # get source if obj was decorated with @decorator
169 if hasattr(obj,"__wrapped__"):
169 if hasattr(obj,"__wrapped__"):
170 obj = obj.__wrapped__
170 obj = obj.__wrapped__
171 try:
171 try:
172 src = inspect.getsource(obj)
172 src = inspect.getsource(obj)
173 except TypeError:
173 except TypeError:
174 if hasattr(obj,'__class__'):
174 if hasattr(obj,'__class__'):
175 src = inspect.getsource(obj.__class__)
175 src = inspect.getsource(obj.__class__)
176 encoding = get_encoding(obj)
176 encoding = get_encoding(obj)
177 return cast_unicode(src, encoding=encoding)
177 return cast_unicode(src, encoding=encoding)
178
178
179 def getargspec(obj):
179 def getargspec(obj):
180 """Get the names and default values of a function's arguments.
180 """Get the names and default values of a function's arguments.
181
181
182 A tuple of four things is returned: (args, varargs, varkw, defaults).
182 A tuple of four things is returned: (args, varargs, varkw, defaults).
183 'args' is a list of the argument names (it may contain nested lists).
183 'args' is a list of the argument names (it may contain nested lists).
184 'varargs' and 'varkw' are the names of the * and ** arguments or None.
184 'varargs' and 'varkw' are the names of the * and ** arguments or None.
185 'defaults' is an n-tuple of the default values of the last n arguments.
185 'defaults' is an n-tuple of the default values of the last n arguments.
186
186
187 Modified version of inspect.getargspec from the Python Standard
187 Modified version of inspect.getargspec from the Python Standard
188 Library."""
188 Library."""
189
189
190 if inspect.isfunction(obj):
190 if inspect.isfunction(obj):
191 func_obj = obj
191 func_obj = obj
192 elif inspect.ismethod(obj):
192 elif inspect.ismethod(obj):
193 func_obj = obj.im_func
193 func_obj = obj.im_func
194 elif hasattr(obj, '__call__'):
194 elif hasattr(obj, '__call__'):
195 func_obj = obj.__call__
195 func_obj = obj.__call__
196 else:
196 else:
197 raise TypeError('arg is not a Python function')
197 raise TypeError('arg is not a Python function')
198 args, varargs, varkw = inspect.getargs(func_obj.func_code)
198 args, varargs, varkw = inspect.getargs(func_obj.func_code)
199 return args, varargs, varkw, func_obj.func_defaults
199 return args, varargs, varkw, func_obj.func_defaults
200
200
201
201
202 def format_argspec(argspec):
202 def format_argspec(argspec):
203 """Format argspect, convenience wrapper around inspect's.
203 """Format argspect, convenience wrapper around inspect's.
204
204
205 This takes a dict instead of ordered arguments and calls
205 This takes a dict instead of ordered arguments and calls
206 inspect.format_argspec with the arguments in the necessary order.
206 inspect.format_argspec with the arguments in the necessary order.
207 """
207 """
208 return inspect.formatargspec(argspec['args'], argspec['varargs'],
208 return inspect.formatargspec(argspec['args'], argspec['varargs'],
209 argspec['varkw'], argspec['defaults'])
209 argspec['varkw'], argspec['defaults'])
210
210
211
211
212 def call_tip(oinfo, format_call=True):
212 def call_tip(oinfo, format_call=True):
213 """Extract call tip data from an oinfo dict.
213 """Extract call tip data from an oinfo dict.
214
214
215 Parameters
215 Parameters
216 ----------
216 ----------
217 oinfo : dict
217 oinfo : dict
218
218
219 format_call : bool, optional
219 format_call : bool, optional
220 If True, the call line is formatted and returned as a string. If not, a
220 If True, the call line is formatted and returned as a string. If not, a
221 tuple of (name, argspec) is returned.
221 tuple of (name, argspec) is returned.
222
222
223 Returns
223 Returns
224 -------
224 -------
225 call_info : None, str or (str, dict) tuple.
225 call_info : None, str or (str, dict) tuple.
226 When format_call is True, the whole call information is formattted as a
226 When format_call is True, the whole call information is formattted as a
227 single string. Otherwise, the object's name and its argspec dict are
227 single string. Otherwise, the object's name and its argspec dict are
228 returned. If no call information is available, None is returned.
228 returned. If no call information is available, None is returned.
229
229
230 docstring : str or None
230 docstring : str or None
231 The most relevant docstring for calling purposes is returned, if
231 The most relevant docstring for calling purposes is returned, if
232 available. The priority is: call docstring for callable instances, then
232 available. The priority is: call docstring for callable instances, then
233 constructor docstring for classes, then main object's docstring otherwise
233 constructor docstring for classes, then main object's docstring otherwise
234 (regular functions).
234 (regular functions).
235 """
235 """
236 # Get call definition
236 # Get call definition
237 argspec = oinfo.get('argspec')
237 argspec = oinfo.get('argspec')
238 if argspec is None:
238 if argspec is None:
239 call_line = None
239 call_line = None
240 else:
240 else:
241 # Callable objects will have 'self' as their first argument, prune
241 # Callable objects will have 'self' as their first argument, prune
242 # it out if it's there for clarity (since users do *not* pass an
242 # it out if it's there for clarity (since users do *not* pass an
243 # extra first argument explicitly).
243 # extra first argument explicitly).
244 try:
244 try:
245 has_self = argspec['args'][0] == 'self'
245 has_self = argspec['args'][0] == 'self'
246 except (KeyError, IndexError):
246 except (KeyError, IndexError):
247 pass
247 pass
248 else:
248 else:
249 if has_self:
249 if has_self:
250 argspec['args'] = argspec['args'][1:]
250 argspec['args'] = argspec['args'][1:]
251
251
252 call_line = oinfo['name']+format_argspec(argspec)
252 call_line = oinfo['name']+format_argspec(argspec)
253
253
254 # Now get docstring.
254 # Now get docstring.
255 # The priority is: call docstring, constructor docstring, main one.
255 # The priority is: call docstring, constructor docstring, main one.
256 doc = oinfo.get('call_docstring')
256 doc = oinfo.get('call_docstring')
257 if doc is None:
257 if doc is None:
258 doc = oinfo.get('init_docstring')
258 doc = oinfo.get('init_docstring')
259 if doc is None:
259 if doc is None:
260 doc = oinfo.get('docstring','')
260 doc = oinfo.get('docstring','')
261
261
262 return call_line, doc
262 return call_line, doc
263
263
264
264
265 def find_file(obj):
265 def find_file(obj):
266 """Find the absolute path to the file where an object was defined.
266 """Find the absolute path to the file where an object was defined.
267
267
268 This is essentially a robust wrapper around `inspect.getabsfile`.
268 This is essentially a robust wrapper around `inspect.getabsfile`.
269
269
270 Returns None if no file can be found.
270 Returns None if no file can be found.
271
271
272 Parameters
272 Parameters
273 ----------
273 ----------
274 obj : any Python object
274 obj : any Python object
275
275
276 Returns
276 Returns
277 -------
277 -------
278 fname : str
278 fname : str
279 The absolute path to the file where the object was defined.
279 The absolute path to the file where the object was defined.
280 """
280 """
281 # get source if obj was decorated with @decorator
281 # get source if obj was decorated with @decorator
282 if hasattr(obj, '__wrapped__'):
282 if hasattr(obj, '__wrapped__'):
283 obj = obj.__wrapped__
283 obj = obj.__wrapped__
284
284
285 fname = None
285 fname = None
286 try:
286 try:
287 fname = inspect.getabsfile(obj)
287 fname = inspect.getabsfile(obj)
288 except TypeError:
288 except TypeError:
289 # For an instance, the file that matters is where its class was
289 # For an instance, the file that matters is where its class was
290 # declared.
290 # declared.
291 if hasattr(obj, '__class__'):
291 if hasattr(obj, '__class__'):
292 try:
292 try:
293 fname = inspect.getabsfile(obj.__class__)
293 fname = inspect.getabsfile(obj.__class__)
294 except TypeError:
294 except TypeError:
295 # Can happen for builtins
295 # Can happen for builtins
296 pass
296 pass
297 except:
297 except:
298 pass
298 pass
299 return fname
299 return fname
300
300
301
301
302 def find_source_lines(obj):
302 def find_source_lines(obj):
303 """Find the line number in a file where an object was defined.
303 """Find the line number in a file where an object was defined.
304
304
305 This is essentially a robust wrapper around `inspect.getsourcelines`.
305 This is essentially a robust wrapper around `inspect.getsourcelines`.
306
306
307 Returns None if no file can be found.
307 Returns None if no file can be found.
308
308
309 Parameters
309 Parameters
310 ----------
310 ----------
311 obj : any Python object
311 obj : any Python object
312
312
313 Returns
313 Returns
314 -------
314 -------
315 lineno : int
315 lineno : int
316 The line number where the object definition starts.
316 The line number where the object definition starts.
317 """
317 """
318 # get source if obj was decorated with @decorator
318 # get source if obj was decorated with @decorator
319 if hasattr(obj, '__wrapped__'):
319 if hasattr(obj, '__wrapped__'):
320 obj = obj.__wrapped__
320 obj = obj.__wrapped__
321
321
322 try:
322 try:
323 try:
323 try:
324 lineno = inspect.getsourcelines(obj)[1]
324 lineno = inspect.getsourcelines(obj)[1]
325 except TypeError:
325 except TypeError:
326 # For instances, try the class object like getsource() does
326 # For instances, try the class object like getsource() does
327 if hasattr(obj, '__class__'):
327 if hasattr(obj, '__class__'):
328 lineno = inspect.getsourcelines(obj.__class__)[1]
328 lineno = inspect.getsourcelines(obj.__class__)[1]
329 except:
329 except:
330 return None
330 return None
331
331
332 return lineno
332 return lineno
333
333
334
334
335 class Inspector:
335 class Inspector:
336 def __init__(self, color_table=InspectColors,
336 def __init__(self, color_table=InspectColors,
337 code_color_table=PyColorize.ANSICodeColors,
337 code_color_table=PyColorize.ANSICodeColors,
338 scheme='NoColor',
338 scheme='NoColor',
339 str_detail_level=0):
339 str_detail_level=0):
340 self.color_table = color_table
340 self.color_table = color_table
341 self.parser = PyColorize.Parser(code_color_table,out='str')
341 self.parser = PyColorize.Parser(code_color_table,out='str')
342 self.format = self.parser.format
342 self.format = self.parser.format
343 self.str_detail_level = str_detail_level
343 self.str_detail_level = str_detail_level
344 self.set_active_scheme(scheme)
344 self.set_active_scheme(scheme)
345
345
346 def _getdef(self,obj,oname=''):
346 def _getdef(self,obj,oname=''):
347 """Return the definition header for any callable object.
347 """Return the call signature for any callable object.
348
348
349 If any exception is generated, None is returned instead and the
349 If any exception is generated, None is returned instead and the
350 exception is suppressed."""
350 exception is suppressed."""
351
351
352 try:
352 try:
353 hdef = oname + inspect.formatargspec(*getargspec(obj))
353 hdef = oname + inspect.formatargspec(*getargspec(obj))
354 return cast_unicode(hdef)
354 return cast_unicode(hdef)
355 except:
355 except:
356 return None
356 return None
357
357
358 def __head(self,h):
358 def __head(self,h):
359 """Return a header string with proper colors."""
359 """Return a header string with proper colors."""
360 return '%s%s%s' % (self.color_table.active_colors.header,h,
360 return '%s%s%s' % (self.color_table.active_colors.header,h,
361 self.color_table.active_colors.normal)
361 self.color_table.active_colors.normal)
362
362
363 def set_active_scheme(self, scheme):
363 def set_active_scheme(self, scheme):
364 self.color_table.set_active_scheme(scheme)
364 self.color_table.set_active_scheme(scheme)
365 self.parser.color_table.set_active_scheme(scheme)
365 self.parser.color_table.set_active_scheme(scheme)
366
366
367 def noinfo(self, msg, oname):
367 def noinfo(self, msg, oname):
368 """Generic message when no information is found."""
368 """Generic message when no information is found."""
369 print('No %s found' % msg, end=' ')
369 print('No %s found' % msg, end=' ')
370 if oname:
370 if oname:
371 print('for %s' % oname)
371 print('for %s' % oname)
372 else:
372 else:
373 print()
373 print()
374
374
375 def pdef(self, obj, oname=''):
375 def pdef(self, obj, oname=''):
376 """Print the definition header for any callable object.
376 """Print the call signature for any callable object.
377
377
378 If the object is a class, print the constructor information."""
378 If the object is a class, print the constructor information."""
379
379
380 if not callable(obj):
380 if not callable(obj):
381 print('Object is not callable.')
381 print('Object is not callable.')
382 return
382 return
383
383
384 header = ''
384 header = ''
385
385
386 if inspect.isclass(obj):
386 if inspect.isclass(obj):
387 header = self.__head('Class constructor information:\n')
387 header = self.__head('Class constructor information:\n')
388 obj = obj.__init__
388 obj = obj.__init__
389 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
389 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
390 obj = obj.__call__
390 obj = obj.__call__
391
391
392 output = self._getdef(obj,oname)
392 output = self._getdef(obj,oname)
393 if output is None:
393 if output is None:
394 self.noinfo('definition header',oname)
394 self.noinfo('definition header',oname)
395 else:
395 else:
396 print(header,self.format(output), end=' ', file=io.stdout)
396 print(header,self.format(output), end=' ', file=io.stdout)
397
397
398 # In Python 3, all classes are new-style, so they all have __init__.
398 # In Python 3, all classes are new-style, so they all have __init__.
399 @skip_doctest_py3
399 @skip_doctest_py3
400 def pdoc(self,obj,oname='',formatter = None):
400 def pdoc(self,obj,oname='',formatter = None):
401 """Print the docstring for any object.
401 """Print the docstring for any object.
402
402
403 Optional:
403 Optional:
404 -formatter: a function to run the docstring through for specially
404 -formatter: a function to run the docstring through for specially
405 formatted docstrings.
405 formatted docstrings.
406
406
407 Examples
407 Examples
408 --------
408 --------
409
409
410 In [1]: class NoInit:
410 In [1]: class NoInit:
411 ...: pass
411 ...: pass
412
412
413 In [2]: class NoDoc:
413 In [2]: class NoDoc:
414 ...: def __init__(self):
414 ...: def __init__(self):
415 ...: pass
415 ...: pass
416
416
417 In [3]: %pdoc NoDoc
417 In [3]: %pdoc NoDoc
418 No documentation found for NoDoc
418 No documentation found for NoDoc
419
419
420 In [4]: %pdoc NoInit
420 In [4]: %pdoc NoInit
421 No documentation found for NoInit
421 No documentation found for NoInit
422
422
423 In [5]: obj = NoInit()
423 In [5]: obj = NoInit()
424
424
425 In [6]: %pdoc obj
425 In [6]: %pdoc obj
426 No documentation found for obj
426 No documentation found for obj
427
427
428 In [5]: obj2 = NoDoc()
428 In [5]: obj2 = NoDoc()
429
429
430 In [6]: %pdoc obj2
430 In [6]: %pdoc obj2
431 No documentation found for obj2
431 No documentation found for obj2
432 """
432 """
433
433
434 head = self.__head # For convenience
434 head = self.__head # For convenience
435 lines = []
435 lines = []
436 ds = getdoc(obj)
436 ds = getdoc(obj)
437 if formatter:
437 if formatter:
438 ds = formatter(ds)
438 ds = formatter(ds)
439 if ds:
439 if ds:
440 lines.append(head("Class Docstring:"))
440 lines.append(head("Class Docstring:"))
441 lines.append(indent(ds))
441 lines.append(indent(ds))
442 if inspect.isclass(obj) and hasattr(obj, '__init__'):
442 if inspect.isclass(obj) and hasattr(obj, '__init__'):
443 init_ds = getdoc(obj.__init__)
443 init_ds = getdoc(obj.__init__)
444 if init_ds is not None:
444 if init_ds is not None:
445 lines.append(head("Constructor Docstring:"))
445 lines.append(head("Constructor Docstring:"))
446 lines.append(indent(init_ds))
446 lines.append(indent(init_ds))
447 elif hasattr(obj,'__call__'):
447 elif hasattr(obj,'__call__'):
448 call_ds = getdoc(obj.__call__)
448 call_ds = getdoc(obj.__call__)
449 if call_ds:
449 if call_ds:
450 lines.append(head("Calling Docstring:"))
450 lines.append(head("Calling Docstring:"))
451 lines.append(indent(call_ds))
451 lines.append(indent(call_ds))
452
452
453 if not lines:
453 if not lines:
454 self.noinfo('documentation',oname)
454 self.noinfo('documentation',oname)
455 else:
455 else:
456 page.page('\n'.join(lines))
456 page.page('\n'.join(lines))
457
457
458 def psource(self,obj,oname=''):
458 def psource(self,obj,oname=''):
459 """Print the source code for an object."""
459 """Print the source code for an object."""
460
460
461 # Flush the source cache because inspect can return out-of-date source
461 # Flush the source cache because inspect can return out-of-date source
462 linecache.checkcache()
462 linecache.checkcache()
463 try:
463 try:
464 src = getsource(obj)
464 src = getsource(obj)
465 except:
465 except:
466 self.noinfo('source',oname)
466 self.noinfo('source',oname)
467 else:
467 else:
468 page.page(self.format(src))
468 page.page(self.format(src))
469
469
470 def pfile(self, obj, oname=''):
470 def pfile(self, obj, oname=''):
471 """Show the whole file where an object was defined."""
471 """Show the whole file where an object was defined."""
472
472
473 lineno = find_source_lines(obj)
473 lineno = find_source_lines(obj)
474 if lineno is None:
474 if lineno is None:
475 self.noinfo('file', oname)
475 self.noinfo('file', oname)
476 return
476 return
477
477
478 ofile = find_file(obj)
478 ofile = find_file(obj)
479 # run contents of file through pager starting at line where the object
479 # run contents of file through pager starting at line where the object
480 # is defined, as long as the file isn't binary and is actually on the
480 # is defined, as long as the file isn't binary and is actually on the
481 # filesystem.
481 # filesystem.
482 if ofile.endswith(('.so', '.dll', '.pyd')):
482 if ofile.endswith(('.so', '.dll', '.pyd')):
483 print('File %r is binary, not printing.' % ofile)
483 print('File %r is binary, not printing.' % ofile)
484 elif not os.path.isfile(ofile):
484 elif not os.path.isfile(ofile):
485 print('File %r does not exist, not printing.' % ofile)
485 print('File %r does not exist, not printing.' % ofile)
486 else:
486 else:
487 # Print only text files, not extension binaries. Note that
487 # Print only text files, not extension binaries. Note that
488 # getsourcelines returns lineno with 1-offset and page() uses
488 # getsourcelines returns lineno with 1-offset and page() uses
489 # 0-offset, so we must adjust.
489 # 0-offset, so we must adjust.
490 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
490 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
491
491
492 def _format_fields(self, fields, title_width=12):
492 def _format_fields(self, fields, title_width=12):
493 """Formats a list of fields for display.
493 """Formats a list of fields for display.
494
494
495 Parameters
495 Parameters
496 ----------
496 ----------
497 fields : list
497 fields : list
498 A list of 2-tuples: (field_title, field_content)
498 A list of 2-tuples: (field_title, field_content)
499 title_width : int
499 title_width : int
500 How many characters to pad titles to. Default 12.
500 How many characters to pad titles to. Default 12.
501 """
501 """
502 out = []
502 out = []
503 header = self.__head
503 header = self.__head
504 for title, content in fields:
504 for title, content in fields:
505 if len(content.splitlines()) > 1:
505 if len(content.splitlines()) > 1:
506 title = header(title + ":") + "\n"
506 title = header(title + ":") + "\n"
507 else:
507 else:
508 title = header((title+":").ljust(title_width))
508 title = header((title+":").ljust(title_width))
509 out.append(cast_unicode(title) + cast_unicode(content))
509 out.append(cast_unicode(title) + cast_unicode(content))
510 return "\n".join(out)
510 return "\n".join(out)
511
511
512 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
512 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
513 pinfo_fields1 = [("Type", "type_name"),
513 pinfo_fields1 = [("Type", "type_name"),
514 ]
514 ]
515
515
516 pinfo_fields2 = [("String Form", "string_form"),
516 pinfo_fields2 = [("String Form", "string_form"),
517 ]
517 ]
518
518
519 pinfo_fields3 = [("Length", "length"),
519 pinfo_fields3 = [("Length", "length"),
520 ("File", "file"),
520 ("File", "file"),
521 ("Definition", "definition"),
521 ("Definition", "definition"),
522 ]
522 ]
523
523
524 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
524 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
525 ("Constructor Docstring","init_docstring"),
525 ("Constructor Docstring","init_docstring"),
526 ("Call def", "call_def"),
526 ("Call def", "call_def"),
527 ("Call docstring", "call_docstring")]
527 ("Call docstring", "call_docstring")]
528
528
529 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
529 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
530 """Show detailed information about an object.
530 """Show detailed information about an object.
531
531
532 Optional arguments:
532 Optional arguments:
533
533
534 - oname: name of the variable pointing to the object.
534 - oname: name of the variable pointing to the object.
535
535
536 - formatter: special formatter for docstrings (see pdoc)
536 - formatter: special formatter for docstrings (see pdoc)
537
537
538 - info: a structure with some information fields which may have been
538 - info: a structure with some information fields which may have been
539 precomputed already.
539 precomputed already.
540
540
541 - detail_level: if set to 1, more information is given.
541 - detail_level: if set to 1, more information is given.
542 """
542 """
543 info = self.info(obj, oname=oname, formatter=formatter,
543 info = self.info(obj, oname=oname, formatter=formatter,
544 info=info, detail_level=detail_level)
544 info=info, detail_level=detail_level)
545 displayfields = []
545 displayfields = []
546 def add_fields(fields):
546 def add_fields(fields):
547 for title, key in fields:
547 for title, key in fields:
548 field = info[key]
548 field = info[key]
549 if field is not None:
549 if field is not None:
550 displayfields.append((title, field.rstrip()))
550 displayfields.append((title, field.rstrip()))
551
551
552 add_fields(self.pinfo_fields1)
552 add_fields(self.pinfo_fields1)
553
553
554 # Base class for old-style instances
554 # Base class for old-style instances
555 if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']:
555 if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']:
556 displayfields.append(("Base Class", info['base_class'].rstrip()))
556 displayfields.append(("Base Class", info['base_class'].rstrip()))
557
557
558 add_fields(self.pinfo_fields2)
558 add_fields(self.pinfo_fields2)
559
559
560 # Namespace
560 # Namespace
561 if info['namespace'] != 'Interactive':
561 if info['namespace'] != 'Interactive':
562 displayfields.append(("Namespace", info['namespace'].rstrip()))
562 displayfields.append(("Namespace", info['namespace'].rstrip()))
563
563
564 add_fields(self.pinfo_fields3)
564 add_fields(self.pinfo_fields3)
565
565
566 # Source or docstring, depending on detail level and whether
566 # Source or docstring, depending on detail level and whether
567 # source found.
567 # source found.
568 if detail_level > 0 and info['source'] is not None:
568 if detail_level > 0 and info['source'] is not None:
569 displayfields.append(("Source",
569 displayfields.append(("Source",
570 self.format(cast_unicode(info['source']))))
570 self.format(cast_unicode(info['source']))))
571 elif info['docstring'] is not None:
571 elif info['docstring'] is not None:
572 displayfields.append(("Docstring", info["docstring"]))
572 displayfields.append(("Docstring", info["docstring"]))
573
573
574 # Constructor info for classes
574 # Constructor info for classes
575 if info['isclass']:
575 if info['isclass']:
576 if info['init_definition'] or info['init_docstring']:
576 if info['init_definition'] or info['init_docstring']:
577 displayfields.append(("Constructor information", ""))
577 displayfields.append(("Constructor information", ""))
578 if info['init_definition'] is not None:
578 if info['init_definition'] is not None:
579 displayfields.append((" Definition",
579 displayfields.append((" Definition",
580 info['init_definition'].rstrip()))
580 info['init_definition'].rstrip()))
581 if info['init_docstring'] is not None:
581 if info['init_docstring'] is not None:
582 displayfields.append((" Docstring",
582 displayfields.append((" Docstring",
583 indent(info['init_docstring'])))
583 indent(info['init_docstring'])))
584
584
585 # Info for objects:
585 # Info for objects:
586 else:
586 else:
587 add_fields(self.pinfo_fields_obj)
587 add_fields(self.pinfo_fields_obj)
588
588
589 # Finally send to printer/pager:
589 # Finally send to printer/pager:
590 if displayfields:
590 if displayfields:
591 page.page(self._format_fields(displayfields))
591 page.page(self._format_fields(displayfields))
592
592
593 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
593 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
594 """Compute a dict with detailed information about an object.
594 """Compute a dict with detailed information about an object.
595
595
596 Optional arguments:
596 Optional arguments:
597
597
598 - oname: name of the variable pointing to the object.
598 - oname: name of the variable pointing to the object.
599
599
600 - formatter: special formatter for docstrings (see pdoc)
600 - formatter: special formatter for docstrings (see pdoc)
601
601
602 - info: a structure with some information fields which may have been
602 - info: a structure with some information fields which may have been
603 precomputed already.
603 precomputed already.
604
604
605 - detail_level: if set to 1, more information is given.
605 - detail_level: if set to 1, more information is given.
606 """
606 """
607
607
608 obj_type = type(obj)
608 obj_type = type(obj)
609
609
610 header = self.__head
610 header = self.__head
611 if info is None:
611 if info is None:
612 ismagic = 0
612 ismagic = 0
613 isalias = 0
613 isalias = 0
614 ospace = ''
614 ospace = ''
615 else:
615 else:
616 ismagic = info.ismagic
616 ismagic = info.ismagic
617 isalias = info.isalias
617 isalias = info.isalias
618 ospace = info.namespace
618 ospace = info.namespace
619
619
620 # Get docstring, special-casing aliases:
620 # Get docstring, special-casing aliases:
621 if isalias:
621 if isalias:
622 if not callable(obj):
622 if not callable(obj):
623 try:
623 try:
624 ds = "Alias to the system command:\n %s" % obj[1]
624 ds = "Alias to the system command:\n %s" % obj[1]
625 except:
625 except:
626 ds = "Alias: " + str(obj)
626 ds = "Alias: " + str(obj)
627 else:
627 else:
628 ds = "Alias to " + str(obj)
628 ds = "Alias to " + str(obj)
629 if obj.__doc__:
629 if obj.__doc__:
630 ds += "\nDocstring:\n" + obj.__doc__
630 ds += "\nDocstring:\n" + obj.__doc__
631 else:
631 else:
632 ds = getdoc(obj)
632 ds = getdoc(obj)
633 if ds is None:
633 if ds is None:
634 ds = '<no docstring>'
634 ds = '<no docstring>'
635 if formatter is not None:
635 if formatter is not None:
636 ds = formatter(ds)
636 ds = formatter(ds)
637
637
638 # store output in a dict, we initialize it here and fill it as we go
638 # store output in a dict, we initialize it here and fill it as we go
639 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
639 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
640
640
641 string_max = 200 # max size of strings to show (snipped if longer)
641 string_max = 200 # max size of strings to show (snipped if longer)
642 shalf = int((string_max -5)/2)
642 shalf = int((string_max -5)/2)
643
643
644 if ismagic:
644 if ismagic:
645 obj_type_name = 'Magic function'
645 obj_type_name = 'Magic function'
646 elif isalias:
646 elif isalias:
647 obj_type_name = 'System alias'
647 obj_type_name = 'System alias'
648 else:
648 else:
649 obj_type_name = obj_type.__name__
649 obj_type_name = obj_type.__name__
650 out['type_name'] = obj_type_name
650 out['type_name'] = obj_type_name
651
651
652 try:
652 try:
653 bclass = obj.__class__
653 bclass = obj.__class__
654 out['base_class'] = str(bclass)
654 out['base_class'] = str(bclass)
655 except: pass
655 except: pass
656
656
657 # String form, but snip if too long in ? form (full in ??)
657 # String form, but snip if too long in ? form (full in ??)
658 if detail_level >= self.str_detail_level:
658 if detail_level >= self.str_detail_level:
659 try:
659 try:
660 ostr = str(obj)
660 ostr = str(obj)
661 str_head = 'string_form'
661 str_head = 'string_form'
662 if not detail_level and len(ostr)>string_max:
662 if not detail_level and len(ostr)>string_max:
663 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
663 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
664 ostr = ("\n" + " " * len(str_head.expandtabs())).\
664 ostr = ("\n" + " " * len(str_head.expandtabs())).\
665 join(q.strip() for q in ostr.split("\n"))
665 join(q.strip() for q in ostr.split("\n"))
666 out[str_head] = ostr
666 out[str_head] = ostr
667 except:
667 except:
668 pass
668 pass
669
669
670 if ospace:
670 if ospace:
671 out['namespace'] = ospace
671 out['namespace'] = ospace
672
672
673 # Length (for strings and lists)
673 # Length (for strings and lists)
674 try:
674 try:
675 out['length'] = str(len(obj))
675 out['length'] = str(len(obj))
676 except: pass
676 except: pass
677
677
678 # Filename where object was defined
678 # Filename where object was defined
679 binary_file = False
679 binary_file = False
680 fname = find_file(obj)
680 fname = find_file(obj)
681 if fname is None:
681 if fname is None:
682 # if anything goes wrong, we don't want to show source, so it's as
682 # if anything goes wrong, we don't want to show source, so it's as
683 # if the file was binary
683 # if the file was binary
684 binary_file = True
684 binary_file = True
685 else:
685 else:
686 if fname.endswith(('.so', '.dll', '.pyd')):
686 if fname.endswith(('.so', '.dll', '.pyd')):
687 binary_file = True
687 binary_file = True
688 elif fname.endswith('<string>'):
688 elif fname.endswith('<string>'):
689 fname = 'Dynamically generated function. No source code available.'
689 fname = 'Dynamically generated function. No source code available.'
690 out['file'] = fname
690 out['file'] = fname
691
691
692 # reconstruct the function definition and print it:
692 # reconstruct the function definition and print it:
693 defln = self._getdef(obj, oname)
693 defln = self._getdef(obj, oname)
694 if defln:
694 if defln:
695 out['definition'] = self.format(defln)
695 out['definition'] = self.format(defln)
696
696
697 # Docstrings only in detail 0 mode, since source contains them (we
697 # Docstrings only in detail 0 mode, since source contains them (we
698 # avoid repetitions). If source fails, we add them back, see below.
698 # avoid repetitions). If source fails, we add them back, see below.
699 if ds and detail_level == 0:
699 if ds and detail_level == 0:
700 out['docstring'] = ds
700 out['docstring'] = ds
701
701
702 # Original source code for any callable
702 # Original source code for any callable
703 if detail_level:
703 if detail_level:
704 # Flush the source cache because inspect can return out-of-date
704 # Flush the source cache because inspect can return out-of-date
705 # source
705 # source
706 linecache.checkcache()
706 linecache.checkcache()
707 source = None
707 source = None
708 try:
708 try:
709 try:
709 try:
710 source = getsource(obj, binary_file)
710 source = getsource(obj, binary_file)
711 except TypeError:
711 except TypeError:
712 if hasattr(obj, '__class__'):
712 if hasattr(obj, '__class__'):
713 source = getsource(obj.__class__, binary_file)
713 source = getsource(obj.__class__, binary_file)
714 if source is not None:
714 if source is not None:
715 out['source'] = source.rstrip()
715 out['source'] = source.rstrip()
716 except Exception:
716 except Exception:
717 pass
717 pass
718
718
719 if ds and source is None:
719 if ds and source is None:
720 out['docstring'] = ds
720 out['docstring'] = ds
721
721
722
722
723 # Constructor docstring for classes
723 # Constructor docstring for classes
724 if inspect.isclass(obj):
724 if inspect.isclass(obj):
725 out['isclass'] = True
725 out['isclass'] = True
726 # reconstruct the function definition and print it:
726 # reconstruct the function definition and print it:
727 try:
727 try:
728 obj_init = obj.__init__
728 obj_init = obj.__init__
729 except AttributeError:
729 except AttributeError:
730 init_def = init_ds = None
730 init_def = init_ds = None
731 else:
731 else:
732 init_def = self._getdef(obj_init,oname)
732 init_def = self._getdef(obj_init,oname)
733 init_ds = getdoc(obj_init)
733 init_ds = getdoc(obj_init)
734 # Skip Python's auto-generated docstrings
734 # Skip Python's auto-generated docstrings
735 if init_ds and \
735 if init_ds and \
736 init_ds.startswith('x.__init__(...) initializes'):
736 init_ds.startswith('x.__init__(...) initializes'):
737 init_ds = None
737 init_ds = None
738
738
739 if init_def or init_ds:
739 if init_def or init_ds:
740 if init_def:
740 if init_def:
741 out['init_definition'] = self.format(init_def)
741 out['init_definition'] = self.format(init_def)
742 if init_ds:
742 if init_ds:
743 out['init_docstring'] = init_ds
743 out['init_docstring'] = init_ds
744
744
745 # and class docstring for instances:
745 # and class docstring for instances:
746 else:
746 else:
747 # First, check whether the instance docstring is identical to the
747 # First, check whether the instance docstring is identical to the
748 # class one, and print it separately if they don't coincide. In
748 # class one, and print it separately if they don't coincide. In
749 # most cases they will, but it's nice to print all the info for
749 # most cases they will, but it's nice to print all the info for
750 # objects which use instance-customized docstrings.
750 # objects which use instance-customized docstrings.
751 if ds:
751 if ds:
752 try:
752 try:
753 cls = getattr(obj,'__class__')
753 cls = getattr(obj,'__class__')
754 except:
754 except:
755 class_ds = None
755 class_ds = None
756 else:
756 else:
757 class_ds = getdoc(cls)
757 class_ds = getdoc(cls)
758 # Skip Python's auto-generated docstrings
758 # Skip Python's auto-generated docstrings
759 if class_ds and \
759 if class_ds and \
760 (class_ds.startswith('function(code, globals[,') or \
760 (class_ds.startswith('function(code, globals[,') or \
761 class_ds.startswith('instancemethod(function, instance,') or \
761 class_ds.startswith('instancemethod(function, instance,') or \
762 class_ds.startswith('module(name[,') ):
762 class_ds.startswith('module(name[,') ):
763 class_ds = None
763 class_ds = None
764 if class_ds and ds != class_ds:
764 if class_ds and ds != class_ds:
765 out['class_docstring'] = class_ds
765 out['class_docstring'] = class_ds
766
766
767 # Next, try to show constructor docstrings
767 # Next, try to show constructor docstrings
768 try:
768 try:
769 init_ds = getdoc(obj.__init__)
769 init_ds = getdoc(obj.__init__)
770 # Skip Python's auto-generated docstrings
770 # Skip Python's auto-generated docstrings
771 if init_ds and \
771 if init_ds and \
772 init_ds.startswith('x.__init__(...) initializes'):
772 init_ds.startswith('x.__init__(...) initializes'):
773 init_ds = None
773 init_ds = None
774 except AttributeError:
774 except AttributeError:
775 init_ds = None
775 init_ds = None
776 if init_ds:
776 if init_ds:
777 out['init_docstring'] = init_ds
777 out['init_docstring'] = init_ds
778
778
779 # Call form docstring for callable instances
779 # Call form docstring for callable instances
780 if hasattr(obj, '__call__'):
780 if hasattr(obj, '__call__'):
781 call_def = self._getdef(obj.__call__, oname)
781 call_def = self._getdef(obj.__call__, oname)
782 if call_def is not None:
782 if call_def is not None:
783 out['call_def'] = self.format(call_def)
783 out['call_def'] = self.format(call_def)
784 call_ds = getdoc(obj.__call__)
784 call_ds = getdoc(obj.__call__)
785 # Skip Python's auto-generated docstrings
785 # Skip Python's auto-generated docstrings
786 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
786 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
787 call_ds = None
787 call_ds = None
788 if call_ds:
788 if call_ds:
789 out['call_docstring'] = call_ds
789 out['call_docstring'] = call_ds
790
790
791 # Compute the object's argspec as a callable. The key is to decide
791 # Compute the object's argspec as a callable. The key is to decide
792 # whether to pull it from the object itself, from its __init__ or
792 # whether to pull it from the object itself, from its __init__ or
793 # from its __call__ method.
793 # from its __call__ method.
794
794
795 if inspect.isclass(obj):
795 if inspect.isclass(obj):
796 # Old-style classes need not have an __init__
796 # Old-style classes need not have an __init__
797 callable_obj = getattr(obj, "__init__", None)
797 callable_obj = getattr(obj, "__init__", None)
798 elif callable(obj):
798 elif callable(obj):
799 callable_obj = obj
799 callable_obj = obj
800 else:
800 else:
801 callable_obj = None
801 callable_obj = None
802
802
803 if callable_obj:
803 if callable_obj:
804 try:
804 try:
805 args, varargs, varkw, defaults = getargspec(callable_obj)
805 args, varargs, varkw, defaults = getargspec(callable_obj)
806 except (TypeError, AttributeError):
806 except (TypeError, AttributeError):
807 # For extensions/builtins we can't retrieve the argspec
807 # For extensions/builtins we can't retrieve the argspec
808 pass
808 pass
809 else:
809 else:
810 out['argspec'] = dict(args=args, varargs=varargs,
810 out['argspec'] = dict(args=args, varargs=varargs,
811 varkw=varkw, defaults=defaults)
811 varkw=varkw, defaults=defaults)
812
812
813 return object_info(**out)
813 return object_info(**out)
814
814
815
815
816 def psearch(self,pattern,ns_table,ns_search=[],
816 def psearch(self,pattern,ns_table,ns_search=[],
817 ignore_case=False,show_all=False):
817 ignore_case=False,show_all=False):
818 """Search namespaces with wildcards for objects.
818 """Search namespaces with wildcards for objects.
819
819
820 Arguments:
820 Arguments:
821
821
822 - pattern: string containing shell-like wildcards to use in namespace
822 - pattern: string containing shell-like wildcards to use in namespace
823 searches and optionally a type specification to narrow the search to
823 searches and optionally a type specification to narrow the search to
824 objects of that type.
824 objects of that type.
825
825
826 - ns_table: dict of name->namespaces for search.
826 - ns_table: dict of name->namespaces for search.
827
827
828 Optional arguments:
828 Optional arguments:
829
829
830 - ns_search: list of namespace names to include in search.
830 - ns_search: list of namespace names to include in search.
831
831
832 - ignore_case(False): make the search case-insensitive.
832 - ignore_case(False): make the search case-insensitive.
833
833
834 - show_all(False): show all names, including those starting with
834 - show_all(False): show all names, including those starting with
835 underscores.
835 underscores.
836 """
836 """
837 #print 'ps pattern:<%r>' % pattern # dbg
837 #print 'ps pattern:<%r>' % pattern # dbg
838
838
839 # defaults
839 # defaults
840 type_pattern = 'all'
840 type_pattern = 'all'
841 filter = ''
841 filter = ''
842
842
843 cmds = pattern.split()
843 cmds = pattern.split()
844 len_cmds = len(cmds)
844 len_cmds = len(cmds)
845 if len_cmds == 1:
845 if len_cmds == 1:
846 # Only filter pattern given
846 # Only filter pattern given
847 filter = cmds[0]
847 filter = cmds[0]
848 elif len_cmds == 2:
848 elif len_cmds == 2:
849 # Both filter and type specified
849 # Both filter and type specified
850 filter,type_pattern = cmds
850 filter,type_pattern = cmds
851 else:
851 else:
852 raise ValueError('invalid argument string for psearch: <%s>' %
852 raise ValueError('invalid argument string for psearch: <%s>' %
853 pattern)
853 pattern)
854
854
855 # filter search namespaces
855 # filter search namespaces
856 for name in ns_search:
856 for name in ns_search:
857 if name not in ns_table:
857 if name not in ns_table:
858 raise ValueError('invalid namespace <%s>. Valid names: %s' %
858 raise ValueError('invalid namespace <%s>. Valid names: %s' %
859 (name,ns_table.keys()))
859 (name,ns_table.keys()))
860
860
861 #print 'type_pattern:',type_pattern # dbg
861 #print 'type_pattern:',type_pattern # dbg
862 search_result, namespaces_seen = set(), set()
862 search_result, namespaces_seen = set(), set()
863 for ns_name in ns_search:
863 for ns_name in ns_search:
864 ns = ns_table[ns_name]
864 ns = ns_table[ns_name]
865 # Normally, locals and globals are the same, so we just check one.
865 # Normally, locals and globals are the same, so we just check one.
866 if id(ns) in namespaces_seen:
866 if id(ns) in namespaces_seen:
867 continue
867 continue
868 namespaces_seen.add(id(ns))
868 namespaces_seen.add(id(ns))
869 tmp_res = list_namespace(ns, type_pattern, filter,
869 tmp_res = list_namespace(ns, type_pattern, filter,
870 ignore_case=ignore_case, show_all=show_all)
870 ignore_case=ignore_case, show_all=show_all)
871 search_result.update(tmp_res)
871 search_result.update(tmp_res)
872
872
873 page.page('\n'.join(sorted(search_result)))
873 page.page('\n'.join(sorted(search_result)))
@@ -1,272 +1,300 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for code execution (%run and related), which is particularly tricky.
2 """Tests for code execution (%run and related), which is particularly tricky.
3
3
4 Because of how %run manages namespaces, and the fact that we are trying here to
4 Because of how %run manages namespaces, and the fact that we are trying here to
5 verify subtle object deletion and reference counting issues, the %run tests
5 verify subtle object deletion and reference counting issues, the %run tests
6 will be kept in this separate file. This makes it easier to aggregate in one
6 will be kept in this separate file. This makes it easier to aggregate in one
7 place the tricks needed to handle it; most other magics are much easier to test
7 place the tricks needed to handle it; most other magics are much easier to test
8 and we do so in a common test_magic file.
8 and we do so in a common test_magic file.
9 """
9 """
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 import os
16 import os
17 import sys
17 import sys
18 import tempfile
18 import tempfile
19
19
20 import nose.tools as nt
20 import nose.tools as nt
21 from nose import SkipTest
21 from nose import SkipTest
22
22
23 from IPython.testing import decorators as dec
23 from IPython.testing import decorators as dec
24 from IPython.testing import tools as tt
24 from IPython.testing import tools as tt
25 from IPython.utils import py3compat
25 from IPython.utils import py3compat
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Test functions begin
28 # Test functions begin
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 def doctest_refbug():
31 def doctest_refbug():
32 """Very nasty problem with references held by multiple runs of a script.
32 """Very nasty problem with references held by multiple runs of a script.
33 See: https://github.com/ipython/ipython/issues/141
33 See: https://github.com/ipython/ipython/issues/141
34
34
35 In [1]: _ip.clear_main_mod_cache()
35 In [1]: _ip.clear_main_mod_cache()
36 # random
36 # random
37
37
38 In [2]: %run refbug
38 In [2]: %run refbug
39
39
40 In [3]: call_f()
40 In [3]: call_f()
41 lowercased: hello
41 lowercased: hello
42
42
43 In [4]: %run refbug
43 In [4]: %run refbug
44
44
45 In [5]: call_f()
45 In [5]: call_f()
46 lowercased: hello
46 lowercased: hello
47 lowercased: hello
47 lowercased: hello
48 """
48 """
49
49
50
50
51 def doctest_run_builtins():
51 def doctest_run_builtins():
52 r"""Check that %run doesn't damage __builtins__.
52 r"""Check that %run doesn't damage __builtins__.
53
53
54 In [1]: import tempfile
54 In [1]: import tempfile
55
55
56 In [2]: bid1 = id(__builtins__)
56 In [2]: bid1 = id(__builtins__)
57
57
58 In [3]: fname = tempfile.mkstemp('.py')[1]
58 In [3]: fname = tempfile.mkstemp('.py')[1]
59
59
60 In [3]: f = open(fname,'w')
60 In [3]: f = open(fname,'w')
61
61
62 In [4]: dummy= f.write('pass\n')
62 In [4]: dummy= f.write('pass\n')
63
63
64 In [5]: f.flush()
64 In [5]: f.flush()
65
65
66 In [6]: t1 = type(__builtins__)
66 In [6]: t1 = type(__builtins__)
67
67
68 In [7]: %run $fname
68 In [7]: %run $fname
69
69
70 In [7]: f.close()
70 In [7]: f.close()
71
71
72 In [8]: bid2 = id(__builtins__)
72 In [8]: bid2 = id(__builtins__)
73
73
74 In [9]: t2 = type(__builtins__)
74 In [9]: t2 = type(__builtins__)
75
75
76 In [10]: t1 == t2
76 In [10]: t1 == t2
77 Out[10]: True
77 Out[10]: True
78
78
79 In [10]: bid1 == bid2
79 In [10]: bid1 == bid2
80 Out[10]: True
80 Out[10]: True
81
81
82 In [12]: try:
82 In [12]: try:
83 ....: os.unlink(fname)
83 ....: os.unlink(fname)
84 ....: except:
84 ....: except:
85 ....: pass
85 ....: pass
86 ....:
86 ....:
87 """
87 """
88
88
89
89
90 def doctest_run_option_parser():
90 def doctest_run_option_parser():
91 r"""Test option parser in %run.
91 r"""Test option parser in %run.
92
92
93 In [1]: %run print_argv.py
93 In [1]: %run print_argv.py
94 []
94 []
95
95
96 In [2]: %run print_argv.py print*.py
96 In [2]: %run print_argv.py print*.py
97 ['print_argv.py']
97 ['print_argv.py']
98
98
99 In [3]: %run print_argv.py print\\*.py
99 In [3]: %run -G print_argv.py print*.py
100 ['print*.py']
100 ['print*.py']
101
101
102 In [4]: %run print_argv.py 'print*.py'
102 """
103
104
105 @dec.skip_win32
106 def doctest_run_option_parser_for_posix():
107 r"""Test option parser in %run (Linux/OSX specific).
108
109 You need double quote to escape glob in POSIX systems:
110
111 In [1]: %run print_argv.py print\\*.py
112 ['print*.py']
113
114 You can't use quote to escape glob in POSIX systems:
115
116 In [2]: %run print_argv.py 'print*.py'
103 ['print_argv.py']
117 ['print_argv.py']
104
118
105 In [5]: %run -G print_argv.py print*.py
119 """
120
121
122 @dec.skip_if_not_win32
123 def doctest_run_option_parser_for_windows():
124 r"""Test option parser in %run (Windows specific).
125
126 In Windows, you can't escape ``*` `by backslash:
127
128 In [1]: %run print_argv.py print\\*.py
129 ['print\\*.py']
130
131 You can use quote to escape glob:
132
133 In [2]: %run print_argv.py 'print*.py'
106 ['print*.py']
134 ['print*.py']
107
135
108 """
136 """
109
137
110
138
111 @py3compat.doctest_refactor_print
139 @py3compat.doctest_refactor_print
112 def doctest_reset_del():
140 def doctest_reset_del():
113 """Test that resetting doesn't cause errors in __del__ methods.
141 """Test that resetting doesn't cause errors in __del__ methods.
114
142
115 In [2]: class A(object):
143 In [2]: class A(object):
116 ...: def __del__(self):
144 ...: def __del__(self):
117 ...: print str("Hi")
145 ...: print str("Hi")
118 ...:
146 ...:
119
147
120 In [3]: a = A()
148 In [3]: a = A()
121
149
122 In [4]: get_ipython().reset()
150 In [4]: get_ipython().reset()
123 Hi
151 Hi
124
152
125 In [5]: 1+1
153 In [5]: 1+1
126 Out[5]: 2
154 Out[5]: 2
127 """
155 """
128
156
129 # For some tests, it will be handy to organize them in a class with a common
157 # For some tests, it will be handy to organize them in a class with a common
130 # setup that makes a temp file
158 # setup that makes a temp file
131
159
132 class TestMagicRunPass(tt.TempFileMixin):
160 class TestMagicRunPass(tt.TempFileMixin):
133
161
134 def setup(self):
162 def setup(self):
135 """Make a valid python temp file."""
163 """Make a valid python temp file."""
136 self.mktmp('pass\n')
164 self.mktmp('pass\n')
137
165
138 def run_tmpfile(self):
166 def run_tmpfile(self):
139 _ip = get_ipython()
167 _ip = get_ipython()
140 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
168 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
141 # See below and ticket https://bugs.launchpad.net/bugs/366353
169 # See below and ticket https://bugs.launchpad.net/bugs/366353
142 _ip.magic('run %s' % self.fname)
170 _ip.magic('run %s' % self.fname)
143
171
144 def run_tmpfile_p(self):
172 def run_tmpfile_p(self):
145 _ip = get_ipython()
173 _ip = get_ipython()
146 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
174 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
147 # See below and ticket https://bugs.launchpad.net/bugs/366353
175 # See below and ticket https://bugs.launchpad.net/bugs/366353
148 _ip.magic('run -p %s' % self.fname)
176 _ip.magic('run -p %s' % self.fname)
149
177
150 def test_builtins_id(self):
178 def test_builtins_id(self):
151 """Check that %run doesn't damage __builtins__ """
179 """Check that %run doesn't damage __builtins__ """
152 _ip = get_ipython()
180 _ip = get_ipython()
153 # Test that the id of __builtins__ is not modified by %run
181 # Test that the id of __builtins__ is not modified by %run
154 bid1 = id(_ip.user_ns['__builtins__'])
182 bid1 = id(_ip.user_ns['__builtins__'])
155 self.run_tmpfile()
183 self.run_tmpfile()
156 bid2 = id(_ip.user_ns['__builtins__'])
184 bid2 = id(_ip.user_ns['__builtins__'])
157 nt.assert_equal(bid1, bid2)
185 nt.assert_equal(bid1, bid2)
158
186
159 def test_builtins_type(self):
187 def test_builtins_type(self):
160 """Check that the type of __builtins__ doesn't change with %run.
188 """Check that the type of __builtins__ doesn't change with %run.
161
189
162 However, the above could pass if __builtins__ was already modified to
190 However, the above could pass if __builtins__ was already modified to
163 be a dict (it should be a module) by a previous use of %run. So we
191 be a dict (it should be a module) by a previous use of %run. So we
164 also check explicitly that it really is a module:
192 also check explicitly that it really is a module:
165 """
193 """
166 _ip = get_ipython()
194 _ip = get_ipython()
167 self.run_tmpfile()
195 self.run_tmpfile()
168 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
196 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
169
197
170 def test_prompts(self):
198 def test_prompts(self):
171 """Test that prompts correctly generate after %run"""
199 """Test that prompts correctly generate after %run"""
172 self.run_tmpfile()
200 self.run_tmpfile()
173 _ip = get_ipython()
201 _ip = get_ipython()
174 p2 = _ip.prompt_manager.render('in2').strip()
202 p2 = _ip.prompt_manager.render('in2').strip()
175 nt.assert_equal(p2[:3], '...')
203 nt.assert_equal(p2[:3], '...')
176
204
177 def test_run_profile( self ):
205 def test_run_profile( self ):
178 """Test that the option -p, which invokes the profiler, do not
206 """Test that the option -p, which invokes the profiler, do not
179 crash by invoking execfile"""
207 crash by invoking execfile"""
180 _ip = get_ipython()
208 _ip = get_ipython()
181 self.run_tmpfile_p()
209 self.run_tmpfile_p()
182
210
183
211
184 class TestMagicRunSimple(tt.TempFileMixin):
212 class TestMagicRunSimple(tt.TempFileMixin):
185
213
186 def test_simpledef(self):
214 def test_simpledef(self):
187 """Test that simple class definitions work."""
215 """Test that simple class definitions work."""
188 src = ("class foo: pass\n"
216 src = ("class foo: pass\n"
189 "def f(): return foo()")
217 "def f(): return foo()")
190 self.mktmp(src)
218 self.mktmp(src)
191 _ip.magic('run %s' % self.fname)
219 _ip.magic('run %s' % self.fname)
192 _ip.run_cell('t = isinstance(f(), foo)')
220 _ip.run_cell('t = isinstance(f(), foo)')
193 nt.assert_true(_ip.user_ns['t'])
221 nt.assert_true(_ip.user_ns['t'])
194
222
195 def test_obj_del(self):
223 def test_obj_del(self):
196 """Test that object's __del__ methods are called on exit."""
224 """Test that object's __del__ methods are called on exit."""
197 if sys.platform == 'win32':
225 if sys.platform == 'win32':
198 try:
226 try:
199 import win32api
227 import win32api
200 except ImportError:
228 except ImportError:
201 raise SkipTest("Test requires pywin32")
229 raise SkipTest("Test requires pywin32")
202 src = ("class A(object):\n"
230 src = ("class A(object):\n"
203 " def __del__(self):\n"
231 " def __del__(self):\n"
204 " print 'object A deleted'\n"
232 " print 'object A deleted'\n"
205 "a = A()\n")
233 "a = A()\n")
206 self.mktmp(py3compat.doctest_refactor_print(src))
234 self.mktmp(py3compat.doctest_refactor_print(src))
207 if dec.module_not_available('sqlite3'):
235 if dec.module_not_available('sqlite3'):
208 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
236 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
209 else:
237 else:
210 err = None
238 err = None
211 tt.ipexec_validate(self.fname, 'object A deleted', err)
239 tt.ipexec_validate(self.fname, 'object A deleted', err)
212
240
213 @dec.skip_known_failure
241 @dec.skip_known_failure
214 def test_aggressive_namespace_cleanup(self):
242 def test_aggressive_namespace_cleanup(self):
215 """Test that namespace cleanup is not too aggressive GH-238
243 """Test that namespace cleanup is not too aggressive GH-238
216
244
217 Returning from another run magic deletes the namespace"""
245 Returning from another run magic deletes the namespace"""
218 # see ticket https://github.com/ipython/ipython/issues/238
246 # see ticket https://github.com/ipython/ipython/issues/238
219 class secondtmp(tt.TempFileMixin): pass
247 class secondtmp(tt.TempFileMixin): pass
220 empty = secondtmp()
248 empty = secondtmp()
221 empty.mktmp('')
249 empty.mktmp('')
222 src = ("ip = get_ipython()\n"
250 src = ("ip = get_ipython()\n"
223 "for i in range(5):\n"
251 "for i in range(5):\n"
224 " try:\n"
252 " try:\n"
225 " ip.magic('run %s')\n"
253 " ip.magic('run %s')\n"
226 " except NameError as e:\n"
254 " except NameError as e:\n"
227 " print i;break\n" % empty.fname)
255 " print i;break\n" % empty.fname)
228 self.mktmp(py3compat.doctest_refactor_print(src))
256 self.mktmp(py3compat.doctest_refactor_print(src))
229 _ip.magic('run %s' % self.fname)
257 _ip.magic('run %s' % self.fname)
230 _ip.run_cell('ip == get_ipython()')
258 _ip.run_cell('ip == get_ipython()')
231 nt.assert_equal(_ip.user_ns['i'], 5)
259 nt.assert_equal(_ip.user_ns['i'], 5)
232
260
233 @dec.skip_win32
261 @dec.skip_win32
234 def test_tclass(self):
262 def test_tclass(self):
235 mydir = os.path.dirname(__file__)
263 mydir = os.path.dirname(__file__)
236 tc = os.path.join(mydir, 'tclass')
264 tc = os.path.join(mydir, 'tclass')
237 src = ("%%run '%s' C-first\n"
265 src = ("%%run '%s' C-first\n"
238 "%%run '%s' C-second\n"
266 "%%run '%s' C-second\n"
239 "%%run '%s' C-third\n") % (tc, tc, tc)
267 "%%run '%s' C-third\n") % (tc, tc, tc)
240 self.mktmp(src, '.ipy')
268 self.mktmp(src, '.ipy')
241 out = """\
269 out = """\
242 ARGV 1-: ['C-first']
270 ARGV 1-: ['C-first']
243 ARGV 1-: ['C-second']
271 ARGV 1-: ['C-second']
244 tclass.py: deleting object: C-first
272 tclass.py: deleting object: C-first
245 ARGV 1-: ['C-third']
273 ARGV 1-: ['C-third']
246 tclass.py: deleting object: C-second
274 tclass.py: deleting object: C-second
247 tclass.py: deleting object: C-third
275 tclass.py: deleting object: C-third
248 """
276 """
249 if dec.module_not_available('sqlite3'):
277 if dec.module_not_available('sqlite3'):
250 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
278 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
251 else:
279 else:
252 err = None
280 err = None
253 tt.ipexec_validate(self.fname, out, err)
281 tt.ipexec_validate(self.fname, out, err)
254
282
255 def test_run_i_after_reset(self):
283 def test_run_i_after_reset(self):
256 """Check that %run -i still works after %reset (gh-693)"""
284 """Check that %run -i still works after %reset (gh-693)"""
257 src = "yy = zz\n"
285 src = "yy = zz\n"
258 self.mktmp(src)
286 self.mktmp(src)
259 _ip.run_cell("zz = 23")
287 _ip.run_cell("zz = 23")
260 _ip.magic('run -i %s' % self.fname)
288 _ip.magic('run -i %s' % self.fname)
261 nt.assert_equal(_ip.user_ns['yy'], 23)
289 nt.assert_equal(_ip.user_ns['yy'], 23)
262 _ip.magic('reset -f')
290 _ip.magic('reset -f')
263 _ip.run_cell("zz = 23")
291 _ip.run_cell("zz = 23")
264 _ip.magic('run -i %s' % self.fname)
292 _ip.magic('run -i %s' % self.fname)
265 nt.assert_equal(_ip.user_ns['yy'], 23)
293 nt.assert_equal(_ip.user_ns['yy'], 23)
266
294
267 def test_unicode(self):
295 def test_unicode(self):
268 """Check that files in odd encodings are accepted."""
296 """Check that files in odd encodings are accepted."""
269 mydir = os.path.dirname(__file__)
297 mydir = os.path.dirname(__file__)
270 na = os.path.join(mydir, 'nonascii.py')
298 na = os.path.join(mydir, 'nonascii.py')
271 _ip.magic('run "%s"' % na)
299 _ip.magic('run "%s"' % na)
272 nt.assert_equal(_ip.user_ns['u'], u'Ўт№Ф')
300 nt.assert_equal(_ip.user_ns['u'], u'Ўт№Ф')
@@ -1,527 +1,521 b''
1 """IPython extension to reload modules before executing user code.
1 """IPython extension to reload modules before executing user code.
2
2
3 ``autoreload`` reloads modules automatically before entering the execution of
3 ``autoreload`` reloads modules automatically before entering the execution of
4 code typed at the IPython prompt.
4 code typed at the IPython prompt.
5
5
6 This makes for example the following workflow possible:
6 This makes for example the following workflow possible:
7
7
8 .. sourcecode:: ipython
8 .. sourcecode:: ipython
9
9
10 In [1]: %load_ext autoreload
10 In [1]: %load_ext autoreload
11
11
12 In [2]: %autoreload 2
12 In [2]: %autoreload 2
13
13
14 In [3]: from foo import some_function
14 In [3]: from foo import some_function
15
15
16 In [4]: some_function()
16 In [4]: some_function()
17 Out[4]: 42
17 Out[4]: 42
18
18
19 In [5]: # open foo.py in an editor and change some_function to return 43
19 In [5]: # open foo.py in an editor and change some_function to return 43
20
20
21 In [6]: some_function()
21 In [6]: some_function()
22 Out[6]: 43
22 Out[6]: 43
23
23
24 The module was reloaded without reloading it explicitly, and the object
24 The module was reloaded without reloading it explicitly, and the object
25 imported with ``from foo import ...`` was also updated.
25 imported with ``from foo import ...`` was also updated.
26
26
27 Usage
27 Usage
28 =====
28 =====
29
29
30 The following magic commands are provided:
30 The following magic commands are provided:
31
31
32 ``%autoreload``
32 ``%autoreload``
33
33
34 Reload all modules (except those excluded by ``%aimport``)
34 Reload all modules (except those excluded by ``%aimport``)
35 automatically now.
35 automatically now.
36
36
37 ``%autoreload 0``
37 ``%autoreload 0``
38
38
39 Disable automatic reloading.
39 Disable automatic reloading.
40
40
41 ``%autoreload 1``
41 ``%autoreload 1``
42
42
43 Reload all modules imported with ``%aimport`` every time before
43 Reload all modules imported with ``%aimport`` every time before
44 executing the Python code typed.
44 executing the Python code typed.
45
45
46 ``%autoreload 2``
46 ``%autoreload 2``
47
47
48 Reload all modules (except those excluded by ``%aimport``) every
48 Reload all modules (except those excluded by ``%aimport``) every
49 time before executing the Python code typed.
49 time before executing the Python code typed.
50
50
51 ``%aimport``
51 ``%aimport``
52
52
53 List modules which are to be automatically imported or not to be imported.
53 List modules which are to be automatically imported or not to be imported.
54
54
55 ``%aimport foo``
55 ``%aimport foo``
56
56
57 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
57 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
58
58
59 ``%aimport -foo``
59 ``%aimport -foo``
60
60
61 Mark module 'foo' to not be autoreloaded.
61 Mark module 'foo' to not be autoreloaded.
62
62
63 Caveats
63 Caveats
64 =======
64 =======
65
65
66 Reloading Python modules in a reliable way is in general difficult,
66 Reloading Python modules in a reliable way is in general difficult,
67 and unexpected things may occur. ``%autoreload`` tries to work around
67 and unexpected things may occur. ``%autoreload`` tries to work around
68 common pitfalls by replacing function code objects and parts of
68 common pitfalls by replacing function code objects and parts of
69 classes previously in the module with new versions. This makes the
69 classes previously in the module with new versions. This makes the
70 following things to work:
70 following things to work:
71
71
72 - Functions and classes imported via 'from xxx import foo' are upgraded
72 - Functions and classes imported via 'from xxx import foo' are upgraded
73 to new versions when 'xxx' is reloaded.
73 to new versions when 'xxx' is reloaded.
74
74
75 - Methods and properties of classes are upgraded on reload, so that
75 - Methods and properties of classes are upgraded on reload, so that
76 calling 'c.foo()' on an object 'c' created before the reload causes
76 calling 'c.foo()' on an object 'c' created before the reload causes
77 the new code for 'foo' to be executed.
77 the new code for 'foo' to be executed.
78
78
79 Some of the known remaining caveats are:
79 Some of the known remaining caveats are:
80
80
81 - Replacing code objects does not always succeed: changing a @property
81 - Replacing code objects does not always succeed: changing a @property
82 in a class to an ordinary method or a method to a member variable
82 in a class to an ordinary method or a method to a member variable
83 can cause problems (but in old objects only).
83 can cause problems (but in old objects only).
84
84
85 - Functions that are removed (eg. via monkey-patching) from a module
85 - Functions that are removed (eg. via monkey-patching) from a module
86 before it is reloaded are not upgraded.
86 before it is reloaded are not upgraded.
87
87
88 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
88 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
89 """
89 """
90 from __future__ import print_function
90 from __future__ import print_function
91
91
92 skip_doctest = True
92 skip_doctest = True
93
93
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95 # Copyright (C) 2000 Thomas Heller
95 # Copyright (C) 2000 Thomas Heller
96 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
96 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
97 # Copyright (C) 2012 The IPython Development Team
97 # Copyright (C) 2012 The IPython Development Team
98 #
98 #
99 # Distributed under the terms of the BSD License. The full license is in
99 # Distributed under the terms of the BSD License. The full license is in
100 # the file COPYING, distributed as part of this software.
100 # the file COPYING, distributed as part of this software.
101 #-----------------------------------------------------------------------------
101 #-----------------------------------------------------------------------------
102 #
102 #
103 # This IPython module is written by Pauli Virtanen, based on the autoreload
103 # This IPython module is written by Pauli Virtanen, based on the autoreload
104 # code by Thomas Heller.
104 # code by Thomas Heller.
105
105
106 #-----------------------------------------------------------------------------
106 #-----------------------------------------------------------------------------
107 # Imports
107 # Imports
108 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
109
109
110 import imp
110 import imp
111 import os
111 import os
112 import sys
112 import sys
113 import traceback
113 import traceback
114 import types
114 import types
115 import weakref
115 import weakref
116
116
117 try:
117 try:
118 # Reload is not defined by default in Python3.
118 # Reload is not defined by default in Python3.
119 reload
119 reload
120 except NameError:
120 except NameError:
121 from imp import reload
121 from imp import reload
122
122
123 from IPython.utils import pyfile
123 from IPython.utils import pyfile
124 from IPython.utils.py3compat import PY3
124 from IPython.utils.py3compat import PY3
125
125
126 #------------------------------------------------------------------------------
126 #------------------------------------------------------------------------------
127 # Autoreload functionality
127 # Autoreload functionality
128 #------------------------------------------------------------------------------
128 #------------------------------------------------------------------------------
129
129
130 def _get_compiled_ext():
130 def _get_compiled_ext():
131 """Official way to get the extension of compiled files (.pyc or .pyo)"""
131 """Official way to get the extension of compiled files (.pyc or .pyo)"""
132 for ext, mode, typ in imp.get_suffixes():
132 for ext, mode, typ in imp.get_suffixes():
133 if typ == imp.PY_COMPILED:
133 if typ == imp.PY_COMPILED:
134 return ext
134 return ext
135
135
136
136
137 PY_COMPILED_EXT = _get_compiled_ext()
137 PY_COMPILED_EXT = _get_compiled_ext()
138
138
139
139
140 class ModuleReloader(object):
140 class ModuleReloader(object):
141 enabled = False
141 enabled = False
142 """Whether this reloader is enabled"""
142 """Whether this reloader is enabled"""
143
143
144 failed = {}
144 failed = {}
145 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
145 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
146
146
147 modules = {}
147 modules = {}
148 """Modules specially marked as autoreloadable."""
148 """Modules specially marked as autoreloadable."""
149
149
150 skip_modules = {}
150 skip_modules = {}
151 """Modules specially marked as not autoreloadable."""
151 """Modules specially marked as not autoreloadable."""
152
152
153 check_all = True
153 check_all = True
154 """Autoreload all modules, not just those listed in 'modules'"""
154 """Autoreload all modules, not just those listed in 'modules'"""
155
155
156 old_objects = {}
156 old_objects = {}
157 """(module-name, name) -> weakref, for replacing old code objects"""
157 """(module-name, name) -> weakref, for replacing old code objects"""
158
158
159 def mark_module_skipped(self, module_name):
159 def mark_module_skipped(self, module_name):
160 """Skip reloading the named module in the future"""
160 """Skip reloading the named module in the future"""
161 try:
161 try:
162 del self.modules[module_name]
162 del self.modules[module_name]
163 except KeyError:
163 except KeyError:
164 pass
164 pass
165 self.skip_modules[module_name] = True
165 self.skip_modules[module_name] = True
166
166
167 def mark_module_reloadable(self, module_name):
167 def mark_module_reloadable(self, module_name):
168 """Reload the named module in the future (if it is imported)"""
168 """Reload the named module in the future (if it is imported)"""
169 try:
169 try:
170 del self.skip_modules[module_name]
170 del self.skip_modules[module_name]
171 except KeyError:
171 except KeyError:
172 pass
172 pass
173 self.modules[module_name] = True
173 self.modules[module_name] = True
174
174
175 def aimport_module(self, module_name):
175 def aimport_module(self, module_name):
176 """Import a module, and mark it reloadable
176 """Import a module, and mark it reloadable
177
177
178 Returns
178 Returns
179 -------
179 -------
180 top_module : module
180 top_module : module
181 The imported module if it is top-level, or the top-level
181 The imported module if it is top-level, or the top-level
182 top_name : module
182 top_name : module
183 Name of top_module
183 Name of top_module
184
184
185 """
185 """
186 self.mark_module_reloadable(module_name)
186 self.mark_module_reloadable(module_name)
187
187
188 __import__(module_name)
188 __import__(module_name)
189 top_name = module_name.split('.')[0]
189 top_name = module_name.split('.')[0]
190 top_module = sys.modules[top_name]
190 top_module = sys.modules[top_name]
191 return top_module, top_name
191 return top_module, top_name
192
192
193 def check(self, check_all=False):
193 def check(self, check_all=False):
194 """Check whether some modules need to be reloaded."""
194 """Check whether some modules need to be reloaded."""
195
195
196 if not self.enabled and not check_all:
196 if not self.enabled and not check_all:
197 return
197 return
198
198
199 if check_all or self.check_all:
199 if check_all or self.check_all:
200 modules = sys.modules.keys()
200 modules = sys.modules.keys()
201 else:
201 else:
202 modules = self.modules.keys()
202 modules = self.modules.keys()
203
203
204 for modname in modules:
204 for modname in modules:
205 m = sys.modules.get(modname, None)
205 m = sys.modules.get(modname, None)
206
206
207 if modname in self.skip_modules:
207 if modname in self.skip_modules:
208 continue
208 continue
209
209
210 if not hasattr(m, '__file__'):
210 if not hasattr(m, '__file__'):
211 continue
211 continue
212
212
213 if m.__name__ == '__main__':
213 if m.__name__ == '__main__':
214 # we cannot reload(__main__)
214 # we cannot reload(__main__)
215 continue
215 continue
216
216
217 filename = m.__file__
217 filename = m.__file__
218 path, ext = os.path.splitext(filename)
218 path, ext = os.path.splitext(filename)
219
219
220 if ext.lower() == '.py':
220 if ext.lower() == '.py':
221 ext = PY_COMPILED_EXT
221 ext = PY_COMPILED_EXT
222 pyc_filename = pyfile.cache_from_source(filename)
222 pyc_filename = pyfile.cache_from_source(filename)
223 py_filename = filename
223 py_filename = filename
224 else:
224 else:
225 pyc_filename = filename
225 pyc_filename = filename
226 try:
226 try:
227 py_filename = pyfile.source_from_cache(filename)
227 py_filename = pyfile.source_from_cache(filename)
228 except ValueError:
228 except ValueError:
229 continue
229 continue
230
230
231 try:
231 try:
232 pymtime = os.stat(py_filename).st_mtime
232 pymtime = os.stat(py_filename).st_mtime
233 if pymtime <= os.stat(pyc_filename).st_mtime:
233 if pymtime <= os.stat(pyc_filename).st_mtime:
234 continue
234 continue
235 if self.failed.get(py_filename, None) == pymtime:
235 if self.failed.get(py_filename, None) == pymtime:
236 continue
236 continue
237 except OSError:
237 except OSError:
238 continue
238 continue
239
239
240 try:
240 try:
241 superreload(m, reload, self.old_objects)
241 superreload(m, reload, self.old_objects)
242 if py_filename in self.failed:
242 if py_filename in self.failed:
243 del self.failed[py_filename]
243 del self.failed[py_filename]
244 except:
244 except:
245 print("[autoreload of %s failed: %s]" % (
245 print("[autoreload of %s failed: %s]" % (
246 modname, traceback.format_exc(1)), file=sys.stderr)
246 modname, traceback.format_exc(1)), file=sys.stderr)
247 self.failed[py_filename] = pymtime
247 self.failed[py_filename] = pymtime
248
248
249 #------------------------------------------------------------------------------
249 #------------------------------------------------------------------------------
250 # superreload
250 # superreload
251 #------------------------------------------------------------------------------
251 #------------------------------------------------------------------------------
252
252
253 if PY3:
253 if PY3:
254 func_attrs = ['__code__', '__defaults__', '__doc__',
254 func_attrs = ['__code__', '__defaults__', '__doc__',
255 '__closure__', '__globals__', '__dict__']
255 '__closure__', '__globals__', '__dict__']
256 else:
256 else:
257 func_attrs = ['func_code', 'func_defaults', 'func_doc',
257 func_attrs = ['func_code', 'func_defaults', 'func_doc',
258 'func_closure', 'func_globals', 'func_dict']
258 'func_closure', 'func_globals', 'func_dict']
259
259
260
260
261 def update_function(old, new):
261 def update_function(old, new):
262 """Upgrade the code object of a function"""
262 """Upgrade the code object of a function"""
263 for name in func_attrs:
263 for name in func_attrs:
264 try:
264 try:
265 setattr(old, name, getattr(new, name))
265 setattr(old, name, getattr(new, name))
266 except (AttributeError, TypeError):
266 except (AttributeError, TypeError):
267 pass
267 pass
268
268
269
269
270 def update_class(old, new):
270 def update_class(old, new):
271 """Replace stuff in the __dict__ of a class, and upgrade
271 """Replace stuff in the __dict__ of a class, and upgrade
272 method code objects"""
272 method code objects"""
273 for key in old.__dict__.keys():
273 for key in old.__dict__.keys():
274 old_obj = getattr(old, key)
274 old_obj = getattr(old, key)
275
275
276 try:
276 try:
277 new_obj = getattr(new, key)
277 new_obj = getattr(new, key)
278 except AttributeError:
278 except AttributeError:
279 # obsolete attribute: remove it
279 # obsolete attribute: remove it
280 try:
280 try:
281 delattr(old, key)
281 delattr(old, key)
282 except (AttributeError, TypeError):
282 except (AttributeError, TypeError):
283 pass
283 pass
284 continue
284 continue
285
285
286 if update_generic(old_obj, new_obj): continue
286 if update_generic(old_obj, new_obj): continue
287
287
288 try:
288 try:
289 setattr(old, key, getattr(new, key))
289 setattr(old, key, getattr(new, key))
290 except (AttributeError, TypeError):
290 except (AttributeError, TypeError):
291 pass # skip non-writable attributes
291 pass # skip non-writable attributes
292
292
293
293
294 def update_property(old, new):
294 def update_property(old, new):
295 """Replace get/set/del functions of a property"""
295 """Replace get/set/del functions of a property"""
296 update_generic(old.fdel, new.fdel)
296 update_generic(old.fdel, new.fdel)
297 update_generic(old.fget, new.fget)
297 update_generic(old.fget, new.fget)
298 update_generic(old.fset, new.fset)
298 update_generic(old.fset, new.fset)
299
299
300
300
301 def isinstance2(a, b, typ):
301 def isinstance2(a, b, typ):
302 return isinstance(a, typ) and isinstance(b, typ)
302 return isinstance(a, typ) and isinstance(b, typ)
303
303
304
304
305 UPDATE_RULES = [
305 UPDATE_RULES = [
306 (lambda a, b: isinstance2(a, b, type),
306 (lambda a, b: isinstance2(a, b, type),
307 update_class),
307 update_class),
308 (lambda a, b: isinstance2(a, b, types.FunctionType),
308 (lambda a, b: isinstance2(a, b, types.FunctionType),
309 update_function),
309 update_function),
310 (lambda a, b: isinstance2(a, b, property),
310 (lambda a, b: isinstance2(a, b, property),
311 update_property),
311 update_property),
312 ]
312 ]
313
313
314
314
315 if PY3:
315 if PY3:
316 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
316 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
317 lambda a, b: update_function(a.__func__, b.__func__)),
317 lambda a, b: update_function(a.__func__, b.__func__)),
318 ])
318 ])
319 else:
319 else:
320 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.ClassType),
320 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.ClassType),
321 update_class),
321 update_class),
322 (lambda a, b: isinstance2(a, b, types.MethodType),
322 (lambda a, b: isinstance2(a, b, types.MethodType),
323 lambda a, b: update_function(a.im_func, b.im_func)),
323 lambda a, b: update_function(a.im_func, b.im_func)),
324 ])
324 ])
325
325
326
326
327 def update_generic(a, b):
327 def update_generic(a, b):
328 for type_check, update in UPDATE_RULES:
328 for type_check, update in UPDATE_RULES:
329 if type_check(a, b):
329 if type_check(a, b):
330 update(a, b)
330 update(a, b)
331 return True
331 return True
332 return False
332 return False
333
333
334
334
335 class StrongRef(object):
335 class StrongRef(object):
336 def __init__(self, obj):
336 def __init__(self, obj):
337 self.obj = obj
337 self.obj = obj
338 def __call__(self):
338 def __call__(self):
339 return self.obj
339 return self.obj
340
340
341
341
342 def superreload(module, reload=reload, old_objects={}):
342 def superreload(module, reload=reload, old_objects={}):
343 """Enhanced version of the builtin reload function.
343 """Enhanced version of the builtin reload function.
344
344
345 superreload remembers objects previously in the module, and
345 superreload remembers objects previously in the module, and
346
346
347 - upgrades the class dictionary of every old class in the module
347 - upgrades the class dictionary of every old class in the module
348 - upgrades the code object of every old function and method
348 - upgrades the code object of every old function and method
349 - clears the module's namespace before reloading
349 - clears the module's namespace before reloading
350
350
351 """
351 """
352
352
353 # collect old objects in the module
353 # collect old objects in the module
354 for name, obj in module.__dict__.items():
354 for name, obj in module.__dict__.items():
355 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
355 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
356 continue
356 continue
357 key = (module.__name__, name)
357 key = (module.__name__, name)
358 try:
358 try:
359 old_objects.setdefault(key, []).append(weakref.ref(obj))
359 old_objects.setdefault(key, []).append(weakref.ref(obj))
360 except TypeError:
360 except TypeError:
361 # weakref doesn't work for all types;
361 # weakref doesn't work for all types;
362 # create strong references for 'important' cases
362 # create strong references for 'important' cases
363 if not PY3 and isinstance(obj, types.ClassType):
363 if not PY3 and isinstance(obj, types.ClassType):
364 old_objects.setdefault(key, []).append(StrongRef(obj))
364 old_objects.setdefault(key, []).append(StrongRef(obj))
365
365
366 # reload module
366 # reload module
367 try:
367 try:
368 # clear namespace first from old cruft
368 # clear namespace first from old cruft
369 old_dict = module.__dict__.copy()
369 old_dict = module.__dict__.copy()
370 old_name = module.__name__
370 old_name = module.__name__
371 module.__dict__.clear()
371 module.__dict__.clear()
372 module.__dict__['__name__'] = old_name
372 module.__dict__['__name__'] = old_name
373 module.__dict__['__loader__'] = old_dict['__loader__']
373 module.__dict__['__loader__'] = old_dict['__loader__']
374 except (TypeError, AttributeError, KeyError):
374 except (TypeError, AttributeError, KeyError):
375 pass
375 pass
376
376
377 try:
377 try:
378 module = reload(module)
378 module = reload(module)
379 except:
379 except:
380 # restore module dictionary on failed reload
380 # restore module dictionary on failed reload
381 module.__dict__.update(old_dict)
381 module.__dict__.update(old_dict)
382 raise
382 raise
383
383
384 # iterate over all objects and update functions & classes
384 # iterate over all objects and update functions & classes
385 for name, new_obj in module.__dict__.items():
385 for name, new_obj in module.__dict__.items():
386 key = (module.__name__, name)
386 key = (module.__name__, name)
387 if key not in old_objects: continue
387 if key not in old_objects: continue
388
388
389 new_refs = []
389 new_refs = []
390 for old_ref in old_objects[key]:
390 for old_ref in old_objects[key]:
391 old_obj = old_ref()
391 old_obj = old_ref()
392 if old_obj is None: continue
392 if old_obj is None: continue
393 new_refs.append(old_ref)
393 new_refs.append(old_ref)
394 update_generic(old_obj, new_obj)
394 update_generic(old_obj, new_obj)
395
395
396 if new_refs:
396 if new_refs:
397 old_objects[key] = new_refs
397 old_objects[key] = new_refs
398 else:
398 else:
399 del old_objects[key]
399 del old_objects[key]
400
400
401 return module
401 return module
402
402
403 #------------------------------------------------------------------------------
403 #------------------------------------------------------------------------------
404 # IPython connectivity
404 # IPython connectivity
405 #------------------------------------------------------------------------------
405 #------------------------------------------------------------------------------
406
406
407 from IPython.core.hooks import TryNext
407 from IPython.core.hooks import TryNext
408 from IPython.core.magic import Magics, magics_class, line_magic
408 from IPython.core.magic import Magics, magics_class, line_magic
409
409
410 @magics_class
410 @magics_class
411 class AutoreloadMagics(Magics):
411 class AutoreloadMagics(Magics):
412 def __init__(self, *a, **kw):
412 def __init__(self, *a, **kw):
413 super(AutoreloadMagics, self).__init__(*a, **kw)
413 super(AutoreloadMagics, self).__init__(*a, **kw)
414 self._reloader = ModuleReloader()
414 self._reloader = ModuleReloader()
415 self._reloader.check_all = False
415 self._reloader.check_all = False
416
416
417 @line_magic
417 @line_magic
418 def autoreload(self, parameter_s=''):
418 def autoreload(self, parameter_s=''):
419 r"""%autoreload => Reload modules automatically
419 r"""%autoreload => Reload modules automatically
420
420
421 %autoreload
421 %autoreload
422 Reload all modules (except those excluded by %aimport) automatically
422 Reload all modules (except those excluded by %aimport) automatically
423 now.
423 now.
424
424
425 %autoreload 0
425 %autoreload 0
426 Disable automatic reloading.
426 Disable automatic reloading.
427
427
428 %autoreload 1
428 %autoreload 1
429 Reload all modules imported with %aimport every time before executing
429 Reload all modules imported with %aimport every time before executing
430 the Python code typed.
430 the Python code typed.
431
431
432 %autoreload 2
432 %autoreload 2
433 Reload all modules (except those excluded by %aimport) every time
433 Reload all modules (except those excluded by %aimport) every time
434 before executing the Python code typed.
434 before executing the Python code typed.
435
435
436 Reloading Python modules in a reliable way is in general
436 Reloading Python modules in a reliable way is in general
437 difficult, and unexpected things may occur. %autoreload tries to
437 difficult, and unexpected things may occur. %autoreload tries to
438 work around common pitfalls by replacing function code objects and
438 work around common pitfalls by replacing function code objects and
439 parts of classes previously in the module with new versions. This
439 parts of classes previously in the module with new versions. This
440 makes the following things to work:
440 makes the following things to work:
441
441
442 - Functions and classes imported via 'from xxx import foo' are upgraded
442 - Functions and classes imported via 'from xxx import foo' are upgraded
443 to new versions when 'xxx' is reloaded.
443 to new versions when 'xxx' is reloaded.
444
444
445 - Methods and properties of classes are upgraded on reload, so that
445 - Methods and properties of classes are upgraded on reload, so that
446 calling 'c.foo()' on an object 'c' created before the reload causes
446 calling 'c.foo()' on an object 'c' created before the reload causes
447 the new code for 'foo' to be executed.
447 the new code for 'foo' to be executed.
448
448
449 Some of the known remaining caveats are:
449 Some of the known remaining caveats are:
450
450
451 - Replacing code objects does not always succeed: changing a @property
451 - Replacing code objects does not always succeed: changing a @property
452 in a class to an ordinary method or a method to a member variable
452 in a class to an ordinary method or a method to a member variable
453 can cause problems (but in old objects only).
453 can cause problems (but in old objects only).
454
454
455 - Functions that are removed (eg. via monkey-patching) from a module
455 - Functions that are removed (eg. via monkey-patching) from a module
456 before it is reloaded are not upgraded.
456 before it is reloaded are not upgraded.
457
457
458 - C extension modules cannot be reloaded, and so cannot be
458 - C extension modules cannot be reloaded, and so cannot be
459 autoreloaded.
459 autoreloaded.
460
460
461 """
461 """
462 if parameter_s == '':
462 if parameter_s == '':
463 self._reloader.check(True)
463 self._reloader.check(True)
464 elif parameter_s == '0':
464 elif parameter_s == '0':
465 self._reloader.enabled = False
465 self._reloader.enabled = False
466 elif parameter_s == '1':
466 elif parameter_s == '1':
467 self._reloader.check_all = False
467 self._reloader.check_all = False
468 self._reloader.enabled = True
468 self._reloader.enabled = True
469 elif parameter_s == '2':
469 elif parameter_s == '2':
470 self._reloader.check_all = True
470 self._reloader.check_all = True
471 self._reloader.enabled = True
471 self._reloader.enabled = True
472
472
473 @line_magic
473 @line_magic
474 def aimport(self, parameter_s='', stream=None):
474 def aimport(self, parameter_s='', stream=None):
475 """%aimport => Import modules for automatic reloading.
475 """%aimport => Import modules for automatic reloading.
476
476
477 %aimport
477 %aimport
478 List modules to automatically import and not to import.
478 List modules to automatically import and not to import.
479
479
480 %aimport foo
480 %aimport foo
481 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
481 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
482
482
483 %aimport -foo
483 %aimport -foo
484 Mark module 'foo' to not be autoreloaded for %autoreload 1
484 Mark module 'foo' to not be autoreloaded for %autoreload 1
485 """
485 """
486 modname = parameter_s
486 modname = parameter_s
487 if not modname:
487 if not modname:
488 to_reload = self._reloader.modules.keys()
488 to_reload = self._reloader.modules.keys()
489 to_reload.sort()
489 to_reload.sort()
490 to_skip = self._reloader.skip_modules.keys()
490 to_skip = self._reloader.skip_modules.keys()
491 to_skip.sort()
491 to_skip.sort()
492 if stream is None:
492 if stream is None:
493 stream = sys.stdout
493 stream = sys.stdout
494 if self._reloader.check_all:
494 if self._reloader.check_all:
495 stream.write("Modules to reload:\nall-except-skipped\n")
495 stream.write("Modules to reload:\nall-except-skipped\n")
496 else:
496 else:
497 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
497 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
498 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
498 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
499 elif modname.startswith('-'):
499 elif modname.startswith('-'):
500 modname = modname[1:]
500 modname = modname[1:]
501 self._reloader.mark_module_skipped(modname)
501 self._reloader.mark_module_skipped(modname)
502 else:
502 else:
503 top_module, top_name = self._reloader.aimport_module(modname)
503 top_module, top_name = self._reloader.aimport_module(modname)
504
504
505 # Inject module to user namespace
505 # Inject module to user namespace
506 self.shell.push({top_name: top_module})
506 self.shell.push({top_name: top_module})
507
507
508 def pre_run_code_hook(self, ip):
508 def pre_run_code_hook(self, ip):
509 if not self._reloader.enabled:
509 if not self._reloader.enabled:
510 raise TryNext
510 raise TryNext
511 try:
511 try:
512 self._reloader.check()
512 self._reloader.check()
513 except:
513 except:
514 pass
514 pass
515
515
516
516
517 _loaded = False
518
519
520 def load_ipython_extension(ip):
517 def load_ipython_extension(ip):
521 """Load the extension in IPython."""
518 """Load the extension in IPython."""
522 global _loaded
519 auto_reload = AutoreloadMagics(ip)
523 if not _loaded:
520 ip.register_magics(auto_reload)
524 auto_reload = AutoreloadMagics(ip)
521 ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook)
525 ip.register_magics(auto_reload)
526 ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook)
527 _loaded = True
@@ -1,283 +1,279 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Cython related magics.
3 Cython related magics.
4
4
5 Author:
5 Author:
6 * Brian Granger
6 * Brian Granger
7
7
8 Parts of this code were taken from Cython.inline.
8 Parts of this code were taken from Cython.inline.
9 """
9 """
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2010-2011, IPython Development Team.
11 # Copyright (C) 2010-2011, IPython Development Team.
12 #
12 #
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14 #
14 #
15 # The full license is in the file COPYING.txt, distributed with this software.
15 # The full license is in the file COPYING.txt, distributed with this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import print_function
18 from __future__ import print_function
19
19
20 import imp
20 import imp
21 import io
21 import io
22 import os
22 import os
23 import re
23 import re
24 import sys
24 import sys
25 import time
25 import time
26
26
27 try:
27 try:
28 import hashlib
28 import hashlib
29 except ImportError:
29 except ImportError:
30 import md5 as hashlib
30 import md5 as hashlib
31
31
32 from distutils.core import Distribution, Extension
32 from distutils.core import Distribution, Extension
33 from distutils.command.build_ext import build_ext
33 from distutils.command.build_ext import build_ext
34
34
35 from IPython.core import display
35 from IPython.core import display
36 from IPython.core import magic_arguments
36 from IPython.core import magic_arguments
37 from IPython.core.magic import Magics, magics_class, cell_magic
37 from IPython.core.magic import Magics, magics_class, cell_magic
38 from IPython.testing.skipdoctest import skip_doctest
38 from IPython.testing.skipdoctest import skip_doctest
39 from IPython.utils import py3compat
39 from IPython.utils import py3compat
40
40
41 import Cython
41 import Cython
42 from Cython.Compiler.Errors import CompileError
42 from Cython.Compiler.Errors import CompileError
43 from Cython.Build.Dependencies import cythonize
43 from Cython.Build.Dependencies import cythonize
44
44
45
45
46 @magics_class
46 @magics_class
47 class CythonMagics(Magics):
47 class CythonMagics(Magics):
48
48
49 def __init__(self, shell):
49 def __init__(self, shell):
50 super(CythonMagics,self).__init__(shell)
50 super(CythonMagics,self).__init__(shell)
51 self._reloads = {}
51 self._reloads = {}
52 self._code_cache = {}
52 self._code_cache = {}
53
53
54 def _import_all(self, module):
54 def _import_all(self, module):
55 for k,v in module.__dict__.items():
55 for k,v in module.__dict__.items():
56 if not k.startswith('__'):
56 if not k.startswith('__'):
57 self.shell.push({k:v})
57 self.shell.push({k:v})
58
58
59 @cell_magic
59 @cell_magic
60 def cython_inline(self, line, cell):
60 def cython_inline(self, line, cell):
61 """Compile and run a Cython code cell using Cython.inline.
61 """Compile and run a Cython code cell using Cython.inline.
62
62
63 This magic simply passes the body of the cell to Cython.inline
63 This magic simply passes the body of the cell to Cython.inline
64 and returns the result. If the variables `a` and `b` are defined
64 and returns the result. If the variables `a` and `b` are defined
65 in the user's namespace, here is a simple example that returns
65 in the user's namespace, here is a simple example that returns
66 their sum::
66 their sum::
67
67
68 %%cython_inline
68 %%cython_inline
69 return a+b
69 return a+b
70
70
71 For most purposes, we recommend the usage of the `%%cython` magic.
71 For most purposes, we recommend the usage of the `%%cython` magic.
72 """
72 """
73 locs = self.shell.user_global_ns
73 locs = self.shell.user_global_ns
74 globs = self.shell.user_ns
74 globs = self.shell.user_ns
75 return Cython.inline(cell, locals=locs, globals=globs)
75 return Cython.inline(cell, locals=locs, globals=globs)
76
76
77 @cell_magic
77 @cell_magic
78 def cython_pyximport(self, line, cell):
78 def cython_pyximport(self, line, cell):
79 """Compile and import a Cython code cell using pyximport.
79 """Compile and import a Cython code cell using pyximport.
80
80
81 The contents of the cell are written to a `.pyx` file in the current
81 The contents of the cell are written to a `.pyx` file in the current
82 working directory, which is then imported using `pyximport`. This
82 working directory, which is then imported using `pyximport`. This
83 magic requires a module name to be passed::
83 magic requires a module name to be passed::
84
84
85 %%cython_pyximport modulename
85 %%cython_pyximport modulename
86 def f(x):
86 def f(x):
87 return 2.0*x
87 return 2.0*x
88
88
89 The compiled module is then imported and all of its symbols are
89 The compiled module is then imported and all of its symbols are
90 injected into the user's namespace. For most purposes, we recommend
90 injected into the user's namespace. For most purposes, we recommend
91 the usage of the `%%cython` magic.
91 the usage of the `%%cython` magic.
92 """
92 """
93 module_name = line.strip()
93 module_name = line.strip()
94 if not module_name:
94 if not module_name:
95 raise ValueError('module name must be given')
95 raise ValueError('module name must be given')
96 fname = module_name + '.pyx'
96 fname = module_name + '.pyx'
97 with io.open(fname, 'w', encoding='utf-8') as f:
97 with io.open(fname, 'w', encoding='utf-8') as f:
98 f.write(cell)
98 f.write(cell)
99 if 'pyximport' not in sys.modules:
99 if 'pyximport' not in sys.modules:
100 import pyximport
100 import pyximport
101 pyximport.install(reload_support=True)
101 pyximport.install(reload_support=True)
102 if module_name in self._reloads:
102 if module_name in self._reloads:
103 module = self._reloads[module_name]
103 module = self._reloads[module_name]
104 reload(module)
104 reload(module)
105 else:
105 else:
106 __import__(module_name)
106 __import__(module_name)
107 module = sys.modules[module_name]
107 module = sys.modules[module_name]
108 self._reloads[module_name] = module
108 self._reloads[module_name] = module
109 self._import_all(module)
109 self._import_all(module)
110
110
111 @magic_arguments.magic_arguments()
111 @magic_arguments.magic_arguments()
112 @magic_arguments.argument(
112 @magic_arguments.argument(
113 '-c', '--compile-args', action='append', default=[],
113 '-c', '--compile-args', action='append', default=[],
114 help="Extra flags to pass to compiler via the `extra_compile_args` "
114 help="Extra flags to pass to compiler via the `extra_compile_args` "
115 "Extension flag (can be specified multiple times)."
115 "Extension flag (can be specified multiple times)."
116 )
116 )
117 @magic_arguments.argument(
117 @magic_arguments.argument(
118 '-la', '--link-args', action='append', default=[],
118 '-la', '--link-args', action='append', default=[],
119 help="Extra flags to pass to linker via the `extra_link_args` "
119 help="Extra flags to pass to linker via the `extra_link_args` "
120 "Extension flag (can be specified multiple times)."
120 "Extension flag (can be specified multiple times)."
121 )
121 )
122 @magic_arguments.argument(
122 @magic_arguments.argument(
123 '-l', '--lib', action='append', default=[],
123 '-l', '--lib', action='append', default=[],
124 help="Add a library to link the extension against (can be specified "
124 help="Add a library to link the extension against (can be specified "
125 "multiple times)."
125 "multiple times)."
126 )
126 )
127 @magic_arguments.argument(
127 @magic_arguments.argument(
128 '-L', dest='library_dirs', metavar='dir', action='append', default=[],
128 '-L', dest='library_dirs', metavar='dir', action='append', default=[],
129 help="Add a path to the list of libary directories (can be specified "
129 help="Add a path to the list of libary directories (can be specified "
130 "multiple times)."
130 "multiple times)."
131 )
131 )
132 @magic_arguments.argument(
132 @magic_arguments.argument(
133 '-I', '--include', action='append', default=[],
133 '-I', '--include', action='append', default=[],
134 help="Add a path to the list of include directories (can be specified "
134 help="Add a path to the list of include directories (can be specified "
135 "multiple times)."
135 "multiple times)."
136 )
136 )
137 @magic_arguments.argument(
137 @magic_arguments.argument(
138 '-+', '--cplus', action='store_true', default=False,
138 '-+', '--cplus', action='store_true', default=False,
139 help="Output a C++ rather than C file."
139 help="Output a C++ rather than C file."
140 )
140 )
141 @magic_arguments.argument(
141 @magic_arguments.argument(
142 '-f', '--force', action='store_true', default=False,
142 '-f', '--force', action='store_true', default=False,
143 help="Force the compilation of a new module, even if the source has been "
143 help="Force the compilation of a new module, even if the source has been "
144 "previously compiled."
144 "previously compiled."
145 )
145 )
146 @magic_arguments.argument(
146 @magic_arguments.argument(
147 '-a', '--annotate', action='store_true', default=False,
147 '-a', '--annotate', action='store_true', default=False,
148 help="Produce a colorized HTML version of the source."
148 help="Produce a colorized HTML version of the source."
149 )
149 )
150 @cell_magic
150 @cell_magic
151 def cython(self, line, cell):
151 def cython(self, line, cell):
152 """Compile and import everything from a Cython code cell.
152 """Compile and import everything from a Cython code cell.
153
153
154 The contents of the cell are written to a `.pyx` file in the
154 The contents of the cell are written to a `.pyx` file in the
155 directory `IPYTHONDIR/cython` using a filename with the hash of the
155 directory `IPYTHONDIR/cython` using a filename with the hash of the
156 code. This file is then cythonized and compiled. The resulting module
156 code. This file is then cythonized and compiled. The resulting module
157 is imported and all of its symbols are injected into the user's
157 is imported and all of its symbols are injected into the user's
158 namespace. The usage is similar to that of `%%cython_pyximport` but
158 namespace. The usage is similar to that of `%%cython_pyximport` but
159 you don't have to pass a module name::
159 you don't have to pass a module name::
160
160
161 %%cython
161 %%cython
162 def f(x):
162 def f(x):
163 return 2.0*x
163 return 2.0*x
164 """
164 """
165 args = magic_arguments.parse_argstring(self.cython, line)
165 args = magic_arguments.parse_argstring(self.cython, line)
166 code = cell if cell.endswith('\n') else cell+'\n'
166 code = cell if cell.endswith('\n') else cell+'\n'
167 lib_dir = os.path.join(self.shell.ipython_dir, 'cython')
167 lib_dir = os.path.join(self.shell.ipython_dir, 'cython')
168 quiet = True
168 quiet = True
169 key = code, sys.version_info, sys.executable, Cython.__version__
169 key = code, sys.version_info, sys.executable, Cython.__version__
170
170
171 if not os.path.exists(lib_dir):
171 if not os.path.exists(lib_dir):
172 os.makedirs(lib_dir)
172 os.makedirs(lib_dir)
173
173
174 if args.force:
174 if args.force:
175 # Force a new module name by adding the current time to the
175 # Force a new module name by adding the current time to the
176 # key which is hashed to determine the module name.
176 # key which is hashed to determine the module name.
177 key += time.time(),
177 key += time.time(),
178
178
179 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
179 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
180 module_path = os.path.join(lib_dir, module_name + self.so_ext)
180 module_path = os.path.join(lib_dir, module_name + self.so_ext)
181
181
182 have_module = os.path.isfile(module_path)
182 have_module = os.path.isfile(module_path)
183 need_cythonize = not have_module
183 need_cythonize = not have_module
184
184
185 if args.annotate:
185 if args.annotate:
186 html_file = os.path.join(lib_dir, module_name + '.html')
186 html_file = os.path.join(lib_dir, module_name + '.html')
187 if not os.path.isfile(html_file):
187 if not os.path.isfile(html_file):
188 need_cythonize = True
188 need_cythonize = True
189
189
190 if need_cythonize:
190 if need_cythonize:
191 c_include_dirs = args.include
191 c_include_dirs = args.include
192 if 'numpy' in code:
192 if 'numpy' in code:
193 import numpy
193 import numpy
194 c_include_dirs.append(numpy.get_include())
194 c_include_dirs.append(numpy.get_include())
195 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
195 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
196 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
196 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
197 with io.open(pyx_file, 'w', encoding='utf-8') as f:
197 with io.open(pyx_file, 'w', encoding='utf-8') as f:
198 f.write(code)
198 f.write(code)
199 extension = Extension(
199 extension = Extension(
200 name = module_name,
200 name = module_name,
201 sources = [pyx_file],
201 sources = [pyx_file],
202 include_dirs = c_include_dirs,
202 include_dirs = c_include_dirs,
203 library_dirs = args.library_dirs,
203 library_dirs = args.library_dirs,
204 extra_compile_args = args.compile_args,
204 extra_compile_args = args.compile_args,
205 extra_link_args = args.link_args,
205 extra_link_args = args.link_args,
206 libraries = args.lib,
206 libraries = args.lib,
207 language = 'c++' if args.cplus else 'c',
207 language = 'c++' if args.cplus else 'c',
208 )
208 )
209 build_extension = self._get_build_extension()
209 build_extension = self._get_build_extension()
210 try:
210 try:
211 opts = dict(
211 opts = dict(
212 quiet=quiet,
212 quiet=quiet,
213 annotate = args.annotate,
213 annotate = args.annotate,
214 force = True,
214 force = True,
215 )
215 )
216 build_extension.extensions = cythonize([extension], **opts)
216 build_extension.extensions = cythonize([extension], **opts)
217 except CompileError:
217 except CompileError:
218 return
218 return
219
219
220 if not have_module:
220 if not have_module:
221 build_extension.build_temp = os.path.dirname(pyx_file)
221 build_extension.build_temp = os.path.dirname(pyx_file)
222 build_extension.build_lib = lib_dir
222 build_extension.build_lib = lib_dir
223 build_extension.run()
223 build_extension.run()
224 self._code_cache[key] = module_name
224 self._code_cache[key] = module_name
225
225
226 module = imp.load_dynamic(module_name, module_path)
226 module = imp.load_dynamic(module_name, module_path)
227 self._import_all(module)
227 self._import_all(module)
228
228
229 if args.annotate:
229 if args.annotate:
230 try:
230 try:
231 with io.open(html_file, encoding='utf-8') as f:
231 with io.open(html_file, encoding='utf-8') as f:
232 annotated_html = f.read()
232 annotated_html = f.read()
233 except IOError as e:
233 except IOError as e:
234 # File could not be opened. Most likely the user has a version
234 # File could not be opened. Most likely the user has a version
235 # of Cython before 0.15.1 (when `cythonize` learned the
235 # of Cython before 0.15.1 (when `cythonize` learned the
236 # `force` keyword argument) and has already compiled this
236 # `force` keyword argument) and has already compiled this
237 # exact source without annotation.
237 # exact source without annotation.
238 print('Cython completed successfully but the annotated '
238 print('Cython completed successfully but the annotated '
239 'source could not be read.', file=sys.stderr)
239 'source could not be read.', file=sys.stderr)
240 print(e, file=sys.stderr)
240 print(e, file=sys.stderr)
241 else:
241 else:
242 return display.HTML(self.clean_annotated_html(annotated_html))
242 return display.HTML(self.clean_annotated_html(annotated_html))
243
243
244 @property
244 @property
245 def so_ext(self):
245 def so_ext(self):
246 """The extension suffix for compiled modules."""
246 """The extension suffix for compiled modules."""
247 try:
247 try:
248 return self._so_ext
248 return self._so_ext
249 except AttributeError:
249 except AttributeError:
250 self._so_ext = self._get_build_extension().get_ext_filename('')
250 self._so_ext = self._get_build_extension().get_ext_filename('')
251 return self._so_ext
251 return self._so_ext
252
252
253 def _get_build_extension(self):
253 def _get_build_extension(self):
254 dist = Distribution()
254 dist = Distribution()
255 config_files = dist.find_config_files()
255 config_files = dist.find_config_files()
256 try:
256 try:
257 config_files.remove('setup.cfg')
257 config_files.remove('setup.cfg')
258 except ValueError:
258 except ValueError:
259 pass
259 pass
260 dist.parse_config_files(config_files)
260 dist.parse_config_files(config_files)
261 build_extension = build_ext(dist)
261 build_extension = build_ext(dist)
262 build_extension.finalize_options()
262 build_extension.finalize_options()
263 return build_extension
263 return build_extension
264
264
265 @staticmethod
265 @staticmethod
266 def clean_annotated_html(html):
266 def clean_annotated_html(html):
267 """Clean up the annotated HTML source.
267 """Clean up the annotated HTML source.
268
268
269 Strips the link to the generated C or C++ file, which we do not
269 Strips the link to the generated C or C++ file, which we do not
270 present to the user.
270 present to the user.
271 """
271 """
272 r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
272 r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
273 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
273 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
274 return html
274 return html
275
275
276 _loaded = False
277
276
278 def load_ipython_extension(ip):
277 def load_ipython_extension(ip):
279 """Load the extension in IPython."""
278 """Load the extension in IPython."""
280 global _loaded
279 ip.register_magics(CythonMagics)
281 if not _loaded:
282 ip.register_magics(CythonMagics)
283 _loaded = True
@@ -1,371 +1,367 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 ===========
3 ===========
4 octavemagic
4 octavemagic
5 ===========
5 ===========
6
6
7 Magics for interacting with Octave via oct2py.
7 Magics for interacting with Octave via oct2py.
8
8
9 .. note::
9 .. note::
10
10
11 The ``oct2py`` module needs to be installed separately and
11 The ``oct2py`` module needs to be installed separately and
12 can be obtained using ``easy_install`` or ``pip``.
12 can be obtained using ``easy_install`` or ``pip``.
13
13
14 Usage
14 Usage
15 =====
15 =====
16
16
17 ``%octave``
17 ``%octave``
18
18
19 {OCTAVE_DOC}
19 {OCTAVE_DOC}
20
20
21 ``%octave_push``
21 ``%octave_push``
22
22
23 {OCTAVE_PUSH_DOC}
23 {OCTAVE_PUSH_DOC}
24
24
25 ``%octave_pull``
25 ``%octave_pull``
26
26
27 {OCTAVE_PULL_DOC}
27 {OCTAVE_PULL_DOC}
28
28
29 """
29 """
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # Copyright (C) 2012 The IPython Development Team
32 # Copyright (C) 2012 The IPython Development Team
33 #
33 #
34 # Distributed under the terms of the BSD License. The full license is in
34 # Distributed under the terms of the BSD License. The full license is in
35 # the file COPYING, distributed as part of this software.
35 # the file COPYING, distributed as part of this software.
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 import tempfile
38 import tempfile
39 from glob import glob
39 from glob import glob
40 from shutil import rmtree
40 from shutil import rmtree
41
41
42 import numpy as np
42 import numpy as np
43 import oct2py
43 import oct2py
44 from xml.dom import minidom
44 from xml.dom import minidom
45
45
46 from IPython.core.displaypub import publish_display_data
46 from IPython.core.displaypub import publish_display_data
47 from IPython.core.magic import (Magics, magics_class, line_magic,
47 from IPython.core.magic import (Magics, magics_class, line_magic,
48 line_cell_magic, needs_local_scope)
48 line_cell_magic, needs_local_scope)
49 from IPython.testing.skipdoctest import skip_doctest
49 from IPython.testing.skipdoctest import skip_doctest
50 from IPython.core.magic_arguments import (
50 from IPython.core.magic_arguments import (
51 argument, magic_arguments, parse_argstring
51 argument, magic_arguments, parse_argstring
52 )
52 )
53 from IPython.utils.py3compat import unicode_to_str
53 from IPython.utils.py3compat import unicode_to_str
54
54
55 class OctaveMagicError(oct2py.Oct2PyError):
55 class OctaveMagicError(oct2py.Oct2PyError):
56 pass
56 pass
57
57
58 _mimetypes = {'png' : 'image/png',
58 _mimetypes = {'png' : 'image/png',
59 'svg' : 'image/svg+xml',
59 'svg' : 'image/svg+xml',
60 'jpg' : 'image/jpeg',
60 'jpg' : 'image/jpeg',
61 'jpeg': 'image/jpeg'}
61 'jpeg': 'image/jpeg'}
62
62
63 @magics_class
63 @magics_class
64 class OctaveMagics(Magics):
64 class OctaveMagics(Magics):
65 """A set of magics useful for interactive work with Octave via oct2py.
65 """A set of magics useful for interactive work with Octave via oct2py.
66 """
66 """
67 def __init__(self, shell):
67 def __init__(self, shell):
68 """
68 """
69 Parameters
69 Parameters
70 ----------
70 ----------
71 shell : IPython shell
71 shell : IPython shell
72
72
73 """
73 """
74 super(OctaveMagics, self).__init__(shell)
74 super(OctaveMagics, self).__init__(shell)
75 self._oct = oct2py.Oct2Py()
75 self._oct = oct2py.Oct2Py()
76 self._plot_format = 'png'
76 self._plot_format = 'png'
77
77
78 # Allow publish_display_data to be overridden for
78 # Allow publish_display_data to be overridden for
79 # testing purposes.
79 # testing purposes.
80 self._publish_display_data = publish_display_data
80 self._publish_display_data = publish_display_data
81
81
82
82
83 def _fix_gnuplot_svg_size(self, image, size=None):
83 def _fix_gnuplot_svg_size(self, image, size=None):
84 """
84 """
85 GnuPlot SVGs do not have height/width attributes. Set
85 GnuPlot SVGs do not have height/width attributes. Set
86 these to be the same as the viewBox, so that the browser
86 these to be the same as the viewBox, so that the browser
87 scales the image correctly.
87 scales the image correctly.
88
88
89 Parameters
89 Parameters
90 ----------
90 ----------
91 image : str
91 image : str
92 SVG data.
92 SVG data.
93 size : tuple of int
93 size : tuple of int
94 Image width, height.
94 Image width, height.
95
95
96 """
96 """
97 (svg,) = minidom.parseString(image).getElementsByTagName('svg')
97 (svg,) = minidom.parseString(image).getElementsByTagName('svg')
98 viewbox = svg.getAttribute('viewBox').split(' ')
98 viewbox = svg.getAttribute('viewBox').split(' ')
99
99
100 if size is not None:
100 if size is not None:
101 width, height = size
101 width, height = size
102 else:
102 else:
103 width, height = viewbox[2:]
103 width, height = viewbox[2:]
104
104
105 svg.setAttribute('width', '%dpx' % width)
105 svg.setAttribute('width', '%dpx' % width)
106 svg.setAttribute('height', '%dpx' % height)
106 svg.setAttribute('height', '%dpx' % height)
107 return svg.toxml()
107 return svg.toxml()
108
108
109
109
110 @skip_doctest
110 @skip_doctest
111 @line_magic
111 @line_magic
112 def octave_push(self, line):
112 def octave_push(self, line):
113 '''
113 '''
114 Line-level magic that pushes a variable to Octave.
114 Line-level magic that pushes a variable to Octave.
115
115
116 `line` should be made up of whitespace separated variable names in the
116 `line` should be made up of whitespace separated variable names in the
117 IPython namespace::
117 IPython namespace::
118
118
119 In [7]: import numpy as np
119 In [7]: import numpy as np
120
120
121 In [8]: X = np.arange(5)
121 In [8]: X = np.arange(5)
122
122
123 In [9]: X.mean()
123 In [9]: X.mean()
124 Out[9]: 2.0
124 Out[9]: 2.0
125
125
126 In [10]: %octave_push X
126 In [10]: %octave_push X
127
127
128 In [11]: %octave mean(X)
128 In [11]: %octave mean(X)
129 Out[11]: 2.0
129 Out[11]: 2.0
130
130
131 '''
131 '''
132 inputs = line.split(' ')
132 inputs = line.split(' ')
133 for input in inputs:
133 for input in inputs:
134 input = unicode_to_str(input)
134 input = unicode_to_str(input)
135 self._oct.put(input, self.shell.user_ns[input])
135 self._oct.put(input, self.shell.user_ns[input])
136
136
137
137
138 @skip_doctest
138 @skip_doctest
139 @line_magic
139 @line_magic
140 def octave_pull(self, line):
140 def octave_pull(self, line):
141 '''
141 '''
142 Line-level magic that pulls a variable from Octave.
142 Line-level magic that pulls a variable from Octave.
143
143
144 In [18]: _ = %octave x = [1 2; 3 4]; y = 'hello'
144 In [18]: _ = %octave x = [1 2; 3 4]; y = 'hello'
145
145
146 In [19]: %octave_pull x y
146 In [19]: %octave_pull x y
147
147
148 In [20]: x
148 In [20]: x
149 Out[20]:
149 Out[20]:
150 array([[ 1., 2.],
150 array([[ 1., 2.],
151 [ 3., 4.]])
151 [ 3., 4.]])
152
152
153 In [21]: y
153 In [21]: y
154 Out[21]: 'hello'
154 Out[21]: 'hello'
155
155
156 '''
156 '''
157 outputs = line.split(' ')
157 outputs = line.split(' ')
158 for output in outputs:
158 for output in outputs:
159 output = unicode_to_str(output)
159 output = unicode_to_str(output)
160 self.shell.push({output: self._oct.get(output)})
160 self.shell.push({output: self._oct.get(output)})
161
161
162
162
163 @skip_doctest
163 @skip_doctest
164 @magic_arguments()
164 @magic_arguments()
165 @argument(
165 @argument(
166 '-i', '--input', action='append',
166 '-i', '--input', action='append',
167 help='Names of input variables to be pushed to Octave. Multiple names '
167 help='Names of input variables to be pushed to Octave. Multiple names '
168 'can be passed, separated by commas with no whitespace.'
168 'can be passed, separated by commas with no whitespace.'
169 )
169 )
170 @argument(
170 @argument(
171 '-o', '--output', action='append',
171 '-o', '--output', action='append',
172 help='Names of variables to be pulled from Octave after executing cell '
172 help='Names of variables to be pulled from Octave after executing cell '
173 'body. Multiple names can be passed, separated by commas with no '
173 'body. Multiple names can be passed, separated by commas with no '
174 'whitespace.'
174 'whitespace.'
175 )
175 )
176 @argument(
176 @argument(
177 '-s', '--size', action='store',
177 '-s', '--size', action='store',
178 help='Pixel size of plots, "width,height". Default is "-s 400,250".'
178 help='Pixel size of plots, "width,height". Default is "-s 400,250".'
179 )
179 )
180 @argument(
180 @argument(
181 '-f', '--format', action='store',
181 '-f', '--format', action='store',
182 help='Plot format (png, svg or jpg).'
182 help='Plot format (png, svg or jpg).'
183 )
183 )
184
184
185 @needs_local_scope
185 @needs_local_scope
186 @argument(
186 @argument(
187 'code',
187 'code',
188 nargs='*',
188 nargs='*',
189 )
189 )
190 @line_cell_magic
190 @line_cell_magic
191 def octave(self, line, cell=None, local_ns=None):
191 def octave(self, line, cell=None, local_ns=None):
192 '''
192 '''
193 Execute code in Octave, and pull some of the results back into the
193 Execute code in Octave, and pull some of the results back into the
194 Python namespace.
194 Python namespace.
195
195
196 In [9]: %octave X = [1 2; 3 4]; mean(X)
196 In [9]: %octave X = [1 2; 3 4]; mean(X)
197 Out[9]: array([[ 2., 3.]])
197 Out[9]: array([[ 2., 3.]])
198
198
199 As a cell, this will run a block of Octave code, without returning any
199 As a cell, this will run a block of Octave code, without returning any
200 value::
200 value::
201
201
202 In [10]: %%octave
202 In [10]: %%octave
203 ....: p = [-2, -1, 0, 1, 2]
203 ....: p = [-2, -1, 0, 1, 2]
204 ....: polyout(p, 'x')
204 ....: polyout(p, 'x')
205
205
206 -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2
206 -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2
207
207
208 In the notebook, plots are published as the output of the cell, e.g.
208 In the notebook, plots are published as the output of the cell, e.g.
209
209
210 %octave plot([1 2 3], [4 5 6])
210 %octave plot([1 2 3], [4 5 6])
211
211
212 will create a line plot.
212 will create a line plot.
213
213
214 Objects can be passed back and forth between Octave and IPython via the
214 Objects can be passed back and forth between Octave and IPython via the
215 -i and -o flags in line::
215 -i and -o flags in line::
216
216
217 In [14]: Z = np.array([1, 4, 5, 10])
217 In [14]: Z = np.array([1, 4, 5, 10])
218
218
219 In [15]: %octave -i Z mean(Z)
219 In [15]: %octave -i Z mean(Z)
220 Out[15]: array([ 5.])
220 Out[15]: array([ 5.])
221
221
222
222
223 In [16]: %octave -o W W = Z * mean(Z)
223 In [16]: %octave -o W W = Z * mean(Z)
224 Out[16]: array([ 5., 20., 25., 50.])
224 Out[16]: array([ 5., 20., 25., 50.])
225
225
226 In [17]: W
226 In [17]: W
227 Out[17]: array([ 5., 20., 25., 50.])
227 Out[17]: array([ 5., 20., 25., 50.])
228
228
229 The size and format of output plots can be specified::
229 The size and format of output plots can be specified::
230
230
231 In [18]: %%octave -s 600,800 -f svg
231 In [18]: %%octave -s 600,800 -f svg
232 ...: plot([1, 2, 3]);
232 ...: plot([1, 2, 3]);
233
233
234 '''
234 '''
235 args = parse_argstring(self.octave, line)
235 args = parse_argstring(self.octave, line)
236
236
237 # arguments 'code' in line are prepended to the cell lines
237 # arguments 'code' in line are prepended to the cell lines
238 if cell is None:
238 if cell is None:
239 code = ''
239 code = ''
240 return_output = True
240 return_output = True
241 else:
241 else:
242 code = cell
242 code = cell
243 return_output = False
243 return_output = False
244
244
245 code = ' '.join(args.code) + code
245 code = ' '.join(args.code) + code
246
246
247 # if there is no local namespace then default to an empty dict
247 # if there is no local namespace then default to an empty dict
248 if local_ns is None:
248 if local_ns is None:
249 local_ns = {}
249 local_ns = {}
250
250
251 if args.input:
251 if args.input:
252 for input in ','.join(args.input).split(','):
252 for input in ','.join(args.input).split(','):
253 input = unicode_to_str(input)
253 input = unicode_to_str(input)
254 try:
254 try:
255 val = local_ns[input]
255 val = local_ns[input]
256 except KeyError:
256 except KeyError:
257 val = self.shell.user_ns[input]
257 val = self.shell.user_ns[input]
258 self._oct.put(input, val)
258 self._oct.put(input, val)
259
259
260 # generate plots in a temporary directory
260 # generate plots in a temporary directory
261 plot_dir = tempfile.mkdtemp()
261 plot_dir = tempfile.mkdtemp()
262 if args.size is not None:
262 if args.size is not None:
263 size = args.size
263 size = args.size
264 else:
264 else:
265 size = '400,240'
265 size = '400,240'
266
266
267 if args.format is not None:
267 if args.format is not None:
268 plot_format = args.format
268 plot_format = args.format
269 else:
269 else:
270 plot_format = 'png'
270 plot_format = 'png'
271
271
272 pre_call = '''
272 pre_call = '''
273 global __ipy_figures = [];
273 global __ipy_figures = [];
274 page_screen_output(0);
274 page_screen_output(0);
275
275
276 function fig_create(src, event)
276 function fig_create(src, event)
277 global __ipy_figures;
277 global __ipy_figures;
278 __ipy_figures(size(__ipy_figures) + 1) = src;
278 __ipy_figures(size(__ipy_figures) + 1) = src;
279 set(src, "visible", "off");
279 set(src, "visible", "off");
280 end
280 end
281
281
282 set(0, 'DefaultFigureCreateFcn', @fig_create);
282 set(0, 'DefaultFigureCreateFcn', @fig_create);
283
283
284 close all;
284 close all;
285 clear ans;
285 clear ans;
286
286
287 # ___<end_pre_call>___ #
287 # ___<end_pre_call>___ #
288 '''
288 '''
289
289
290 post_call = '''
290 post_call = '''
291 # ___<start_post_call>___ #
291 # ___<start_post_call>___ #
292
292
293 # Save output of the last execution
293 # Save output of the last execution
294 if exist("ans") == 1
294 if exist("ans") == 1
295 _ = ans;
295 _ = ans;
296 else
296 else
297 _ = nan;
297 _ = nan;
298 end
298 end
299
299
300 for f = __ipy_figures
300 for f = __ipy_figures
301 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
301 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
302 try
302 try
303 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
303 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
304 end
304 end
305 end
305 end
306
306
307 ''' % locals()
307 ''' % locals()
308
308
309 code = ' '.join((pre_call, code, post_call))
309 code = ' '.join((pre_call, code, post_call))
310 try:
310 try:
311 text_output = self._oct.run(code, verbose=False)
311 text_output = self._oct.run(code, verbose=False)
312 except (oct2py.Oct2PyError) as exception:
312 except (oct2py.Oct2PyError) as exception:
313 msg = exception.message
313 msg = exception.message
314 msg = msg.split('# ___<end_pre_call>___ #')[1]
314 msg = msg.split('# ___<end_pre_call>___ #')[1]
315 msg = msg.split('# ___<start_post_call>___ #')[0]
315 msg = msg.split('# ___<start_post_call>___ #')[0]
316 raise OctaveMagicError('Octave could not complete execution. '
316 raise OctaveMagicError('Octave could not complete execution. '
317 'Traceback (currently broken in oct2py): %s'
317 'Traceback (currently broken in oct2py): %s'
318 % msg)
318 % msg)
319
319
320 key = 'OctaveMagic.Octave'
320 key = 'OctaveMagic.Octave'
321 display_data = []
321 display_data = []
322
322
323 # Publish text output
323 # Publish text output
324 if text_output:
324 if text_output:
325 display_data.append((key, {'text/plain': text_output}))
325 display_data.append((key, {'text/plain': text_output}))
326
326
327 # Publish images
327 # Publish images
328 images = [open(imgfile, 'rb').read() for imgfile in \
328 images = [open(imgfile, 'rb').read() for imgfile in \
329 glob("%s/*" % plot_dir)]
329 glob("%s/*" % plot_dir)]
330 rmtree(plot_dir)
330 rmtree(plot_dir)
331
331
332 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
332 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
333 width, height = [int(s) for s in size.split(',')]
333 width, height = [int(s) for s in size.split(',')]
334 for image in images:
334 for image in images:
335 if plot_format == 'svg':
335 if plot_format == 'svg':
336 image = self._fix_gnuplot_svg_size(image, size=(width, height))
336 image = self._fix_gnuplot_svg_size(image, size=(width, height))
337 display_data.append((key, {plot_mime_type: image}))
337 display_data.append((key, {plot_mime_type: image}))
338
338
339 if args.output:
339 if args.output:
340 for output in ','.join(args.output).split(','):
340 for output in ','.join(args.output).split(','):
341 output = unicode_to_str(output)
341 output = unicode_to_str(output)
342 self.shell.push({output: self._oct.get(output)})
342 self.shell.push({output: self._oct.get(output)})
343
343
344 for source, data in display_data:
344 for source, data in display_data:
345 self._publish_display_data(source, data)
345 self._publish_display_data(source, data)
346
346
347 if return_output:
347 if return_output:
348 ans = self._oct.get('_')
348 ans = self._oct.get('_')
349
349
350 # Unfortunately, Octave doesn't have a "None" object,
350 # Unfortunately, Octave doesn't have a "None" object,
351 # so we can't return any NaN outputs
351 # so we can't return any NaN outputs
352 if np.isscalar(ans) and np.isnan(ans):
352 if np.isscalar(ans) and np.isnan(ans):
353 ans = None
353 ans = None
354
354
355 return ans
355 return ans
356
356
357
357
358 __doc__ = __doc__.format(
358 __doc__ = __doc__.format(
359 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
359 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
360 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
360 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
361 OCTAVE_PULL_DOC = ' '*8 + OctaveMagics.octave_pull.__doc__
361 OCTAVE_PULL_DOC = ' '*8 + OctaveMagics.octave_pull.__doc__
362 )
362 )
363
363
364
364
365 _loaded = False
366 def load_ipython_extension(ip):
365 def load_ipython_extension(ip):
367 """Load the extension in IPython."""
366 """Load the extension in IPython."""
368 global _loaded
367 ip.register_magics(OctaveMagics)
369 if not _loaded:
370 ip.register_magics(OctaveMagics)
371 _loaded = True
@@ -1,597 +1,593 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 ======
3 ======
4 Rmagic
4 Rmagic
5 ======
5 ======
6
6
7 Magic command interface for interactive work with R via rpy2
7 Magic command interface for interactive work with R via rpy2
8
8
9 Usage
9 Usage
10 =====
10 =====
11
11
12 ``%R``
12 ``%R``
13
13
14 {R_DOC}
14 {R_DOC}
15
15
16 ``%Rpush``
16 ``%Rpush``
17
17
18 {RPUSH_DOC}
18 {RPUSH_DOC}
19
19
20 ``%Rpull``
20 ``%Rpull``
21
21
22 {RPULL_DOC}
22 {RPULL_DOC}
23
23
24 ``%Rget``
24 ``%Rget``
25
25
26 {RGET_DOC}
26 {RGET_DOC}
27
27
28 """
28 """
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Copyright (C) 2012 The IPython Development Team
31 # Copyright (C) 2012 The IPython Development Team
32 #
32 #
33 # Distributed under the terms of the BSD License. The full license is in
33 # Distributed under the terms of the BSD License. The full license is in
34 # the file COPYING, distributed as part of this software.
34 # the file COPYING, distributed as part of this software.
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 import sys
37 import sys
38 import tempfile
38 import tempfile
39 from glob import glob
39 from glob import glob
40 from shutil import rmtree
40 from shutil import rmtree
41 from getopt import getopt
41 from getopt import getopt
42
42
43 # numpy and rpy2 imports
43 # numpy and rpy2 imports
44
44
45 import numpy as np
45 import numpy as np
46
46
47 import rpy2.rinterface as ri
47 import rpy2.rinterface as ri
48 import rpy2.robjects as ro
48 import rpy2.robjects as ro
49 from rpy2.robjects.numpy2ri import numpy2ri
49 from rpy2.robjects.numpy2ri import numpy2ri
50 ro.conversion.py2ri = numpy2ri
50 ro.conversion.py2ri = numpy2ri
51
51
52 # IPython imports
52 # IPython imports
53
53
54 from IPython.core.displaypub import publish_display_data
54 from IPython.core.displaypub import publish_display_data
55 from IPython.core.magic import (Magics, magics_class, cell_magic, line_magic,
55 from IPython.core.magic import (Magics, magics_class, cell_magic, line_magic,
56 line_cell_magic, needs_local_scope)
56 line_cell_magic, needs_local_scope)
57 from IPython.testing.skipdoctest import skip_doctest
57 from IPython.testing.skipdoctest import skip_doctest
58 from IPython.core.magic_arguments import (
58 from IPython.core.magic_arguments import (
59 argument, magic_arguments, parse_argstring
59 argument, magic_arguments, parse_argstring
60 )
60 )
61 from IPython.utils.py3compat import str_to_unicode, unicode_to_str, PY3
61 from IPython.utils.py3compat import str_to_unicode, unicode_to_str, PY3
62
62
63 class RInterpreterError(ri.RRuntimeError):
63 class RInterpreterError(ri.RRuntimeError):
64 """An error when running R code in a %%R magic cell."""
64 """An error when running R code in a %%R magic cell."""
65 def __init__(self, line, err, stdout):
65 def __init__(self, line, err, stdout):
66 self.line = line
66 self.line = line
67 self.err = err.rstrip()
67 self.err = err.rstrip()
68 self.stdout = stdout.rstrip()
68 self.stdout = stdout.rstrip()
69
69
70 def __unicode__(self):
70 def __unicode__(self):
71 s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \
71 s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \
72 (self.line, self.err)
72 (self.line, self.err)
73 if self.stdout and (self.stdout != self.err):
73 if self.stdout and (self.stdout != self.err):
74 s += '\nR stdout:\n' + self.stdout
74 s += '\nR stdout:\n' + self.stdout
75 return s
75 return s
76
76
77 if PY3:
77 if PY3:
78 __str__ = __unicode__
78 __str__ = __unicode__
79 else:
79 else:
80 def __str__(self):
80 def __str__(self):
81 return unicode_to_str(unicode(self), 'utf-8')
81 return unicode_to_str(unicode(self), 'utf-8')
82
82
83 def Rconverter(Robj, dataframe=False):
83 def Rconverter(Robj, dataframe=False):
84 """
84 """
85 Convert an object in R's namespace to one suitable
85 Convert an object in R's namespace to one suitable
86 for ipython's namespace.
86 for ipython's namespace.
87
87
88 For a data.frame, it tries to return a structured array.
88 For a data.frame, it tries to return a structured array.
89 It first checks for colnames, then names.
89 It first checks for colnames, then names.
90 If all are NULL, it returns np.asarray(Robj), else
90 If all are NULL, it returns np.asarray(Robj), else
91 it tries to construct a recarray
91 it tries to construct a recarray
92
92
93 Parameters
93 Parameters
94 ----------
94 ----------
95
95
96 Robj: an R object returned from rpy2
96 Robj: an R object returned from rpy2
97 """
97 """
98 is_data_frame = ro.r('is.data.frame')
98 is_data_frame = ro.r('is.data.frame')
99 colnames = ro.r('colnames')
99 colnames = ro.r('colnames')
100 rownames = ro.r('rownames') # with pandas, these could be used for the index
100 rownames = ro.r('rownames') # with pandas, these could be used for the index
101 names = ro.r('names')
101 names = ro.r('names')
102
102
103 if dataframe:
103 if dataframe:
104 as_data_frame = ro.r('as.data.frame')
104 as_data_frame = ro.r('as.data.frame')
105 cols = colnames(Robj)
105 cols = colnames(Robj)
106 _names = names(Robj)
106 _names = names(Robj)
107 if cols != ri.NULL:
107 if cols != ri.NULL:
108 Robj = as_data_frame(Robj)
108 Robj = as_data_frame(Robj)
109 names = tuple(np.array(cols))
109 names = tuple(np.array(cols))
110 elif _names != ri.NULL:
110 elif _names != ri.NULL:
111 names = tuple(np.array(_names))
111 names = tuple(np.array(_names))
112 else: # failed to find names
112 else: # failed to find names
113 return np.asarray(Robj)
113 return np.asarray(Robj)
114 Robj = np.rec.fromarrays(Robj, names = names)
114 Robj = np.rec.fromarrays(Robj, names = names)
115 return np.asarray(Robj)
115 return np.asarray(Robj)
116
116
117 @magics_class
117 @magics_class
118 class RMagics(Magics):
118 class RMagics(Magics):
119 """A set of magics useful for interactive work with R via rpy2.
119 """A set of magics useful for interactive work with R via rpy2.
120 """
120 """
121
121
122 def __init__(self, shell, Rconverter=Rconverter,
122 def __init__(self, shell, Rconverter=Rconverter,
123 pyconverter=np.asarray,
123 pyconverter=np.asarray,
124 cache_display_data=False):
124 cache_display_data=False):
125 """
125 """
126 Parameters
126 Parameters
127 ----------
127 ----------
128
128
129 shell : IPython shell
129 shell : IPython shell
130
130
131 pyconverter : callable
131 pyconverter : callable
132 To be called on values in ipython namespace before
132 To be called on values in ipython namespace before
133 assigning to variables in rpy2.
133 assigning to variables in rpy2.
134
134
135 cache_display_data : bool
135 cache_display_data : bool
136 If True, the published results of the final call to R are
136 If True, the published results of the final call to R are
137 cached in the variable 'display_cache'.
137 cached in the variable 'display_cache'.
138
138
139 """
139 """
140 super(RMagics, self).__init__(shell)
140 super(RMagics, self).__init__(shell)
141 self.cache_display_data = cache_display_data
141 self.cache_display_data = cache_display_data
142
142
143 self.r = ro.R()
143 self.r = ro.R()
144
144
145 self.Rstdout_cache = []
145 self.Rstdout_cache = []
146 self.pyconverter = pyconverter
146 self.pyconverter = pyconverter
147 self.Rconverter = Rconverter
147 self.Rconverter = Rconverter
148
148
149 def eval(self, line):
149 def eval(self, line):
150 '''
150 '''
151 Parse and evaluate a line with rpy2.
151 Parse and evaluate a line with rpy2.
152 Returns the output to R's stdout() connection
152 Returns the output to R's stdout() connection
153 and the value of eval(parse(line)).
153 and the value of eval(parse(line)).
154 '''
154 '''
155 old_writeconsole = ri.get_writeconsole()
155 old_writeconsole = ri.get_writeconsole()
156 ri.set_writeconsole(self.write_console)
156 ri.set_writeconsole(self.write_console)
157 try:
157 try:
158 value = ri.baseenv['eval'](ri.parse(line))
158 value = ri.baseenv['eval'](ri.parse(line))
159 except (ri.RRuntimeError, ValueError) as exception:
159 except (ri.RRuntimeError, ValueError) as exception:
160 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
160 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
161 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
161 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
162 text_output = self.flush()
162 text_output = self.flush()
163 ri.set_writeconsole(old_writeconsole)
163 ri.set_writeconsole(old_writeconsole)
164 return text_output, value
164 return text_output, value
165
165
166 def write_console(self, output):
166 def write_console(self, output):
167 '''
167 '''
168 A hook to capture R's stdout in a cache.
168 A hook to capture R's stdout in a cache.
169 '''
169 '''
170 self.Rstdout_cache.append(output)
170 self.Rstdout_cache.append(output)
171
171
172 def flush(self):
172 def flush(self):
173 '''
173 '''
174 Flush R's stdout cache to a string, returning the string.
174 Flush R's stdout cache to a string, returning the string.
175 '''
175 '''
176 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
176 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
177 self.Rstdout_cache = []
177 self.Rstdout_cache = []
178 return value
178 return value
179
179
180 @skip_doctest
180 @skip_doctest
181 @line_magic
181 @line_magic
182 def Rpush(self, line):
182 def Rpush(self, line):
183 '''
183 '''
184 A line-level magic for R that pushes
184 A line-level magic for R that pushes
185 variables from python to rpy2. The line should be made up
185 variables from python to rpy2. The line should be made up
186 of whitespace separated variable names in the IPython
186 of whitespace separated variable names in the IPython
187 namespace::
187 namespace::
188
188
189 In [7]: import numpy as np
189 In [7]: import numpy as np
190
190
191 In [8]: X = np.array([4.5,6.3,7.9])
191 In [8]: X = np.array([4.5,6.3,7.9])
192
192
193 In [9]: X.mean()
193 In [9]: X.mean()
194 Out[9]: 6.2333333333333343
194 Out[9]: 6.2333333333333343
195
195
196 In [10]: %Rpush X
196 In [10]: %Rpush X
197
197
198 In [11]: %R mean(X)
198 In [11]: %R mean(X)
199 Out[11]: array([ 6.23333333])
199 Out[11]: array([ 6.23333333])
200
200
201 '''
201 '''
202
202
203 inputs = line.split(' ')
203 inputs = line.split(' ')
204 for input in inputs:
204 for input in inputs:
205 self.r.assign(input, self.pyconverter(self.shell.user_ns[input]))
205 self.r.assign(input, self.pyconverter(self.shell.user_ns[input]))
206
206
207 @skip_doctest
207 @skip_doctest
208 @magic_arguments()
208 @magic_arguments()
209 @argument(
209 @argument(
210 '-d', '--as_dataframe', action='store_true',
210 '-d', '--as_dataframe', action='store_true',
211 default=False,
211 default=False,
212 help='Convert objects to data.frames before returning to ipython.'
212 help='Convert objects to data.frames before returning to ipython.'
213 )
213 )
214 @argument(
214 @argument(
215 'outputs',
215 'outputs',
216 nargs='*',
216 nargs='*',
217 )
217 )
218 @line_magic
218 @line_magic
219 def Rpull(self, line):
219 def Rpull(self, line):
220 '''
220 '''
221 A line-level magic for R that pulls
221 A line-level magic for R that pulls
222 variables from python to rpy2::
222 variables from python to rpy2::
223
223
224 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
224 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
225
225
226 In [19]: %Rpull x y z
226 In [19]: %Rpull x y z
227
227
228 In [20]: x
228 In [20]: x
229 Out[20]: array([ 3. , 4. , 6.7])
229 Out[20]: array([ 3. , 4. , 6.7])
230
230
231 In [21]: y
231 In [21]: y
232 Out[21]: array([ 4., 6., 7.])
232 Out[21]: array([ 4., 6., 7.])
233
233
234 In [22]: z
234 In [22]: z
235 Out[22]:
235 Out[22]:
236 array(['a', '3', '4'],
236 array(['a', '3', '4'],
237 dtype='|S1')
237 dtype='|S1')
238
238
239
239
240 If --as_dataframe, then each object is returned as a structured array
240 If --as_dataframe, then each object is returned as a structured array
241 after first passed through "as.data.frame" in R before
241 after first passed through "as.data.frame" in R before
242 being calling self.Rconverter.
242 being calling self.Rconverter.
243 This is useful when a structured array is desired as output, or
243 This is useful when a structured array is desired as output, or
244 when the object in R has mixed data types.
244 when the object in R has mixed data types.
245 See the %%R docstring for more examples.
245 See the %%R docstring for more examples.
246
246
247 Notes
247 Notes
248 -----
248 -----
249
249
250 Beware that R names can have '.' so this is not fool proof.
250 Beware that R names can have '.' so this is not fool proof.
251 To avoid this, don't name your R objects with '.'s...
251 To avoid this, don't name your R objects with '.'s...
252
252
253 '''
253 '''
254 args = parse_argstring(self.Rpull, line)
254 args = parse_argstring(self.Rpull, line)
255 outputs = args.outputs
255 outputs = args.outputs
256 for output in outputs:
256 for output in outputs:
257 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
257 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
258
258
259 @skip_doctest
259 @skip_doctest
260 @magic_arguments()
260 @magic_arguments()
261 @argument(
261 @argument(
262 '-d', '--as_dataframe', action='store_true',
262 '-d', '--as_dataframe', action='store_true',
263 default=False,
263 default=False,
264 help='Convert objects to data.frames before returning to ipython.'
264 help='Convert objects to data.frames before returning to ipython.'
265 )
265 )
266 @argument(
266 @argument(
267 'output',
267 'output',
268 nargs=1,
268 nargs=1,
269 type=str,
269 type=str,
270 )
270 )
271 @line_magic
271 @line_magic
272 def Rget(self, line):
272 def Rget(self, line):
273 '''
273 '''
274 Return an object from rpy2, possibly as a structured array (if possible).
274 Return an object from rpy2, possibly as a structured array (if possible).
275 Similar to Rpull except only one argument is accepted and the value is
275 Similar to Rpull except only one argument is accepted and the value is
276 returned rather than pushed to self.shell.user_ns::
276 returned rather than pushed to self.shell.user_ns::
277
277
278 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
278 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
279
279
280 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
280 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
281
281
282 In [5]: %R -i datapy
282 In [5]: %R -i datapy
283
283
284 In [6]: %Rget datapy
284 In [6]: %Rget datapy
285 Out[6]:
285 Out[6]:
286 array([['1', '2', '3', '4'],
286 array([['1', '2', '3', '4'],
287 ['2', '3', '2', '5'],
287 ['2', '3', '2', '5'],
288 ['a', 'b', 'c', 'e']],
288 ['a', 'b', 'c', 'e']],
289 dtype='|S1')
289 dtype='|S1')
290
290
291 In [7]: %Rget -d datapy
291 In [7]: %Rget -d datapy
292 Out[7]:
292 Out[7]:
293 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
293 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
294 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
294 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
295
295
296 '''
296 '''
297 args = parse_argstring(self.Rget, line)
297 args = parse_argstring(self.Rget, line)
298 output = args.output
298 output = args.output
299 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
299 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
300
300
301
301
302 @skip_doctest
302 @skip_doctest
303 @magic_arguments()
303 @magic_arguments()
304 @argument(
304 @argument(
305 '-i', '--input', action='append',
305 '-i', '--input', action='append',
306 help='Names of input variable from shell.user_ns to be assigned to R variables of the same names after calling self.pyconverter. Multiple names can be passed separated only by commas with no whitespace.'
306 help='Names of input variable from shell.user_ns to be assigned to R variables of the same names after calling self.pyconverter. Multiple names can be passed separated only by commas with no whitespace.'
307 )
307 )
308 @argument(
308 @argument(
309 '-o', '--output', action='append',
309 '-o', '--output', action='append',
310 help='Names of variables to be pushed from rpy2 to shell.user_ns after executing cell body and applying self.Rconverter. Multiple names can be passed separated only by commas with no whitespace.'
310 help='Names of variables to be pushed from rpy2 to shell.user_ns after executing cell body and applying self.Rconverter. Multiple names can be passed separated only by commas with no whitespace.'
311 )
311 )
312 @argument(
312 @argument(
313 '-w', '--width', type=int,
313 '-w', '--width', type=int,
314 help='Width of png plotting device sent as an argument to *png* in R.'
314 help='Width of png plotting device sent as an argument to *png* in R.'
315 )
315 )
316 @argument(
316 @argument(
317 '-h', '--height', type=int,
317 '-h', '--height', type=int,
318 help='Height of png plotting device sent as an argument to *png* in R.'
318 help='Height of png plotting device sent as an argument to *png* in R.'
319 )
319 )
320
320
321 @argument(
321 @argument(
322 '-d', '--dataframe', action='append',
322 '-d', '--dataframe', action='append',
323 help='Convert these objects to data.frames and return as structured arrays.'
323 help='Convert these objects to data.frames and return as structured arrays.'
324 )
324 )
325 @argument(
325 @argument(
326 '-u', '--units', type=int,
326 '-u', '--units', type=int,
327 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
327 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
328 )
328 )
329 @argument(
329 @argument(
330 '-p', '--pointsize', type=int,
330 '-p', '--pointsize', type=int,
331 help='Pointsize of png plotting device sent as an argument to *png* in R.'
331 help='Pointsize of png plotting device sent as an argument to *png* in R.'
332 )
332 )
333 @argument(
333 @argument(
334 '-b', '--bg',
334 '-b', '--bg',
335 help='Background of png plotting device sent as an argument to *png* in R.'
335 help='Background of png plotting device sent as an argument to *png* in R.'
336 )
336 )
337 @argument(
337 @argument(
338 '-n', '--noreturn',
338 '-n', '--noreturn',
339 help='Force the magic to not return anything.',
339 help='Force the magic to not return anything.',
340 action='store_true',
340 action='store_true',
341 default=False
341 default=False
342 )
342 )
343 @argument(
343 @argument(
344 'code',
344 'code',
345 nargs='*',
345 nargs='*',
346 )
346 )
347 @needs_local_scope
347 @needs_local_scope
348 @line_cell_magic
348 @line_cell_magic
349 def R(self, line, cell=None, local_ns=None):
349 def R(self, line, cell=None, local_ns=None):
350 '''
350 '''
351 Execute code in R, and pull some of the results back into the Python namespace.
351 Execute code in R, and pull some of the results back into the Python namespace.
352
352
353 In line mode, this will evaluate an expression and convert the returned value to a Python object.
353 In line mode, this will evaluate an expression and convert the returned value to a Python object.
354 The return value is determined by rpy2's behaviour of returning the result of evaluating the
354 The return value is determined by rpy2's behaviour of returning the result of evaluating the
355 final line.
355 final line.
356
356
357 Multiple R lines can be executed by joining them with semicolons::
357 Multiple R lines can be executed by joining them with semicolons::
358
358
359 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
359 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
360 Out[9]: array([ 4.25])
360 Out[9]: array([ 4.25])
361
361
362 As a cell, this will run a block of R code, without bringing anything back by default::
362 As a cell, this will run a block of R code, without bringing anything back by default::
363
363
364 In [10]: %%R
364 In [10]: %%R
365 ....: Y = c(2,4,3,9)
365 ....: Y = c(2,4,3,9)
366 ....: print(summary(lm(Y~X)))
366 ....: print(summary(lm(Y~X)))
367 ....:
367 ....:
368
368
369 Call:
369 Call:
370 lm(formula = Y ~ X)
370 lm(formula = Y ~ X)
371
371
372 Residuals:
372 Residuals:
373 1 2 3 4
373 1 2 3 4
374 0.88 -0.24 -2.28 1.64
374 0.88 -0.24 -2.28 1.64
375
375
376 Coefficients:
376 Coefficients:
377 Estimate Std. Error t value Pr(>|t|)
377 Estimate Std. Error t value Pr(>|t|)
378 (Intercept) 0.0800 2.3000 0.035 0.975
378 (Intercept) 0.0800 2.3000 0.035 0.975
379 X 1.0400 0.4822 2.157 0.164
379 X 1.0400 0.4822 2.157 0.164
380
380
381 Residual standard error: 2.088 on 2 degrees of freedom
381 Residual standard error: 2.088 on 2 degrees of freedom
382 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
382 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
383 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
383 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
384
384
385 In the notebook, plots are published as the output of the cell.
385 In the notebook, plots are published as the output of the cell.
386
386
387 %R plot(X, Y)
387 %R plot(X, Y)
388
388
389 will create a scatter plot of X bs Y.
389 will create a scatter plot of X bs Y.
390
390
391 If cell is not None and line has some R code, it is prepended to
391 If cell is not None and line has some R code, it is prepended to
392 the R code in cell.
392 the R code in cell.
393
393
394 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
394 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
395
395
396 In [14]: Z = np.array([1,4,5,10])
396 In [14]: Z = np.array([1,4,5,10])
397
397
398 In [15]: %R -i Z mean(Z)
398 In [15]: %R -i Z mean(Z)
399 Out[15]: array([ 5.])
399 Out[15]: array([ 5.])
400
400
401
401
402 In [16]: %R -o W W=Z*mean(Z)
402 In [16]: %R -o W W=Z*mean(Z)
403 Out[16]: array([ 5., 20., 25., 50.])
403 Out[16]: array([ 5., 20., 25., 50.])
404
404
405 In [17]: W
405 In [17]: W
406 Out[17]: array([ 5., 20., 25., 50.])
406 Out[17]: array([ 5., 20., 25., 50.])
407
407
408 The return value is determined by these rules:
408 The return value is determined by these rules:
409
409
410 * If the cell is not None, the magic returns None.
410 * If the cell is not None, the magic returns None.
411
411
412 * If the cell evaluates as False, the resulting value is returned
412 * If the cell evaluates as False, the resulting value is returned
413 unless the final line prints something to the console, in
413 unless the final line prints something to the console, in
414 which case None is returned.
414 which case None is returned.
415
415
416 * If the final line results in a NULL value when evaluated
416 * If the final line results in a NULL value when evaluated
417 by rpy2, then None is returned.
417 by rpy2, then None is returned.
418
418
419 * No attempt is made to convert the final value to a structured array.
419 * No attempt is made to convert the final value to a structured array.
420 Use the --dataframe flag or %Rget to push / return a structured array.
420 Use the --dataframe flag or %Rget to push / return a structured array.
421
421
422 * If the -n flag is present, there is no return value.
422 * If the -n flag is present, there is no return value.
423
423
424 * A trailing ';' will also result in no return value as the last
424 * A trailing ';' will also result in no return value as the last
425 value in the line is an empty string.
425 value in the line is an empty string.
426
426
427 The --dataframe argument will attempt to return structured arrays.
427 The --dataframe argument will attempt to return structured arrays.
428 This is useful for dataframes with
428 This is useful for dataframes with
429 mixed data types. Note also that for a data.frame,
429 mixed data types. Note also that for a data.frame,
430 if it is returned as an ndarray, it is transposed::
430 if it is returned as an ndarray, it is transposed::
431
431
432 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
432 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
433
433
434 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
434 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
435
435
436 In [20]: %%R -o datar
436 In [20]: %%R -o datar
437 datar = datapy
437 datar = datapy
438 ....:
438 ....:
439
439
440 In [21]: datar
440 In [21]: datar
441 Out[21]:
441 Out[21]:
442 array([['1', '2', '3', '4'],
442 array([['1', '2', '3', '4'],
443 ['2', '3', '2', '5'],
443 ['2', '3', '2', '5'],
444 ['a', 'b', 'c', 'e']],
444 ['a', 'b', 'c', 'e']],
445 dtype='|S1')
445 dtype='|S1')
446
446
447 In [22]: %%R -d datar
447 In [22]: %%R -d datar
448 datar = datapy
448 datar = datapy
449 ....:
449 ....:
450
450
451 In [23]: datar
451 In [23]: datar
452 Out[23]:
452 Out[23]:
453 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
453 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
454 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
454 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
455
455
456 The --dataframe argument first tries colnames, then names.
456 The --dataframe argument first tries colnames, then names.
457 If both are NULL, it returns an ndarray (i.e. unstructured)::
457 If both are NULL, it returns an ndarray (i.e. unstructured)::
458
458
459 In [1]: %R mydata=c(4,6,8.3); NULL
459 In [1]: %R mydata=c(4,6,8.3); NULL
460
460
461 In [2]: %R -d mydata
461 In [2]: %R -d mydata
462
462
463 In [3]: mydata
463 In [3]: mydata
464 Out[3]: array([ 4. , 6. , 8.3])
464 Out[3]: array([ 4. , 6. , 8.3])
465
465
466 In [4]: %R names(mydata) = c('a','b','c'); NULL
466 In [4]: %R names(mydata) = c('a','b','c'); NULL
467
467
468 In [5]: %R -d mydata
468 In [5]: %R -d mydata
469
469
470 In [6]: mydata
470 In [6]: mydata
471 Out[6]:
471 Out[6]:
472 array((4.0, 6.0, 8.3),
472 array((4.0, 6.0, 8.3),
473 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
473 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
474
474
475 In [7]: %R -o mydata
475 In [7]: %R -o mydata
476
476
477 In [8]: mydata
477 In [8]: mydata
478 Out[8]: array([ 4. , 6. , 8.3])
478 Out[8]: array([ 4. , 6. , 8.3])
479
479
480 '''
480 '''
481
481
482 args = parse_argstring(self.R, line)
482 args = parse_argstring(self.R, line)
483
483
484 # arguments 'code' in line are prepended to
484 # arguments 'code' in line are prepended to
485 # the cell lines
485 # the cell lines
486
486
487 if cell is None:
487 if cell is None:
488 code = ''
488 code = ''
489 return_output = True
489 return_output = True
490 line_mode = True
490 line_mode = True
491 else:
491 else:
492 code = cell
492 code = cell
493 return_output = False
493 return_output = False
494 line_mode = False
494 line_mode = False
495
495
496 code = ' '.join(args.code) + code
496 code = ' '.join(args.code) + code
497
497
498 # if there is no local namespace then default to an empty dict
498 # if there is no local namespace then default to an empty dict
499 if local_ns is None:
499 if local_ns is None:
500 local_ns = {}
500 local_ns = {}
501
501
502 if args.input:
502 if args.input:
503 for input in ','.join(args.input).split(','):
503 for input in ','.join(args.input).split(','):
504 try:
504 try:
505 val = local_ns[input]
505 val = local_ns[input]
506 except KeyError:
506 except KeyError:
507 val = self.shell.user_ns[input]
507 val = self.shell.user_ns[input]
508 self.r.assign(input, self.pyconverter(val))
508 self.r.assign(input, self.pyconverter(val))
509
509
510 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'height', 'width', 'bg', 'pointsize']])
510 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'height', 'width', 'bg', 'pointsize']])
511 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
511 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
512 # execute the R code in a temporary directory
512 # execute the R code in a temporary directory
513
513
514 tmpd = tempfile.mkdtemp()
514 tmpd = tempfile.mkdtemp()
515 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd, png_args))
515 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd, png_args))
516
516
517 text_output = ''
517 text_output = ''
518 if line_mode:
518 if line_mode:
519 for line in code.split(';'):
519 for line in code.split(';'):
520 text_result, result = self.eval(line)
520 text_result, result = self.eval(line)
521 text_output += text_result
521 text_output += text_result
522 if text_result:
522 if text_result:
523 # the last line printed something to the console so we won't return it
523 # the last line printed something to the console so we won't return it
524 return_output = False
524 return_output = False
525 else:
525 else:
526 text_result, result = self.eval(code)
526 text_result, result = self.eval(code)
527 text_output += text_result
527 text_output += text_result
528
528
529 self.r('dev.off()')
529 self.r('dev.off()')
530
530
531 # read out all the saved .png files
531 # read out all the saved .png files
532
532
533 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
533 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
534
534
535 # now publish the images
535 # now publish the images
536 # mimicking IPython/zmq/pylab/backend_inline.py
536 # mimicking IPython/zmq/pylab/backend_inline.py
537 fmt = 'png'
537 fmt = 'png'
538 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
538 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
539 mime = mimetypes[fmt]
539 mime = mimetypes[fmt]
540
540
541 # publish the printed R objects, if any
541 # publish the printed R objects, if any
542
542
543 display_data = []
543 display_data = []
544 if text_output:
544 if text_output:
545 display_data.append(('RMagic.R', {'text/plain':text_output}))
545 display_data.append(('RMagic.R', {'text/plain':text_output}))
546
546
547 # flush text streams before sending figures, helps a little with output
547 # flush text streams before sending figures, helps a little with output
548 for image in images:
548 for image in images:
549 # synchronization in the console (though it's a bandaid, not a real sln)
549 # synchronization in the console (though it's a bandaid, not a real sln)
550 sys.stdout.flush(); sys.stderr.flush()
550 sys.stdout.flush(); sys.stderr.flush()
551 display_data.append(('RMagic.R', {mime: image}))
551 display_data.append(('RMagic.R', {mime: image}))
552
552
553 # kill the temporary directory
553 # kill the temporary directory
554 rmtree(tmpd)
554 rmtree(tmpd)
555
555
556 # try to turn every output into a numpy array
556 # try to turn every output into a numpy array
557 # this means that output are assumed to be castable
557 # this means that output are assumed to be castable
558 # as numpy arrays
558 # as numpy arrays
559
559
560 if args.output:
560 if args.output:
561 for output in ','.join(args.output).split(','):
561 for output in ','.join(args.output).split(','):
562 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
562 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
563
563
564 if args.dataframe:
564 if args.dataframe:
565 for output in ','.join(args.dataframe).split(','):
565 for output in ','.join(args.dataframe).split(','):
566 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
566 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
567
567
568 for tag, disp_d in display_data:
568 for tag, disp_d in display_data:
569 publish_display_data(tag, disp_d)
569 publish_display_data(tag, disp_d)
570
570
571 # this will keep a reference to the display_data
571 # this will keep a reference to the display_data
572 # which might be useful to other objects who happen to use
572 # which might be useful to other objects who happen to use
573 # this method
573 # this method
574
574
575 if self.cache_display_data:
575 if self.cache_display_data:
576 self.display_cache = display_data
576 self.display_cache = display_data
577
577
578 # if in line mode and return_output, return the result as an ndarray
578 # if in line mode and return_output, return the result as an ndarray
579 if return_output and not args.noreturn:
579 if return_output and not args.noreturn:
580 if result != ri.NULL:
580 if result != ri.NULL:
581 return self.Rconverter(result, dataframe=False)
581 return self.Rconverter(result, dataframe=False)
582
582
583 __doc__ = __doc__.format(
583 __doc__ = __doc__.format(
584 R_DOC = ' '*8 + RMagics.R.__doc__,
584 R_DOC = ' '*8 + RMagics.R.__doc__,
585 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
585 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
586 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
586 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
587 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
587 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
588 )
588 )
589
589
590
590
591 _loaded = False
592 def load_ipython_extension(ip):
591 def load_ipython_extension(ip):
593 """Load the extension in IPython."""
592 """Load the extension in IPython."""
594 global _loaded
593 ip.register_magics(RMagics)
595 if not _loaded:
596 ip.register_magics(RMagics)
597 _loaded = True
@@ -1,220 +1,214 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 %store magic for lightweight persistence.
3 %store magic for lightweight persistence.
4
4
5 Stores variables, aliases and macros in IPython's database.
5 Stores variables, aliases and macros in IPython's database.
6
6
7 To automatically restore stored variables at startup, add this to your
7 To automatically restore stored variables at startup, add this to your
8 :file:`ipython_config.py` file::
8 :file:`ipython_config.py` file::
9
9
10 c.StoreMagic.autorestore = True
10 c.StoreMagic.autorestore = True
11 """
11 """
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (c) 2012, The IPython Development Team.
13 # Copyright (c) 2012, The IPython Development Team.
14 #
14 #
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16 #
16 #
17 # The full license is in the file COPYING.txt, distributed with this software.
17 # The full license is in the file COPYING.txt, distributed with this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 # Stdlib
24 # Stdlib
25 import inspect, os, sys, textwrap
25 import inspect, os, sys, textwrap
26
26
27 # Our own
27 # Our own
28 from IPython.core.error import UsageError
28 from IPython.core.error import UsageError
29 from IPython.core.fakemodule import FakeModule
29 from IPython.core.fakemodule import FakeModule
30 from IPython.core.magic import Magics, magics_class, line_magic
30 from IPython.core.magic import Magics, magics_class, line_magic
31 from IPython.testing.skipdoctest import skip_doctest
31 from IPython.testing.skipdoctest import skip_doctest
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Functions and classes
34 # Functions and classes
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 def restore_aliases(ip):
37 def restore_aliases(ip):
38 staliases = ip.db.get('stored_aliases', {})
38 staliases = ip.db.get('stored_aliases', {})
39 for k,v in staliases.items():
39 for k,v in staliases.items():
40 #print "restore alias",k,v # dbg
40 #print "restore alias",k,v # dbg
41 #self.alias_table[k] = v
41 #self.alias_table[k] = v
42 ip.alias_manager.define_alias(k,v)
42 ip.alias_manager.define_alias(k,v)
43
43
44
44
45 def refresh_variables(ip):
45 def refresh_variables(ip):
46 db = ip.db
46 db = ip.db
47 for key in db.keys('autorestore/*'):
47 for key in db.keys('autorestore/*'):
48 # strip autorestore
48 # strip autorestore
49 justkey = os.path.basename(key)
49 justkey = os.path.basename(key)
50 try:
50 try:
51 obj = db[key]
51 obj = db[key]
52 except KeyError:
52 except KeyError:
53 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
53 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
54 print "The error was:", sys.exc_info()[0]
54 print "The error was:", sys.exc_info()[0]
55 else:
55 else:
56 #print "restored",justkey,"=",obj #dbg
56 #print "restored",justkey,"=",obj #dbg
57 ip.user_ns[justkey] = obj
57 ip.user_ns[justkey] = obj
58
58
59
59
60 def restore_dhist(ip):
60 def restore_dhist(ip):
61 ip.user_ns['_dh'] = ip.db.get('dhist',[])
61 ip.user_ns['_dh'] = ip.db.get('dhist',[])
62
62
63
63
64 def restore_data(ip):
64 def restore_data(ip):
65 refresh_variables(ip)
65 refresh_variables(ip)
66 restore_aliases(ip)
66 restore_aliases(ip)
67 restore_dhist(ip)
67 restore_dhist(ip)
68
68
69
69
70 @magics_class
70 @magics_class
71 class StoreMagics(Magics):
71 class StoreMagics(Magics):
72 """Lightweight persistence for python variables.
72 """Lightweight persistence for python variables.
73
73
74 Provides the %store magic."""
74 Provides the %store magic."""
75
75
76 @skip_doctest
76 @skip_doctest
77 @line_magic
77 @line_magic
78 def store(self, parameter_s=''):
78 def store(self, parameter_s=''):
79 """Lightweight persistence for python variables.
79 """Lightweight persistence for python variables.
80
80
81 Example::
81 Example::
82
82
83 In [1]: l = ['hello',10,'world']
83 In [1]: l = ['hello',10,'world']
84 In [2]: %store l
84 In [2]: %store l
85 In [3]: exit
85 In [3]: exit
86
86
87 (IPython session is closed and started again...)
87 (IPython session is closed and started again...)
88
88
89 ville@badger:~$ ipython
89 ville@badger:~$ ipython
90 In [1]: l
90 In [1]: l
91 Out[1]: ['hello', 10, 'world']
91 Out[1]: ['hello', 10, 'world']
92
92
93 Usage:
93 Usage:
94
94
95 * ``%store`` - Show list of all variables and their current
95 * ``%store`` - Show list of all variables and their current
96 values
96 values
97 * ``%store spam`` - Store the *current* value of the variable spam
97 * ``%store spam`` - Store the *current* value of the variable spam
98 to disk
98 to disk
99 * ``%store -d spam`` - Remove the variable and its value from storage
99 * ``%store -d spam`` - Remove the variable and its value from storage
100 * ``%store -z`` - Remove all variables from storage
100 * ``%store -z`` - Remove all variables from storage
101 * ``%store -r`` - Refresh all variables from store (delete
101 * ``%store -r`` - Refresh all variables from store (delete
102 current vals)
102 current vals)
103 * ``%store foo >a.txt`` - Store value of foo to new file a.txt
103 * ``%store foo >a.txt`` - Store value of foo to new file a.txt
104 * ``%store foo >>a.txt`` - Append value of foo to file a.txt
104 * ``%store foo >>a.txt`` - Append value of foo to file a.txt
105
105
106 It should be noted that if you change the value of a variable, you
106 It should be noted that if you change the value of a variable, you
107 need to %store it again if you want to persist the new value.
107 need to %store it again if you want to persist the new value.
108
108
109 Note also that the variables will need to be pickleable; most basic
109 Note also that the variables will need to be pickleable; most basic
110 python types can be safely %store'd.
110 python types can be safely %store'd.
111
111
112 Also aliases can be %store'd across sessions.
112 Also aliases can be %store'd across sessions.
113 """
113 """
114
114
115 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
115 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
116 args = argsl.split(None,1)
116 args = argsl.split(None,1)
117 ip = self.shell
117 ip = self.shell
118 db = ip.db
118 db = ip.db
119 # delete
119 # delete
120 if 'd' in opts:
120 if 'd' in opts:
121 try:
121 try:
122 todel = args[0]
122 todel = args[0]
123 except IndexError:
123 except IndexError:
124 raise UsageError('You must provide the variable to forget')
124 raise UsageError('You must provide the variable to forget')
125 else:
125 else:
126 try:
126 try:
127 del db['autorestore/' + todel]
127 del db['autorestore/' + todel]
128 except:
128 except:
129 raise UsageError("Can't delete variable '%s'" % todel)
129 raise UsageError("Can't delete variable '%s'" % todel)
130 # reset
130 # reset
131 elif 'z' in opts:
131 elif 'z' in opts:
132 for k in db.keys('autorestore/*'):
132 for k in db.keys('autorestore/*'):
133 del db[k]
133 del db[k]
134
134
135 elif 'r' in opts:
135 elif 'r' in opts:
136 refresh_variables(ip)
136 refresh_variables(ip)
137
137
138
138
139 # run without arguments -> list variables & values
139 # run without arguments -> list variables & values
140 elif not args:
140 elif not args:
141 vars = db.keys('autorestore/*')
141 vars = db.keys('autorestore/*')
142 vars.sort()
142 vars.sort()
143 if vars:
143 if vars:
144 size = max(map(len, vars))
144 size = max(map(len, vars))
145 else:
145 else:
146 size = 0
146 size = 0
147
147
148 print 'Stored variables and their in-db values:'
148 print 'Stored variables and their in-db values:'
149 fmt = '%-'+str(size)+'s -> %s'
149 fmt = '%-'+str(size)+'s -> %s'
150 get = db.get
150 get = db.get
151 for var in vars:
151 for var in vars:
152 justkey = os.path.basename(var)
152 justkey = os.path.basename(var)
153 # print 30 first characters from every var
153 # print 30 first characters from every var
154 print fmt % (justkey, repr(get(var, '<unavailable>'))[:50])
154 print fmt % (justkey, repr(get(var, '<unavailable>'))[:50])
155
155
156 # default action - store the variable
156 # default action - store the variable
157 else:
157 else:
158 # %store foo >file.txt or >>file.txt
158 # %store foo >file.txt or >>file.txt
159 if len(args) > 1 and args[1].startswith('>'):
159 if len(args) > 1 and args[1].startswith('>'):
160 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
160 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
161 if args[1].startswith('>>'):
161 if args[1].startswith('>>'):
162 fil = open(fnam, 'a')
162 fil = open(fnam, 'a')
163 else:
163 else:
164 fil = open(fnam, 'w')
164 fil = open(fnam, 'w')
165 obj = ip.ev(args[0])
165 obj = ip.ev(args[0])
166 print "Writing '%s' (%s) to file '%s'." % (args[0],
166 print "Writing '%s' (%s) to file '%s'." % (args[0],
167 obj.__class__.__name__, fnam)
167 obj.__class__.__name__, fnam)
168
168
169
169
170 if not isinstance (obj, basestring):
170 if not isinstance (obj, basestring):
171 from pprint import pprint
171 from pprint import pprint
172 pprint(obj, fil)
172 pprint(obj, fil)
173 else:
173 else:
174 fil.write(obj)
174 fil.write(obj)
175 if not obj.endswith('\n'):
175 if not obj.endswith('\n'):
176 fil.write('\n')
176 fil.write('\n')
177
177
178 fil.close()
178 fil.close()
179 return
179 return
180
180
181 # %store foo
181 # %store foo
182 try:
182 try:
183 obj = ip.user_ns[args[0]]
183 obj = ip.user_ns[args[0]]
184 except KeyError:
184 except KeyError:
185 # it might be an alias
185 # it might be an alias
186 # This needs to be refactored to use the new AliasManager stuff.
186 # This needs to be refactored to use the new AliasManager stuff.
187 if args[0] in ip.alias_manager:
187 if args[0] in ip.alias_manager:
188 name = args[0]
188 name = args[0]
189 nargs, cmd = ip.alias_manager.alias_table[ name ]
189 nargs, cmd = ip.alias_manager.alias_table[ name ]
190 staliases = db.get('stored_aliases',{})
190 staliases = db.get('stored_aliases',{})
191 staliases[ name ] = cmd
191 staliases[ name ] = cmd
192 db['stored_aliases'] = staliases
192 db['stored_aliases'] = staliases
193 print "Alias stored: %s (%s)" % (name, cmd)
193 print "Alias stored: %s (%s)" % (name, cmd)
194 return
194 return
195 else:
195 else:
196 raise UsageError("Unknown variable '%s'" % args[0])
196 raise UsageError("Unknown variable '%s'" % args[0])
197
197
198 else:
198 else:
199 if isinstance(inspect.getmodule(obj), FakeModule):
199 if isinstance(inspect.getmodule(obj), FakeModule):
200 print textwrap.dedent("""\
200 print textwrap.dedent("""\
201 Warning:%s is %s
201 Warning:%s is %s
202 Proper storage of interactively declared classes (or instances
202 Proper storage of interactively declared classes (or instances
203 of those classes) is not possible! Only instances
203 of those classes) is not possible! Only instances
204 of classes in real modules on file system can be %%store'd.
204 of classes in real modules on file system can be %%store'd.
205 """ % (args[0], obj) )
205 """ % (args[0], obj) )
206 return
206 return
207 #pickled = pickle.dumps(obj)
207 #pickled = pickle.dumps(obj)
208 db[ 'autorestore/' + args[0] ] = obj
208 db[ 'autorestore/' + args[0] ] = obj
209 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
209 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
210
210
211
211
212 _loaded = False
213
214
215 def load_ipython_extension(ip):
212 def load_ipython_extension(ip):
216 """Load the extension in IPython."""
213 """Load the extension in IPython."""
217 global _loaded
214 ip.register_magics(StoreMagics)
218 if not _loaded:
219 ip.register_magics(StoreMagics)
220 _loaded = True
@@ -1,354 +1,356 b''
1 """Basic ssh tunnel utilities, and convenience functions for tunneling
1 """Basic ssh tunnel utilities, and convenience functions for tunneling
2 zeromq connections.
2 zeromq connections.
3
3
4 Authors
4 Authors
5 -------
5 -------
6 * Min RK
6 * Min RK
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2010-2011 The IPython Development Team
10 # Copyright (C) 2010-2011 The IPython Development Team
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
16
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 from __future__ import print_function
22 from __future__ import print_function
23
23
24 import os,sys, atexit
24 import os,sys, atexit
25 import signal
25 import socket
26 import socket
26 from multiprocessing import Process
27 from multiprocessing import Process
27 from getpass import getpass, getuser
28 from getpass import getpass, getuser
28 import warnings
29 import warnings
29
30
30 try:
31 try:
31 with warnings.catch_warnings():
32 with warnings.catch_warnings():
32 warnings.simplefilter('ignore', DeprecationWarning)
33 warnings.simplefilter('ignore', DeprecationWarning)
33 import paramiko
34 import paramiko
34 except ImportError:
35 except ImportError:
35 paramiko = None
36 paramiko = None
36 else:
37 else:
37 from forward import forward_tunnel
38 from forward import forward_tunnel
38
39
39 try:
40 try:
40 from IPython.external import pexpect
41 from IPython.external import pexpect
41 except ImportError:
42 except ImportError:
42 pexpect = None
43 pexpect = None
43
44
44 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
45 # Code
46 # Code
46 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
47
48
48 # select_random_ports copied from IPython.parallel.util
49 # select_random_ports copied from IPython.parallel.util
49 _random_ports = set()
50 _random_ports = set()
50
51
51 def select_random_ports(n):
52 def select_random_ports(n):
52 """Selects and return n random ports that are available."""
53 """Selects and return n random ports that are available."""
53 ports = []
54 ports = []
54 for i in xrange(n):
55 for i in xrange(n):
55 sock = socket.socket()
56 sock = socket.socket()
56 sock.bind(('', 0))
57 sock.bind(('', 0))
57 while sock.getsockname()[1] in _random_ports:
58 while sock.getsockname()[1] in _random_ports:
58 sock.close()
59 sock.close()
59 sock = socket.socket()
60 sock = socket.socket()
60 sock.bind(('', 0))
61 sock.bind(('', 0))
61 ports.append(sock)
62 ports.append(sock)
62 for i, sock in enumerate(ports):
63 for i, sock in enumerate(ports):
63 port = sock.getsockname()[1]
64 port = sock.getsockname()[1]
64 sock.close()
65 sock.close()
65 ports[i] = port
66 ports[i] = port
66 _random_ports.add(port)
67 _random_ports.add(port)
67 return ports
68 return ports
68
69
69
70
70 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
71 # Check for passwordless login
72 # Check for passwordless login
72 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
73
74
74 def try_passwordless_ssh(server, keyfile, paramiko=None):
75 def try_passwordless_ssh(server, keyfile, paramiko=None):
75 """Attempt to make an ssh connection without a password.
76 """Attempt to make an ssh connection without a password.
76 This is mainly used for requiring password input only once
77 This is mainly used for requiring password input only once
77 when many tunnels may be connected to the same server.
78 when many tunnels may be connected to the same server.
78
79
79 If paramiko is None, the default for the platform is chosen.
80 If paramiko is None, the default for the platform is chosen.
80 """
81 """
81 if paramiko is None:
82 if paramiko is None:
82 paramiko = sys.platform == 'win32'
83 paramiko = sys.platform == 'win32'
83 if not paramiko:
84 if not paramiko:
84 f = _try_passwordless_openssh
85 f = _try_passwordless_openssh
85 else:
86 else:
86 f = _try_passwordless_paramiko
87 f = _try_passwordless_paramiko
87 return f(server, keyfile)
88 return f(server, keyfile)
88
89
89 def _try_passwordless_openssh(server, keyfile):
90 def _try_passwordless_openssh(server, keyfile):
90 """Try passwordless login with shell ssh command."""
91 """Try passwordless login with shell ssh command."""
91 if pexpect is None:
92 if pexpect is None:
92 raise ImportError("pexpect unavailable, use paramiko")
93 raise ImportError("pexpect unavailable, use paramiko")
93 cmd = 'ssh -f '+ server
94 cmd = 'ssh -f '+ server
94 if keyfile:
95 if keyfile:
95 cmd += ' -i ' + keyfile
96 cmd += ' -i ' + keyfile
96 cmd += ' exit'
97 cmd += ' exit'
97 p = pexpect.spawn(cmd)
98 p = pexpect.spawn(cmd)
98 while True:
99 while True:
99 try:
100 try:
100 p.expect('[Pp]assword:', timeout=.1)
101 p.expect('[Pp]assword:', timeout=.1)
101 except pexpect.TIMEOUT:
102 except pexpect.TIMEOUT:
102 continue
103 continue
103 except pexpect.EOF:
104 except pexpect.EOF:
104 return True
105 return True
105 else:
106 else:
106 return False
107 return False
107
108
108 def _try_passwordless_paramiko(server, keyfile):
109 def _try_passwordless_paramiko(server, keyfile):
109 """Try passwordless login with paramiko."""
110 """Try passwordless login with paramiko."""
110 if paramiko is None:
111 if paramiko is None:
111 msg = "Paramiko unavaliable, "
112 msg = "Paramiko unavaliable, "
112 if sys.platform == 'win32':
113 if sys.platform == 'win32':
113 msg += "Paramiko is required for ssh tunneled connections on Windows."
114 msg += "Paramiko is required for ssh tunneled connections on Windows."
114 else:
115 else:
115 msg += "use OpenSSH."
116 msg += "use OpenSSH."
116 raise ImportError(msg)
117 raise ImportError(msg)
117 username, server, port = _split_server(server)
118 username, server, port = _split_server(server)
118 client = paramiko.SSHClient()
119 client = paramiko.SSHClient()
119 client.load_system_host_keys()
120 client.load_system_host_keys()
120 client.set_missing_host_key_policy(paramiko.WarningPolicy())
121 client.set_missing_host_key_policy(paramiko.WarningPolicy())
121 try:
122 try:
122 client.connect(server, port, username=username, key_filename=keyfile,
123 client.connect(server, port, username=username, key_filename=keyfile,
123 look_for_keys=True)
124 look_for_keys=True)
124 except paramiko.AuthenticationException:
125 except paramiko.AuthenticationException:
125 return False
126 return False
126 else:
127 else:
127 client.close()
128 client.close()
128 return True
129 return True
129
130
130
131
131 def tunnel_connection(socket, addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
132 def tunnel_connection(socket, addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
132 """Connect a socket to an address via an ssh tunnel.
133 """Connect a socket to an address via an ssh tunnel.
133
134
134 This is a wrapper for socket.connect(addr), when addr is not accessible
135 This is a wrapper for socket.connect(addr), when addr is not accessible
135 from the local machine. It simply creates an ssh tunnel using the remaining args,
136 from the local machine. It simply creates an ssh tunnel using the remaining args,
136 and calls socket.connect('tcp://localhost:lport') where lport is the randomly
137 and calls socket.connect('tcp://localhost:lport') where lport is the randomly
137 selected local port of the tunnel.
138 selected local port of the tunnel.
138
139
139 """
140 """
140 new_url, tunnel = open_tunnel(addr, server, keyfile=keyfile, password=password, paramiko=paramiko, timeout=timeout)
141 new_url, tunnel = open_tunnel(addr, server, keyfile=keyfile, password=password, paramiko=paramiko, timeout=timeout)
141 socket.connect(new_url)
142 socket.connect(new_url)
142 return tunnel
143 return tunnel
143
144
144
145
145 def open_tunnel(addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
146 def open_tunnel(addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
146 """Open a tunneled connection from a 0MQ url.
147 """Open a tunneled connection from a 0MQ url.
147
148
148 For use inside tunnel_connection.
149 For use inside tunnel_connection.
149
150
150 Returns
151 Returns
151 -------
152 -------
152
153
153 (url, tunnel): The 0MQ url that has been forwarded, and the tunnel object
154 (url, tunnel): The 0MQ url that has been forwarded, and the tunnel object
154 """
155 """
155
156
156 lport = select_random_ports(1)[0]
157 lport = select_random_ports(1)[0]
157 transport, addr = addr.split('://')
158 transport, addr = addr.split('://')
158 ip,rport = addr.split(':')
159 ip,rport = addr.split(':')
159 rport = int(rport)
160 rport = int(rport)
160 if paramiko is None:
161 if paramiko is None:
161 paramiko = sys.platform == 'win32'
162 paramiko = sys.platform == 'win32'
162 if paramiko:
163 if paramiko:
163 tunnelf = paramiko_tunnel
164 tunnelf = paramiko_tunnel
164 else:
165 else:
165 tunnelf = openssh_tunnel
166 tunnelf = openssh_tunnel
166
167
167 tunnel = tunnelf(lport, rport, server, remoteip=ip, keyfile=keyfile, password=password, timeout=timeout)
168 tunnel = tunnelf(lport, rport, server, remoteip=ip, keyfile=keyfile, password=password, timeout=timeout)
168 return 'tcp://127.0.0.1:%i'%lport, tunnel
169 return 'tcp://127.0.0.1:%i'%lport, tunnel
169
170
170 def openssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60):
171 def openssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60):
171 """Create an ssh tunnel using command-line ssh that connects port lport
172 """Create an ssh tunnel using command-line ssh that connects port lport
172 on this machine to localhost:rport on server. The tunnel
173 on this machine to localhost:rport on server. The tunnel
173 will automatically close when not in use, remaining open
174 will automatically close when not in use, remaining open
174 for a minimum of timeout seconds for an initial connection.
175 for a minimum of timeout seconds for an initial connection.
175
176
176 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
177 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
177 as seen from `server`.
178 as seen from `server`.
178
179
179 keyfile and password may be specified, but ssh config is checked for defaults.
180 keyfile and password may be specified, but ssh config is checked for defaults.
180
181
181 Parameters
182 Parameters
182 ----------
183 ----------
183
184
184 lport : int
185 lport : int
185 local port for connecting to the tunnel from this machine.
186 local port for connecting to the tunnel from this machine.
186 rport : int
187 rport : int
187 port on the remote machine to connect to.
188 port on the remote machine to connect to.
188 server : str
189 server : str
189 The ssh server to connect to. The full ssh server string will be parsed.
190 The ssh server to connect to. The full ssh server string will be parsed.
190 user@server:port
191 user@server:port
191 remoteip : str [Default: 127.0.0.1]
192 remoteip : str [Default: 127.0.0.1]
192 The remote ip, specifying the destination of the tunnel.
193 The remote ip, specifying the destination of the tunnel.
193 Default is localhost, which means that the tunnel would redirect
194 Default is localhost, which means that the tunnel would redirect
194 localhost:lport on this machine to localhost:rport on the *server*.
195 localhost:lport on this machine to localhost:rport on the *server*.
195
196
196 keyfile : str; path to public key file
197 keyfile : str; path to public key file
197 This specifies a key to be used in ssh login, default None.
198 This specifies a key to be used in ssh login, default None.
198 Regular default ssh keys will be used without specifying this argument.
199 Regular default ssh keys will be used without specifying this argument.
199 password : str;
200 password : str;
200 Your ssh password to the ssh server. Note that if this is left None,
201 Your ssh password to the ssh server. Note that if this is left None,
201 you will be prompted for it if passwordless key based login is unavailable.
202 you will be prompted for it if passwordless key based login is unavailable.
202 timeout : int [default: 60]
203 timeout : int [default: 60]
203 The time (in seconds) after which no activity will result in the tunnel
204 The time (in seconds) after which no activity will result in the tunnel
204 closing. This prevents orphaned tunnels from running forever.
205 closing. This prevents orphaned tunnels from running forever.
205 """
206 """
206 if pexpect is None:
207 if pexpect is None:
207 raise ImportError("pexpect unavailable, use paramiko_tunnel")
208 raise ImportError("pexpect unavailable, use paramiko_tunnel")
208 ssh="ssh "
209 ssh="ssh "
209 if keyfile:
210 if keyfile:
210 ssh += "-i " + keyfile
211 ssh += "-i " + keyfile
211
212
212 if ':' in server:
213 if ':' in server:
213 server, port = server.split(':')
214 server, port = server.split(':')
214 ssh += " -p %s" % port
215 ssh += " -p %s" % port
215
216
216 cmd = "%s -f -L 127.0.0.1:%i:%s:%i %s sleep %i" % (
217 cmd = "%s -f -L 127.0.0.1:%i:%s:%i %s sleep %i" % (
217 ssh, lport, remoteip, rport, server, timeout)
218 ssh, lport, remoteip, rport, server, timeout)
218 tunnel = pexpect.spawn(cmd)
219 tunnel = pexpect.spawn(cmd)
219 failed = False
220 failed = False
220 while True:
221 while True:
221 try:
222 try:
222 tunnel.expect('[Pp]assword:', timeout=.1)
223 tunnel.expect('[Pp]assword:', timeout=.1)
223 except pexpect.TIMEOUT:
224 except pexpect.TIMEOUT:
224 continue
225 continue
225 except pexpect.EOF:
226 except pexpect.EOF:
226 if tunnel.exitstatus:
227 if tunnel.exitstatus:
227 print (tunnel.exitstatus)
228 print (tunnel.exitstatus)
228 print (tunnel.before)
229 print (tunnel.before)
229 print (tunnel.after)
230 print (tunnel.after)
230 raise RuntimeError("tunnel '%s' failed to start"%(cmd))
231 raise RuntimeError("tunnel '%s' failed to start"%(cmd))
231 else:
232 else:
232 return tunnel.pid
233 return tunnel.pid
233 else:
234 else:
234 if failed:
235 if failed:
235 print("Password rejected, try again")
236 print("Password rejected, try again")
236 password=None
237 password=None
237 if password is None:
238 if password is None:
238 password = getpass("%s's password: "%(server))
239 password = getpass("%s's password: "%(server))
239 tunnel.sendline(password)
240 tunnel.sendline(password)
240 failed = True
241 failed = True
241
242
242 def _split_server(server):
243 def _split_server(server):
243 if '@' in server:
244 if '@' in server:
244 username,server = server.split('@', 1)
245 username,server = server.split('@', 1)
245 else:
246 else:
246 username = getuser()
247 username = getuser()
247 if ':' in server:
248 if ':' in server:
248 server, port = server.split(':')
249 server, port = server.split(':')
249 port = int(port)
250 port = int(port)
250 else:
251 else:
251 port = 22
252 port = 22
252 return username, server, port
253 return username, server, port
253
254
254 def paramiko_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60):
255 def paramiko_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60):
255 """launch a tunner with paramiko in a subprocess. This should only be used
256 """launch a tunner with paramiko in a subprocess. This should only be used
256 when shell ssh is unavailable (e.g. Windows).
257 when shell ssh is unavailable (e.g. Windows).
257
258
258 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
259 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
259 as seen from `server`.
260 as seen from `server`.
260
261
261 If you are familiar with ssh tunnels, this creates the tunnel:
262 If you are familiar with ssh tunnels, this creates the tunnel:
262
263
263 ssh server -L localhost:lport:remoteip:rport
264 ssh server -L localhost:lport:remoteip:rport
264
265
265 keyfile and password may be specified, but ssh config is checked for defaults.
266 keyfile and password may be specified, but ssh config is checked for defaults.
266
267
267
268
268 Parameters
269 Parameters
269 ----------
270 ----------
270
271
271 lport : int
272 lport : int
272 local port for connecting to the tunnel from this machine.
273 local port for connecting to the tunnel from this machine.
273 rport : int
274 rport : int
274 port on the remote machine to connect to.
275 port on the remote machine to connect to.
275 server : str
276 server : str
276 The ssh server to connect to. The full ssh server string will be parsed.
277 The ssh server to connect to. The full ssh server string will be parsed.
277 user@server:port
278 user@server:port
278 remoteip : str [Default: 127.0.0.1]
279 remoteip : str [Default: 127.0.0.1]
279 The remote ip, specifying the destination of the tunnel.
280 The remote ip, specifying the destination of the tunnel.
280 Default is localhost, which means that the tunnel would redirect
281 Default is localhost, which means that the tunnel would redirect
281 localhost:lport on this machine to localhost:rport on the *server*.
282 localhost:lport on this machine to localhost:rport on the *server*.
282
283
283 keyfile : str; path to public key file
284 keyfile : str; path to public key file
284 This specifies a key to be used in ssh login, default None.
285 This specifies a key to be used in ssh login, default None.
285 Regular default ssh keys will be used without specifying this argument.
286 Regular default ssh keys will be used without specifying this argument.
286 password : str;
287 password : str;
287 Your ssh password to the ssh server. Note that if this is left None,
288 Your ssh password to the ssh server. Note that if this is left None,
288 you will be prompted for it if passwordless key based login is unavailable.
289 you will be prompted for it if passwordless key based login is unavailable.
289 timeout : int [default: 60]
290 timeout : int [default: 60]
290 The time (in seconds) after which no activity will result in the tunnel
291 The time (in seconds) after which no activity will result in the tunnel
291 closing. This prevents orphaned tunnels from running forever.
292 closing. This prevents orphaned tunnels from running forever.
292
293
293 """
294 """
294 if paramiko is None:
295 if paramiko is None:
295 raise ImportError("Paramiko not available")
296 raise ImportError("Paramiko not available")
296
297
297 if password is None:
298 if password is None:
298 if not _try_passwordless_paramiko(server, keyfile):
299 if not _try_passwordless_paramiko(server, keyfile):
299 password = getpass("%s's password: "%(server))
300 password = getpass("%s's password: "%(server))
300
301
301 p = Process(target=_paramiko_tunnel,
302 p = Process(target=_paramiko_tunnel,
302 args=(lport, rport, server, remoteip),
303 args=(lport, rport, server, remoteip),
303 kwargs=dict(keyfile=keyfile, password=password))
304 kwargs=dict(keyfile=keyfile, password=password))
304 p.daemon=False
305 p.daemon=False
305 p.start()
306 p.start()
306 atexit.register(_shutdown_process, p)
307 atexit.register(_shutdown_process, p)
307 return p
308 return p
308
309
309 def _shutdown_process(p):
310 def _shutdown_process(p):
310 if p.is_alive():
311 if p.is_alive():
311 p.terminate()
312 p.terminate()
312
313
313 def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None):
314 def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None):
314 """Function for actually starting a paramiko tunnel, to be passed
315 """Function for actually starting a paramiko tunnel, to be passed
315 to multiprocessing.Process(target=this), and not called directly.
316 to multiprocessing.Process(target=this), and not called directly.
316 """
317 """
317 username, server, port = _split_server(server)
318 username, server, port = _split_server(server)
318 client = paramiko.SSHClient()
319 client = paramiko.SSHClient()
319 client.load_system_host_keys()
320 client.load_system_host_keys()
320 client.set_missing_host_key_policy(paramiko.WarningPolicy())
321 client.set_missing_host_key_policy(paramiko.WarningPolicy())
321
322
322 try:
323 try:
323 client.connect(server, port, username=username, key_filename=keyfile,
324 client.connect(server, port, username=username, key_filename=keyfile,
324 look_for_keys=True, password=password)
325 look_for_keys=True, password=password)
325 # except paramiko.AuthenticationException:
326 # except paramiko.AuthenticationException:
326 # if password is None:
327 # if password is None:
327 # password = getpass("%s@%s's password: "%(username, server))
328 # password = getpass("%s@%s's password: "%(username, server))
328 # client.connect(server, port, username=username, password=password)
329 # client.connect(server, port, username=username, password=password)
329 # else:
330 # else:
330 # raise
331 # raise
331 except Exception as e:
332 except Exception as e:
332 print ('*** Failed to connect to %s:%d: %r' % (server, port, e))
333 print ('*** Failed to connect to %s:%d: %r' % (server, port, e))
333 sys.exit(1)
334 sys.exit(1)
334
335
335 # print ('Now forwarding port %d to %s:%d ...' % (lport, server, rport))
336 # Don't let SIGINT kill the tunnel subprocess
336
337 signal.signal(signal.SIGINT, signal.SIG_IGN)
338
337 try:
339 try:
338 forward_tunnel(lport, remoteip, rport, client.get_transport())
340 forward_tunnel(lport, remoteip, rport, client.get_transport())
339 except KeyboardInterrupt:
341 except KeyboardInterrupt:
340 print ('SIGINT: Port forwarding stopped cleanly')
342 print ('SIGINT: Port forwarding stopped cleanly')
341 sys.exit(0)
343 sys.exit(0)
342 except Exception as e:
344 except Exception as e:
343 print ("Port forwarding stopped uncleanly: %s"%e)
345 print ("Port forwarding stopped uncleanly: %s"%e)
344 sys.exit(255)
346 sys.exit(255)
345
347
346 if sys.platform == 'win32':
348 if sys.platform == 'win32':
347 ssh_tunnel = paramiko_tunnel
349 ssh_tunnel = paramiko_tunnel
348 else:
350 else:
349 ssh_tunnel = openssh_tunnel
351 ssh_tunnel = openssh_tunnel
350
352
351
353
352 __all__ = ['tunnel_connection', 'ssh_tunnel', 'openssh_tunnel', 'paramiko_tunnel', 'try_passwordless_ssh']
354 __all__ = ['tunnel_connection', 'ssh_tunnel', 'openssh_tunnel', 'paramiko_tunnel', 'try_passwordless_ssh']
353
355
354
356
@@ -1,619 +1,622 b''
1 # coding: utf-8
1 # coding: utf-8
2 """A tornado based IPython notebook server.
2 """A tornado based IPython notebook server.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # stdlib
19 # stdlib
20 import errno
20 import errno
21 import logging
21 import logging
22 import os
22 import os
23 import random
23 import random
24 import re
24 import re
25 import select
25 import select
26 import signal
26 import signal
27 import socket
27 import socket
28 import sys
28 import sys
29 import threading
29 import threading
30 import time
30 import time
31 import uuid
31 import uuid
32 import webbrowser
32 import webbrowser
33
33
34 # Third party
34 # Third party
35 import zmq
35 import zmq
36
36
37 # Install the pyzmq ioloop. This has to be done before anything else from
37 # Install the pyzmq ioloop. This has to be done before anything else from
38 # tornado is imported.
38 # tornado is imported.
39 from zmq.eventloop import ioloop
39 from zmq.eventloop import ioloop
40 ioloop.install()
40 ioloop.install()
41
41
42 from tornado import httpserver
42 from tornado import httpserver
43 from tornado import web
43 from tornado import web
44
44
45 # Our own libraries
45 # Our own libraries
46 from .kernelmanager import MappingKernelManager
46 from .kernelmanager import MappingKernelManager
47 from .handlers import (LoginHandler, LogoutHandler,
47 from .handlers import (LoginHandler, LogoutHandler,
48 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
48 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
49 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
49 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
50 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
50 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
51 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
51 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
52 MainClusterHandler, ClusterProfileHandler, ClusterActionHandler,
52 MainClusterHandler, ClusterProfileHandler, ClusterActionHandler,
53 FileFindHandler,
53 FileFindHandler,
54 )
54 )
55 from .nbmanager import NotebookManager
55 from .nbmanager import NotebookManager
56 from .filenbmanager import FileNotebookManager
56 from .filenbmanager import FileNotebookManager
57 from .clustermanager import ClusterManager
57 from .clustermanager import ClusterManager
58
58
59 from IPython.config.application import catch_config_error, boolean_flag
59 from IPython.config.application import catch_config_error, boolean_flag
60 from IPython.core.application import BaseIPythonApplication
60 from IPython.core.application import BaseIPythonApplication
61 from IPython.core.profiledir import ProfileDir
61 from IPython.core.profiledir import ProfileDir
62 from IPython.frontend.consoleapp import IPythonConsoleApp
62 from IPython.frontend.consoleapp import IPythonConsoleApp
63 from IPython.lib.kernel import swallow_argv
63 from IPython.lib.kernel import swallow_argv
64 from IPython.zmq.session import Session, default_secure
64 from IPython.zmq.session import Session, default_secure
65 from IPython.zmq.zmqshell import ZMQInteractiveShell
65 from IPython.zmq.zmqshell import ZMQInteractiveShell
66 from IPython.zmq.ipkernel import (
66 from IPython.zmq.ipkernel import (
67 flags as ipkernel_flags,
67 flags as ipkernel_flags,
68 aliases as ipkernel_aliases,
68 aliases as ipkernel_aliases,
69 IPKernelApp
69 IPKernelApp
70 )
70 )
71 from IPython.utils.importstring import import_item
71 from IPython.utils.importstring import import_item
72 from IPython.utils.traitlets import (
72 from IPython.utils.traitlets import (
73 Dict, Unicode, Integer, List, Enum, Bool,
73 Dict, Unicode, Integer, List, Enum, Bool,
74 DottedObjectName
74 DottedObjectName
75 )
75 )
76 from IPython.utils import py3compat
76 from IPython.utils import py3compat
77 from IPython.utils.path import filefind
77 from IPython.utils.path import filefind
78
78
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80 # Module globals
80 # Module globals
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82
82
83 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
83 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
84 _kernel_action_regex = r"(?P<action>restart|interrupt)"
84 _kernel_action_regex = r"(?P<action>restart|interrupt)"
85 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
85 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
86 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
86 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
87 _cluster_action_regex = r"(?P<action>start|stop)"
87 _cluster_action_regex = r"(?P<action>start|stop)"
88
88
89
89
90 LOCALHOST = '127.0.0.1'
90 LOCALHOST = '127.0.0.1'
91
91
92 _examples = """
92 _examples = """
93 ipython notebook # start the notebook
93 ipython notebook # start the notebook
94 ipython notebook --profile=sympy # use the sympy profile
94 ipython notebook --profile=sympy # use the sympy profile
95 ipython notebook --pylab=inline # pylab in inline plotting mode
95 ipython notebook --pylab=inline # pylab in inline plotting mode
96 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
96 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
97 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
97 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
98 """
98 """
99
99
100 #-----------------------------------------------------------------------------
100 #-----------------------------------------------------------------------------
101 # Helper functions
101 # Helper functions
102 #-----------------------------------------------------------------------------
102 #-----------------------------------------------------------------------------
103
103
104 def url_path_join(a,b):
104 def url_path_join(a,b):
105 if a.endswith('/') and b.startswith('/'):
105 if a.endswith('/') and b.startswith('/'):
106 return a[:-1]+b
106 return a[:-1]+b
107 else:
107 else:
108 return a+b
108 return a+b
109
109
110 def random_ports(port, n):
110 def random_ports(port, n):
111 """Generate a list of n random ports near the given port.
111 """Generate a list of n random ports near the given port.
112
112
113 The first 5 ports will be sequential, and the remaining n-5 will be
113 The first 5 ports will be sequential, and the remaining n-5 will be
114 randomly selected in the range [port-2*n, port+2*n].
114 randomly selected in the range [port-2*n, port+2*n].
115 """
115 """
116 for i in range(min(5, n)):
116 for i in range(min(5, n)):
117 yield port + i
117 yield port + i
118 for i in range(n-5):
118 for i in range(n-5):
119 yield port + random.randint(-2*n, 2*n)
119 yield port + random.randint(-2*n, 2*n)
120
120
121 #-----------------------------------------------------------------------------
121 #-----------------------------------------------------------------------------
122 # The Tornado web application
122 # The Tornado web application
123 #-----------------------------------------------------------------------------
123 #-----------------------------------------------------------------------------
124
124
125 class NotebookWebApplication(web.Application):
125 class NotebookWebApplication(web.Application):
126
126
127 def __init__(self, ipython_app, kernel_manager, notebook_manager,
127 def __init__(self, ipython_app, kernel_manager, notebook_manager,
128 cluster_manager, log,
128 cluster_manager, log,
129 base_project_url, settings_overrides):
129 base_project_url, settings_overrides):
130 handlers = [
130 handlers = [
131 (r"/", ProjectDashboardHandler),
131 (r"/", ProjectDashboardHandler),
132 (r"/login", LoginHandler),
132 (r"/login", LoginHandler),
133 (r"/logout", LogoutHandler),
133 (r"/logout", LogoutHandler),
134 (r"/new", NewHandler),
134 (r"/new", NewHandler),
135 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
135 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
136 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
136 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
137 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
137 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
138 (r"/kernels", MainKernelHandler),
138 (r"/kernels", MainKernelHandler),
139 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
139 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
140 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
140 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
141 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
141 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
142 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
142 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
143 (r"/notebooks", NotebookRootHandler),
143 (r"/notebooks", NotebookRootHandler),
144 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
144 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
145 (r"/rstservice/render", RSTHandler),
145 (r"/rstservice/render", RSTHandler),
146 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
146 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
147 (r"/clusters", MainClusterHandler),
147 (r"/clusters", MainClusterHandler),
148 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
148 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
149 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
149 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
150 ]
150 ]
151
151
152 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
152 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
153 # base_project_url will always be unicode, which will in turn
153 # base_project_url will always be unicode, which will in turn
154 # make the patterns unicode, and ultimately result in unicode
154 # make the patterns unicode, and ultimately result in unicode
155 # keys in kwargs to handler._execute(**kwargs) in tornado.
155 # keys in kwargs to handler._execute(**kwargs) in tornado.
156 # This enforces that base_project_url be ascii in that situation.
156 # This enforces that base_project_url be ascii in that situation.
157 #
157 #
158 # Note that the URLs these patterns check against are escaped,
158 # Note that the URLs these patterns check against are escaped,
159 # and thus guaranteed to be ASCII: 'héllo' is really 'h%C3%A9llo'.
159 # and thus guaranteed to be ASCII: 'héllo' is really 'h%C3%A9llo'.
160 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
160 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
161
161
162 settings = dict(
162 settings = dict(
163 template_path=os.path.join(os.path.dirname(__file__), "templates"),
163 template_path=os.path.join(os.path.dirname(__file__), "templates"),
164 static_path=ipython_app.static_file_path,
164 static_path=ipython_app.static_file_path,
165 static_handler_class = FileFindHandler,
165 static_handler_class = FileFindHandler,
166 cookie_secret=os.urandom(1024),
166 cookie_secret=os.urandom(1024),
167 login_url="%s/login"%(base_project_url.rstrip('/')),
167 login_url="%s/login"%(base_project_url.rstrip('/')),
168 cookie_name='username-%s' % uuid.uuid4(),
168 cookie_name='username-%s' % uuid.uuid4(),
169 )
169 )
170
170
171 # allow custom overrides for the tornado web app.
171 # allow custom overrides for the tornado web app.
172 settings.update(settings_overrides)
172 settings.update(settings_overrides)
173
173
174 # prepend base_project_url onto the patterns that we match
174 # prepend base_project_url onto the patterns that we match
175 new_handlers = []
175 new_handlers = []
176 for handler in handlers:
176 for handler in handlers:
177 pattern = url_path_join(base_project_url, handler[0])
177 pattern = url_path_join(base_project_url, handler[0])
178 new_handler = tuple([pattern]+list(handler[1:]))
178 new_handler = tuple([pattern]+list(handler[1:]))
179 new_handlers.append( new_handler )
179 new_handlers.append( new_handler )
180
180
181 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
181 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
182
182
183 self.kernel_manager = kernel_manager
183 self.kernel_manager = kernel_manager
184 self.notebook_manager = notebook_manager
184 self.notebook_manager = notebook_manager
185 self.cluster_manager = cluster_manager
185 self.cluster_manager = cluster_manager
186 self.ipython_app = ipython_app
186 self.ipython_app = ipython_app
187 self.read_only = self.ipython_app.read_only
187 self.read_only = self.ipython_app.read_only
188 self.log = log
188 self.log = log
189
189
190
190
191 #-----------------------------------------------------------------------------
191 #-----------------------------------------------------------------------------
192 # Aliases and Flags
192 # Aliases and Flags
193 #-----------------------------------------------------------------------------
193 #-----------------------------------------------------------------------------
194
194
195 flags = dict(ipkernel_flags)
195 flags = dict(ipkernel_flags)
196 flags['no-browser']=(
196 flags['no-browser']=(
197 {'NotebookApp' : {'open_browser' : False}},
197 {'NotebookApp' : {'open_browser' : False}},
198 "Don't open the notebook in a browser after startup."
198 "Don't open the notebook in a browser after startup."
199 )
199 )
200 flags['no-mathjax']=(
200 flags['no-mathjax']=(
201 {'NotebookApp' : {'enable_mathjax' : False}},
201 {'NotebookApp' : {'enable_mathjax' : False}},
202 """Disable MathJax
202 """Disable MathJax
203
203
204 MathJax is the javascript library IPython uses to render math/LaTeX. It is
204 MathJax is the javascript library IPython uses to render math/LaTeX. It is
205 very large, so you may want to disable it if you have a slow internet
205 very large, so you may want to disable it if you have a slow internet
206 connection, or for offline use of the notebook.
206 connection, or for offline use of the notebook.
207
207
208 When disabled, equations etc. will appear as their untransformed TeX source.
208 When disabled, equations etc. will appear as their untransformed TeX source.
209 """
209 """
210 )
210 )
211 flags['read-only'] = (
211 flags['read-only'] = (
212 {'NotebookApp' : {'read_only' : True}},
212 {'NotebookApp' : {'read_only' : True}},
213 """Allow read-only access to notebooks.
213 """Allow read-only access to notebooks.
214
214
215 When using a password to protect the notebook server, this flag
215 When using a password to protect the notebook server, this flag
216 allows unauthenticated clients to view the notebook list, and
216 allows unauthenticated clients to view the notebook list, and
217 individual notebooks, but not edit them, start kernels, or run
217 individual notebooks, but not edit them, start kernels, or run
218 code.
218 code.
219
219
220 If no password is set, the server will be entirely read-only.
220 If no password is set, the server will be entirely read-only.
221 """
221 """
222 )
222 )
223
223
224 # Add notebook manager flags
224 # Add notebook manager flags
225 flags.update(boolean_flag('script', 'FileNotebookManager.save_script',
225 flags.update(boolean_flag('script', 'FileNotebookManager.save_script',
226 'Auto-save a .py script everytime the .ipynb notebook is saved',
226 'Auto-save a .py script everytime the .ipynb notebook is saved',
227 'Do not auto-save .py scripts for every notebook'))
227 'Do not auto-save .py scripts for every notebook'))
228
228
229 # the flags that are specific to the frontend
229 # the flags that are specific to the frontend
230 # these must be scrubbed before being passed to the kernel,
230 # these must be scrubbed before being passed to the kernel,
231 # or it will raise an error on unrecognized flags
231 # or it will raise an error on unrecognized flags
232 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
232 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
233
233
234 aliases = dict(ipkernel_aliases)
234 aliases = dict(ipkernel_aliases)
235
235
236 aliases.update({
236 aliases.update({
237 'ip': 'NotebookApp.ip',
237 'ip': 'NotebookApp.ip',
238 'port': 'NotebookApp.port',
238 'port': 'NotebookApp.port',
239 'port-retries': 'NotebookApp.port_retries',
239 'port-retries': 'NotebookApp.port_retries',
240 'keyfile': 'NotebookApp.keyfile',
240 'keyfile': 'NotebookApp.keyfile',
241 'certfile': 'NotebookApp.certfile',
241 'certfile': 'NotebookApp.certfile',
242 'notebook-dir': 'NotebookManager.notebook_dir',
242 'notebook-dir': 'NotebookManager.notebook_dir',
243 'browser': 'NotebookApp.browser',
243 'browser': 'NotebookApp.browser',
244 })
244 })
245
245
246 # remove ipkernel flags that are singletons, and don't make sense in
246 # remove ipkernel flags that are singletons, and don't make sense in
247 # multi-kernel evironment:
247 # multi-kernel evironment:
248 aliases.pop('f', None)
248 aliases.pop('f', None)
249
249
250 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
250 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
251 u'notebook-dir']
251 u'notebook-dir']
252
252
253 #-----------------------------------------------------------------------------
253 #-----------------------------------------------------------------------------
254 # NotebookApp
254 # NotebookApp
255 #-----------------------------------------------------------------------------
255 #-----------------------------------------------------------------------------
256
256
257 class NotebookApp(BaseIPythonApplication):
257 class NotebookApp(BaseIPythonApplication):
258
258
259 name = 'ipython-notebook'
259 name = 'ipython-notebook'
260 default_config_file_name='ipython_notebook_config.py'
260 default_config_file_name='ipython_notebook_config.py'
261
261
262 description = """
262 description = """
263 The IPython HTML Notebook.
263 The IPython HTML Notebook.
264
264
265 This launches a Tornado based HTML Notebook Server that serves up an
265 This launches a Tornado based HTML Notebook Server that serves up an
266 HTML5/Javascript Notebook client.
266 HTML5/Javascript Notebook client.
267 """
267 """
268 examples = _examples
268 examples = _examples
269
269
270 classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager,
270 classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager,
271 FileNotebookManager]
271 FileNotebookManager]
272 flags = Dict(flags)
272 flags = Dict(flags)
273 aliases = Dict(aliases)
273 aliases = Dict(aliases)
274
274
275 kernel_argv = List(Unicode)
275 kernel_argv = List(Unicode)
276
276
277 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
277 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
278 default_value=logging.INFO,
278 default_value=logging.INFO,
279 config=True,
279 config=True,
280 help="Set the log level by value or name.")
280 help="Set the log level by value or name.")
281
281
282 # create requested profiles by default, if they don't exist:
282 # create requested profiles by default, if they don't exist:
283 auto_create = Bool(True)
283 auto_create = Bool(True)
284
284
285 # file to be opened in the notebook server
285 # file to be opened in the notebook server
286 file_to_run = Unicode('')
286 file_to_run = Unicode('')
287
287
288 # Network related information.
288 # Network related information.
289
289
290 ip = Unicode(LOCALHOST, config=True,
290 ip = Unicode(LOCALHOST, config=True,
291 help="The IP address the notebook server will listen on."
291 help="The IP address the notebook server will listen on."
292 )
292 )
293
293
294 def _ip_changed(self, name, old, new):
294 def _ip_changed(self, name, old, new):
295 if new == u'*': self.ip = u''
295 if new == u'*': self.ip = u''
296
296
297 port = Integer(8888, config=True,
297 port = Integer(8888, config=True,
298 help="The port the notebook server will listen on."
298 help="The port the notebook server will listen on."
299 )
299 )
300 port_retries = Integer(50, config=True,
300 port_retries = Integer(50, config=True,
301 help="The number of additional ports to try if the specified port is not available."
301 help="The number of additional ports to try if the specified port is not available."
302 )
302 )
303
303
304 certfile = Unicode(u'', config=True,
304 certfile = Unicode(u'', config=True,
305 help="""The full path to an SSL/TLS certificate file."""
305 help="""The full path to an SSL/TLS certificate file."""
306 )
306 )
307
307
308 keyfile = Unicode(u'', config=True,
308 keyfile = Unicode(u'', config=True,
309 help="""The full path to a private key file for usage with SSL/TLS."""
309 help="""The full path to a private key file for usage with SSL/TLS."""
310 )
310 )
311
311
312 password = Unicode(u'', config=True,
312 password = Unicode(u'', config=True,
313 help="""Hashed password to use for web authentication.
313 help="""Hashed password to use for web authentication.
314
314
315 To generate, type in a python/IPython shell:
315 To generate, type in a python/IPython shell:
316
316
317 from IPython.lib import passwd; passwd()
317 from IPython.lib import passwd; passwd()
318
318
319 The string should be of the form type:salt:hashed-password.
319 The string should be of the form type:salt:hashed-password.
320 """
320 """
321 )
321 )
322
322
323 open_browser = Bool(True, config=True,
323 open_browser = Bool(True, config=True,
324 help="""Whether to open in a browser after starting.
324 help="""Whether to open in a browser after starting.
325 The specific browser used is platform dependent and
325 The specific browser used is platform dependent and
326 determined by the python standard library `webbrowser`
326 determined by the python standard library `webbrowser`
327 module, unless it is overridden using the --browser
327 module, unless it is overridden using the --browser
328 (NotebookApp.browser) configuration option.
328 (NotebookApp.browser) configuration option.
329 """)
329 """)
330
330
331 browser = Unicode(u'', config=True,
331 browser = Unicode(u'', config=True,
332 help="""Specify what command to use to invoke a web
332 help="""Specify what command to use to invoke a web
333 browser when opening the notebook. If not specified, the
333 browser when opening the notebook. If not specified, the
334 default browser will be determined by the `webbrowser`
334 default browser will be determined by the `webbrowser`
335 standard library module, which allows setting of the
335 standard library module, which allows setting of the
336 BROWSER environment variable to override it.
336 BROWSER environment variable to override it.
337 """)
337 """)
338
338
339 read_only = Bool(False, config=True,
339 read_only = Bool(False, config=True,
340 help="Whether to prevent editing/execution of notebooks."
340 help="Whether to prevent editing/execution of notebooks."
341 )
341 )
342
342
343 webapp_settings = Dict(config=True,
343 webapp_settings = Dict(config=True,
344 help="Supply overrides for the tornado.web.Application that the "
344 help="Supply overrides for the tornado.web.Application that the "
345 "IPython notebook uses.")
345 "IPython notebook uses.")
346
346
347 enable_mathjax = Bool(True, config=True,
347 enable_mathjax = Bool(True, config=True,
348 help="""Whether to enable MathJax for typesetting math/TeX
348 help="""Whether to enable MathJax for typesetting math/TeX
349
349
350 MathJax is the javascript library IPython uses to render math/LaTeX. It is
350 MathJax is the javascript library IPython uses to render math/LaTeX. It is
351 very large, so you may want to disable it if you have a slow internet
351 very large, so you may want to disable it if you have a slow internet
352 connection, or for offline use of the notebook.
352 connection, or for offline use of the notebook.
353
353
354 When disabled, equations etc. will appear as their untransformed TeX source.
354 When disabled, equations etc. will appear as their untransformed TeX source.
355 """
355 """
356 )
356 )
357 def _enable_mathjax_changed(self, name, old, new):
357 def _enable_mathjax_changed(self, name, old, new):
358 """set mathjax url to empty if mathjax is disabled"""
358 """set mathjax url to empty if mathjax is disabled"""
359 if not new:
359 if not new:
360 self.mathjax_url = u''
360 self.mathjax_url = u''
361
361
362 base_project_url = Unicode('/', config=True,
362 base_project_url = Unicode('/', config=True,
363 help='''The base URL for the notebook server''')
363 help='''The base URL for the notebook server''')
364 base_kernel_url = Unicode('/', config=True,
364 base_kernel_url = Unicode('/', config=True,
365 help='''The base URL for the kernel server''')
365 help='''The base URL for the kernel server''')
366 websocket_host = Unicode("", config=True,
366 websocket_host = Unicode("", config=True,
367 help="""The hostname for the websocket server."""
367 help="""The hostname for the websocket server."""
368 )
368 )
369
369
370 extra_static_paths = List(Unicode, config=True,
370 extra_static_paths = List(Unicode, config=True,
371 help="""Extra paths to search for serving static files.
371 help="""Extra paths to search for serving static files.
372
372
373 This allows adding javascript/css to be available from the notebook server machine,
373 This allows adding javascript/css to be available from the notebook server machine,
374 or overriding individual files in the IPython"""
374 or overriding individual files in the IPython"""
375 )
375 )
376 def _extra_static_paths_default(self):
376 def _extra_static_paths_default(self):
377 return [os.path.join(self.profile_dir.location, 'static')]
377 return [os.path.join(self.profile_dir.location, 'static')]
378
378
379 @property
379 @property
380 def static_file_path(self):
380 def static_file_path(self):
381 """return extra paths + the default location"""
381 """return extra paths + the default location"""
382 return self.extra_static_paths + [os.path.join(os.path.dirname(__file__), "static")]
382 return self.extra_static_paths + [os.path.join(os.path.dirname(__file__), "static")]
383
383
384 mathjax_url = Unicode("", config=True,
384 mathjax_url = Unicode("", config=True,
385 help="""The url for MathJax.js."""
385 help="""The url for MathJax.js."""
386 )
386 )
387 def _mathjax_url_default(self):
387 def _mathjax_url_default(self):
388 if not self.enable_mathjax:
388 if not self.enable_mathjax:
389 return u''
389 return u''
390 static_url_prefix = self.webapp_settings.get("static_url_prefix",
390 static_url_prefix = self.webapp_settings.get("static_url_prefix",
391 "/static/")
391 "/static/")
392 try:
392 try:
393 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), self.static_file_path)
393 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), self.static_file_path)
394 except IOError:
394 except IOError:
395 if self.certfile:
395 if self.certfile:
396 # HTTPS: load from Rackspace CDN, because SSL certificate requires it
396 # HTTPS: load from Rackspace CDN, because SSL certificate requires it
397 base = u"https://c328740.ssl.cf1.rackcdn.com"
397 base = u"https://c328740.ssl.cf1.rackcdn.com"
398 else:
398 else:
399 base = u"http://cdn.mathjax.org"
399 base = u"http://cdn.mathjax.org"
400
400
401 url = base + u"/mathjax/latest/MathJax.js"
401 url = base + u"/mathjax/latest/MathJax.js"
402 self.log.info("Using MathJax from CDN: %s", url)
402 self.log.info("Using MathJax from CDN: %s", url)
403 return url
403 return url
404 else:
404 else:
405 self.log.info("Using local MathJax from %s" % mathjax)
405 self.log.info("Using local MathJax from %s" % mathjax)
406 return static_url_prefix+u"mathjax/MathJax.js"
406 return static_url_prefix+u"mathjax/MathJax.js"
407
407
408 def _mathjax_url_changed(self, name, old, new):
408 def _mathjax_url_changed(self, name, old, new):
409 if new and not self.enable_mathjax:
409 if new and not self.enable_mathjax:
410 # enable_mathjax=False overrides mathjax_url
410 # enable_mathjax=False overrides mathjax_url
411 self.mathjax_url = u''
411 self.mathjax_url = u''
412 else:
412 else:
413 self.log.info("Using MathJax: %s", new)
413 self.log.info("Using MathJax: %s", new)
414
414
415 notebook_manager_class = DottedObjectName('IPython.frontend.html.notebook.filenbmanager.FileNotebookManager',
415 notebook_manager_class = DottedObjectName('IPython.frontend.html.notebook.filenbmanager.FileNotebookManager',
416 config=True,
416 config=True,
417 help='The notebook manager class to use.')
417 help='The notebook manager class to use.')
418
418
419 def parse_command_line(self, argv=None):
419 def parse_command_line(self, argv=None):
420 super(NotebookApp, self).parse_command_line(argv)
420 super(NotebookApp, self).parse_command_line(argv)
421 if argv is None:
421 if argv is None:
422 argv = sys.argv[1:]
422 argv = sys.argv[1:]
423
423
424 # Scrub frontend-specific flags
424 # Scrub frontend-specific flags
425 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
425 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
426 # Kernel should inherit default config file from frontend
426 # Kernel should inherit default config file from frontend
427 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
427 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
428
428
429 if self.extra_args:
429 if self.extra_args:
430 f = os.path.abspath(self.extra_args[0])
430 f = os.path.abspath(self.extra_args[0])
431 if os.path.isdir(f):
431 if os.path.isdir(f):
432 nbdir = f
432 nbdir = f
433 else:
433 else:
434 self.file_to_run = f
434 self.file_to_run = f
435 nbdir = os.path.dirname(f)
435 nbdir = os.path.dirname(f)
436 self.config.NotebookManager.notebook_dir = nbdir
436 self.config.NotebookManager.notebook_dir = nbdir
437
437
438 def init_configurables(self):
438 def init_configurables(self):
439 # force Session default to be secure
439 # force Session default to be secure
440 default_secure(self.config)
440 default_secure(self.config)
441 self.kernel_manager = MappingKernelManager(
441 self.kernel_manager = MappingKernelManager(
442 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
442 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
443 connection_dir = self.profile_dir.security_dir,
443 connection_dir = self.profile_dir.security_dir,
444 )
444 )
445 kls = import_item(self.notebook_manager_class)
445 kls = import_item(self.notebook_manager_class)
446 self.notebook_manager = kls(config=self.config, log=self.log)
446 self.notebook_manager = kls(config=self.config, log=self.log)
447 self.notebook_manager.log_info()
447 self.notebook_manager.log_info()
448 self.notebook_manager.load_notebook_names()
448 self.notebook_manager.load_notebook_names()
449 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
449 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
450 self.cluster_manager.update_profiles()
450 self.cluster_manager.update_profiles()
451
451
452 def init_logging(self):
452 def init_logging(self):
453 # This prevents double log messages because tornado use a root logger that
453 # This prevents double log messages because tornado use a root logger that
454 # self.log is a child of. The logging module dipatches log messages to a log
454 # self.log is a child of. The logging module dipatches log messages to a log
455 # and all of its ancenstors until propagate is set to False.
455 # and all of its ancenstors until propagate is set to False.
456 self.log.propagate = False
456 self.log.propagate = False
457
457
458 def init_webapp(self):
458 def init_webapp(self):
459 """initialize tornado webapp and httpserver"""
459 """initialize tornado webapp and httpserver"""
460 self.web_app = NotebookWebApplication(
460 self.web_app = NotebookWebApplication(
461 self, self.kernel_manager, self.notebook_manager,
461 self, self.kernel_manager, self.notebook_manager,
462 self.cluster_manager, self.log,
462 self.cluster_manager, self.log,
463 self.base_project_url, self.webapp_settings
463 self.base_project_url, self.webapp_settings
464 )
464 )
465 if self.certfile:
465 if self.certfile:
466 ssl_options = dict(certfile=self.certfile)
466 ssl_options = dict(certfile=self.certfile)
467 if self.keyfile:
467 if self.keyfile:
468 ssl_options['keyfile'] = self.keyfile
468 ssl_options['keyfile'] = self.keyfile
469 else:
469 else:
470 ssl_options = None
470 ssl_options = None
471 self.web_app.password = self.password
471 self.web_app.password = self.password
472 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
472 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
473 if ssl_options is None and not self.ip and not (self.read_only and not self.password):
473 if not self.ip:
474 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
474 warning = "WARNING: The notebook server is listening on all IP addresses"
475 'but not using any encryption or authentication. This is highly '
475 if ssl_options is None:
476 'insecure and not recommended.')
476 self.log.critical(warning + " and not using encryption. This"
477
477 "is not recommended.")
478 if not self.password and not self.read_only:
479 self.log.critical(warning + "and not using authentication."
480 "This is highly insecure and not recommended.")
478 success = None
481 success = None
479 for port in random_ports(self.port, self.port_retries+1):
482 for port in random_ports(self.port, self.port_retries+1):
480 try:
483 try:
481 self.http_server.listen(port, self.ip)
484 self.http_server.listen(port, self.ip)
482 except socket.error as e:
485 except socket.error as e:
483 if e.errno != errno.EADDRINUSE:
486 if e.errno != errno.EADDRINUSE:
484 raise
487 raise
485 self.log.info('The port %i is already in use, trying another random port.' % port)
488 self.log.info('The port %i is already in use, trying another random port.' % port)
486 else:
489 else:
487 self.port = port
490 self.port = port
488 success = True
491 success = True
489 break
492 break
490 if not success:
493 if not success:
491 self.log.critical('ERROR: the notebook server could not be started because '
494 self.log.critical('ERROR: the notebook server could not be started because '
492 'no available port could be found.')
495 'no available port could be found.')
493 self.exit(1)
496 self.exit(1)
494
497
495 def init_signal(self):
498 def init_signal(self):
496 # FIXME: remove this check when pyzmq dependency is >= 2.1.11
499 # FIXME: remove this check when pyzmq dependency is >= 2.1.11
497 # safely extract zmq version info:
500 # safely extract zmq version info:
498 try:
501 try:
499 zmq_v = zmq.pyzmq_version_info()
502 zmq_v = zmq.pyzmq_version_info()
500 except AttributeError:
503 except AttributeError:
501 zmq_v = [ int(n) for n in re.findall(r'\d+', zmq.__version__) ]
504 zmq_v = [ int(n) for n in re.findall(r'\d+', zmq.__version__) ]
502 if 'dev' in zmq.__version__:
505 if 'dev' in zmq.__version__:
503 zmq_v.append(999)
506 zmq_v.append(999)
504 zmq_v = tuple(zmq_v)
507 zmq_v = tuple(zmq_v)
505 if zmq_v >= (2,1,9) and not sys.platform.startswith('win'):
508 if zmq_v >= (2,1,9) and not sys.platform.startswith('win'):
506 # This won't work with 2.1.7 and
509 # This won't work with 2.1.7 and
507 # 2.1.9-10 will log ugly 'Interrupted system call' messages,
510 # 2.1.9-10 will log ugly 'Interrupted system call' messages,
508 # but it will work
511 # but it will work
509 signal.signal(signal.SIGINT, self._handle_sigint)
512 signal.signal(signal.SIGINT, self._handle_sigint)
510 signal.signal(signal.SIGTERM, self._signal_stop)
513 signal.signal(signal.SIGTERM, self._signal_stop)
511
514
512 def _handle_sigint(self, sig, frame):
515 def _handle_sigint(self, sig, frame):
513 """SIGINT handler spawns confirmation dialog"""
516 """SIGINT handler spawns confirmation dialog"""
514 # register more forceful signal handler for ^C^C case
517 # register more forceful signal handler for ^C^C case
515 signal.signal(signal.SIGINT, self._signal_stop)
518 signal.signal(signal.SIGINT, self._signal_stop)
516 # request confirmation dialog in bg thread, to avoid
519 # request confirmation dialog in bg thread, to avoid
517 # blocking the App
520 # blocking the App
518 thread = threading.Thread(target=self._confirm_exit)
521 thread = threading.Thread(target=self._confirm_exit)
519 thread.daemon = True
522 thread.daemon = True
520 thread.start()
523 thread.start()
521
524
522 def _restore_sigint_handler(self):
525 def _restore_sigint_handler(self):
523 """callback for restoring original SIGINT handler"""
526 """callback for restoring original SIGINT handler"""
524 signal.signal(signal.SIGINT, self._handle_sigint)
527 signal.signal(signal.SIGINT, self._handle_sigint)
525
528
526 def _confirm_exit(self):
529 def _confirm_exit(self):
527 """confirm shutdown on ^C
530 """confirm shutdown on ^C
528
531
529 A second ^C, or answering 'y' within 5s will cause shutdown,
532 A second ^C, or answering 'y' within 5s will cause shutdown,
530 otherwise original SIGINT handler will be restored.
533 otherwise original SIGINT handler will be restored.
531
534
532 This doesn't work on Windows.
535 This doesn't work on Windows.
533 """
536 """
534 # FIXME: remove this delay when pyzmq dependency is >= 2.1.11
537 # FIXME: remove this delay when pyzmq dependency is >= 2.1.11
535 time.sleep(0.1)
538 time.sleep(0.1)
536 sys.stdout.write("Shutdown Notebook Server (y/[n])? ")
539 sys.stdout.write("Shutdown Notebook Server (y/[n])? ")
537 sys.stdout.flush()
540 sys.stdout.flush()
538 r,w,x = select.select([sys.stdin], [], [], 5)
541 r,w,x = select.select([sys.stdin], [], [], 5)
539 if r:
542 if r:
540 line = sys.stdin.readline()
543 line = sys.stdin.readline()
541 if line.lower().startswith('y'):
544 if line.lower().startswith('y'):
542 self.log.critical("Shutdown confirmed")
545 self.log.critical("Shutdown confirmed")
543 ioloop.IOLoop.instance().stop()
546 ioloop.IOLoop.instance().stop()
544 return
547 return
545 else:
548 else:
546 print "No answer for 5s:",
549 print "No answer for 5s:",
547 print "resuming operation..."
550 print "resuming operation..."
548 # no answer, or answer is no:
551 # no answer, or answer is no:
549 # set it back to original SIGINT handler
552 # set it back to original SIGINT handler
550 # use IOLoop.add_callback because signal.signal must be called
553 # use IOLoop.add_callback because signal.signal must be called
551 # from main thread
554 # from main thread
552 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
555 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
553
556
554 def _signal_stop(self, sig, frame):
557 def _signal_stop(self, sig, frame):
555 self.log.critical("received signal %s, stopping", sig)
558 self.log.critical("received signal %s, stopping", sig)
556 ioloop.IOLoop.instance().stop()
559 ioloop.IOLoop.instance().stop()
557
560
558 @catch_config_error
561 @catch_config_error
559 def initialize(self, argv=None):
562 def initialize(self, argv=None):
560 self.init_logging()
563 self.init_logging()
561 super(NotebookApp, self).initialize(argv)
564 super(NotebookApp, self).initialize(argv)
562 self.init_configurables()
565 self.init_configurables()
563 self.init_webapp()
566 self.init_webapp()
564 self.init_signal()
567 self.init_signal()
565
568
566 def cleanup_kernels(self):
569 def cleanup_kernels(self):
567 """shutdown all kernels
570 """shutdown all kernels
568
571
569 The kernels will shutdown themselves when this process no longer exists,
572 The kernels will shutdown themselves when this process no longer exists,
570 but explicit shutdown allows the KernelManagers to cleanup the connection files.
573 but explicit shutdown allows the KernelManagers to cleanup the connection files.
571 """
574 """
572 self.log.info('Shutting down kernels')
575 self.log.info('Shutting down kernels')
573 km = self.kernel_manager
576 km = self.kernel_manager
574 # copy list, since shutdown_kernel deletes keys
577 # copy list, since shutdown_kernel deletes keys
575 for kid in list(km.kernel_ids):
578 for kid in list(km.kernel_ids):
576 km.shutdown_kernel(kid)
579 km.shutdown_kernel(kid)
577
580
578 def start(self):
581 def start(self):
579 ip = self.ip if self.ip else '[all ip addresses on your system]'
582 ip = self.ip if self.ip else '[all ip addresses on your system]'
580 proto = 'https' if self.certfile else 'http'
583 proto = 'https' if self.certfile else 'http'
581 info = self.log.info
584 info = self.log.info
582 info("The IPython Notebook is running at: %s://%s:%i%s" %
585 info("The IPython Notebook is running at: %s://%s:%i%s" %
583 (proto, ip, self.port,self.base_project_url) )
586 (proto, ip, self.port,self.base_project_url) )
584 info("Use Control-C to stop this server and shut down all kernels.")
587 info("Use Control-C to stop this server and shut down all kernels.")
585
588
586 if self.open_browser or self.file_to_run:
589 if self.open_browser or self.file_to_run:
587 ip = self.ip or '127.0.0.1'
590 ip = self.ip or '127.0.0.1'
588 try:
591 try:
589 browser = webbrowser.get(self.browser or None)
592 browser = webbrowser.get(self.browser or None)
590 except webbrowser.Error as e:
593 except webbrowser.Error as e:
591 self.log.warn('No web browser found: %s.' % e)
594 self.log.warn('No web browser found: %s.' % e)
592 browser = None
595 browser = None
593
596
594 if self.file_to_run:
597 if self.file_to_run:
595 name, _ = os.path.splitext(os.path.basename(self.file_to_run))
598 name, _ = os.path.splitext(os.path.basename(self.file_to_run))
596 url = self.notebook_manager.rev_mapping.get(name, '')
599 url = self.notebook_manager.rev_mapping.get(name, '')
597 else:
600 else:
598 url = ''
601 url = ''
599 if browser:
602 if browser:
600 b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip,
603 b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip,
601 self.port, self.base_project_url, url), new=2)
604 self.port, self.base_project_url, url), new=2)
602 threading.Thread(target=b).start()
605 threading.Thread(target=b).start()
603 try:
606 try:
604 ioloop.IOLoop.instance().start()
607 ioloop.IOLoop.instance().start()
605 except KeyboardInterrupt:
608 except KeyboardInterrupt:
606 info("Interrupted...")
609 info("Interrupted...")
607 finally:
610 finally:
608 self.cleanup_kernels()
611 self.cleanup_kernels()
609
612
610
613
611 #-----------------------------------------------------------------------------
614 #-----------------------------------------------------------------------------
612 # Main entry point
615 # Main entry point
613 #-----------------------------------------------------------------------------
616 #-----------------------------------------------------------------------------
614
617
615 def launch_new_instance():
618 def launch_new_instance():
616 app = NotebookApp.instance()
619 app = NotebookApp.instance()
617 app.initialize()
620 app.initialize()
618 app.start()
621 app.start()
619
622
@@ -1,220 +1,218 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Cell
9 // Cell
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16
16
17 var Cell = function () {
17 var Cell = function () {
18 this.placeholder = this.placeholder || '';
18 this.placeholder = this.placeholder || '';
19 this.read_only = false;
19 this.read_only = false;
20 this.selected = false;
20 this.selected = false;
21 this.element = null;
21 this.element = null;
22 this.metadata = {};
22 this.metadata = {};
23 // load this from metadata later ?
23 // load this from metadata later ?
24 this.user_highlight == 'auto';
24 this.user_highlight == 'auto';
25 this.create_element();
25 this.create_element();
26 if (this.element !== null) {
26 if (this.element !== null) {
27 this.element.data("cell", this);
27 this.element.data("cell", this);
28 this.bind_events();
28 this.bind_events();
29 }
29 }
30 this.cell_id = utils.uuid();
30 this.cell_id = utils.uuid();
31 };
31 };
32
32
33
33
34 // Subclasses must implement create_element.
34 // Subclasses must implement create_element.
35 Cell.prototype.create_element = function () {};
35 Cell.prototype.create_element = function () {};
36
36
37
37
38 Cell.prototype.bind_events = function () {
38 Cell.prototype.bind_events = function () {
39 var that = this;
39 var that = this;
40 // We trigger events so that Cell doesn't have to depend on Notebook.
40 // We trigger events so that Cell doesn't have to depend on Notebook.
41 that.element.click(function (event) {
41 that.element.click(function (event) {
42 if (that.selected === false) {
42 if (that.selected === false) {
43 $([IPython.events]).trigger('select.Cell', {'cell':that});
43 $([IPython.events]).trigger('select.Cell', {'cell':that});
44 }
44 }
45 });
45 });
46 that.element.focusin(function (event) {
46 that.element.focusin(function (event) {
47 if (that.selected === false) {
47 if (that.selected === false) {
48 $([IPython.events]).trigger('select.Cell', {'cell':that});
48 $([IPython.events]).trigger('select.Cell', {'cell':that});
49 }
49 }
50 });
50 });
51 };
51 };
52
52
53
54 // typeset with MathJax if MathJax is available
55 Cell.prototype.typeset = function () {
53 Cell.prototype.typeset = function () {
56 if (window.MathJax){
54 if (window.MathJax){
57 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
55 var cell_math = this.element.get(0);
56 MathJax.Hub.Queue(["Typeset",MathJax.Hub,cell_math]);
58 }
57 }
59 };
58 };
60
59
61
62 Cell.prototype.select = function () {
60 Cell.prototype.select = function () {
63 this.element.addClass('ui-widget-content ui-corner-all');
61 this.element.addClass('ui-widget-content ui-corner-all');
64 this.selected = true;
62 this.selected = true;
65 };
63 };
66
64
67
65
68 Cell.prototype.unselect = function () {
66 Cell.prototype.unselect = function () {
69 this.element.removeClass('ui-widget-content ui-corner-all');
67 this.element.removeClass('ui-widget-content ui-corner-all');
70 this.selected = false;
68 this.selected = false;
71 };
69 };
72
70
73
71
74 Cell.prototype.get_text = function () {
72 Cell.prototype.get_text = function () {
75 };
73 };
76
74
77
75
78 Cell.prototype.set_text = function (text) {
76 Cell.prototype.set_text = function (text) {
79 };
77 };
80
78
81
79
82 Cell.prototype.refresh = function () {
80 Cell.prototype.refresh = function () {
83 this.code_mirror.refresh();
81 this.code_mirror.refresh();
84 };
82 };
85
83
86
84
87 Cell.prototype.edit = function () {
85 Cell.prototype.edit = function () {
88 };
86 };
89
87
90
88
91 Cell.prototype.render = function () {
89 Cell.prototype.render = function () {
92 };
90 };
93
91
94
92
95 Cell.prototype.toJSON = function () {
93 Cell.prototype.toJSON = function () {
96 var data = {};
94 var data = {};
97 data.metadata = this.metadata;
95 data.metadata = this.metadata;
98 return data;
96 return data;
99 };
97 };
100
98
101
99
102 Cell.prototype.fromJSON = function (data) {
100 Cell.prototype.fromJSON = function (data) {
103 if (data.metadata !== undefined) {
101 if (data.metadata !== undefined) {
104 this.metadata = data.metadata;
102 this.metadata = data.metadata;
105 }
103 }
106 };
104 };
107
105
108
106
109 Cell.prototype.is_splittable = function () {
107 Cell.prototype.is_splittable = function () {
110 return true;
108 return true;
111 };
109 };
112
110
113
111
114 Cell.prototype.get_pre_cursor = function () {
112 Cell.prototype.get_pre_cursor = function () {
115 var cursor = this.code_mirror.getCursor();
113 var cursor = this.code_mirror.getCursor();
116 var text = this.code_mirror.getRange({line:0,ch:0}, cursor);
114 var text = this.code_mirror.getRange({line:0,ch:0}, cursor);
117 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
115 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
118 return text;
116 return text;
119 }
117 }
120
118
121
119
122 Cell.prototype.get_post_cursor = function () {
120 Cell.prototype.get_post_cursor = function () {
123 var cursor = this.code_mirror.getCursor();
121 var cursor = this.code_mirror.getCursor();
124 var last_line_num = this.code_mirror.lineCount()-1;
122 var last_line_num = this.code_mirror.lineCount()-1;
125 var last_line_len = this.code_mirror.getLine(last_line_num).length;
123 var last_line_len = this.code_mirror.getLine(last_line_num).length;
126 var end = {line:last_line_num, ch:last_line_len}
124 var end = {line:last_line_num, ch:last_line_len}
127 var text = this.code_mirror.getRange(cursor, end);
125 var text = this.code_mirror.getRange(cursor, end);
128 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
126 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
129 return text;
127 return text;
130 };
128 };
131
129
132
130
133 Cell.prototype.grow = function(element) {
131 Cell.prototype.grow = function(element) {
134 // Grow the cell by hand. This is used upon reloading from JSON, when the
132 // Grow the cell by hand. This is used upon reloading from JSON, when the
135 // autogrow handler is not called.
133 // autogrow handler is not called.
136 var dom = element.get(0);
134 var dom = element.get(0);
137 var lines_count = 0;
135 var lines_count = 0;
138 // modified split rule from
136 // modified split rule from
139 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
137 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
140 var lines = dom.value.split(/\r|\r\n|\n/);
138 var lines = dom.value.split(/\r|\r\n|\n/);
141 lines_count = lines.length;
139 lines_count = lines.length;
142 if (lines_count >= 1) {
140 if (lines_count >= 1) {
143 dom.rows = lines_count;
141 dom.rows = lines_count;
144 } else {
142 } else {
145 dom.rows = 1;
143 dom.rows = 1;
146 }
144 }
147 };
145 };
148
146
149
147
150 Cell.prototype.toggle_line_numbers = function () {
148 Cell.prototype.toggle_line_numbers = function () {
151 if (this.code_mirror.getOption('lineNumbers') == false) {
149 if (this.code_mirror.getOption('lineNumbers') == false) {
152 this.code_mirror.setOption('lineNumbers', true);
150 this.code_mirror.setOption('lineNumbers', true);
153 } else {
151 } else {
154 this.code_mirror.setOption('lineNumbers', false);
152 this.code_mirror.setOption('lineNumbers', false);
155 }
153 }
156 this.code_mirror.refresh();
154 this.code_mirror.refresh();
157 };
155 };
158
156
159 Cell.prototype.force_highlight = function(mode) {
157 Cell.prototype.force_highlight = function(mode) {
160 this.user_highlight = mode;
158 this.user_highlight = mode;
161 this.auto_highlight();
159 this.auto_highlight();
162 };
160 };
163
161
164 Cell.prototype._auto_highlight = function (modes) {
162 Cell.prototype._auto_highlight = function (modes) {
165 //Here we handle manually selected modes
163 //Here we handle manually selected modes
166 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
164 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
167 {
165 {
168 var mode = this.user_highlight;
166 var mode = this.user_highlight;
169 CodeMirror.autoLoadMode(this.code_mirror, mode);
167 CodeMirror.autoLoadMode(this.code_mirror, mode);
170 this.code_mirror.setOption('mode', mode);
168 this.code_mirror.setOption('mode', mode);
171 return;
169 return;
172 }
170 }
173 var first_line = this.code_mirror.getLine(0);
171 var first_line = this.code_mirror.getLine(0);
174 // loop on every pairs
172 // loop on every pairs
175 for( var mode in modes) {
173 for( var mode in modes) {
176 var regs = modes[mode]['reg'];
174 var regs = modes[mode]['reg'];
177 // only one key every time but regexp can't be keys...
175 // only one key every time but regexp can't be keys...
178 for(var reg in regs ) {
176 for(var reg in regs ) {
179 // here we handle non magic_modes
177 // here we handle non magic_modes
180 if(first_line.match(regs[reg]) != null) {
178 if(first_line.match(regs[reg]) != null) {
181 if (mode.search('magic_') != 0) {
179 if (mode.search('magic_') != 0) {
182 this.code_mirror.setOption('mode',mode);
180 this.code_mirror.setOption('mode',mode);
183 CodeMirror.autoLoadMode(this.code_mirror, mode);
181 CodeMirror.autoLoadMode(this.code_mirror, mode);
184 return;
182 return;
185 }
183 }
186 var open = modes[mode]['open']|| "%%";
184 var open = modes[mode]['open']|| "%%";
187 var close = modes[mode]['close']|| "%%end";
185 var close = modes[mode]['close']|| "%%end";
188 var mmode = mode;
186 var mmode = mode;
189 mode = mmode.substr(6);
187 mode = mmode.substr(6);
190 CodeMirror.autoLoadMode(this.code_mirror, mode);
188 CodeMirror.autoLoadMode(this.code_mirror, mode);
191 // create on the fly a mode that swhitch between
189 // create on the fly a mode that swhitch between
192 // plain/text and smth else otherwise `%%` is
190 // plain/text and smth else otherwise `%%` is
193 // source of some highlight issues.
191 // source of some highlight issues.
194 // we use patchedGetMode to circumvent a bug in CM
192 // we use patchedGetMode to circumvent a bug in CM
195 CodeMirror.defineMode(mmode , function(config) {
193 CodeMirror.defineMode(mmode , function(config) {
196 return CodeMirror.multiplexingMode(
194 return CodeMirror.multiplexingMode(
197 CodeMirror.patchedGetMode(config, 'text/plain'),
195 CodeMirror.patchedGetMode(config, 'text/plain'),
198 // always set someting on close
196 // always set someting on close
199 {open: open, close: close,
197 {open: open, close: close,
200 mode: CodeMirror.patchedGetMode(config, mode),
198 mode: CodeMirror.patchedGetMode(config, mode),
201 delimStyle: "delimit"
199 delimStyle: "delimit"
202 }
200 }
203 );
201 );
204 });
202 });
205 this.code_mirror.setOption('mode', mmode);
203 this.code_mirror.setOption('mode', mmode);
206 return;
204 return;
207 }
205 }
208 }
206 }
209 }
207 }
210 // fallback on default (python)
208 // fallback on default (python)
211 var default_mode = this.default_mode || 'text/plain';
209 var default_mode = this.default_mode || 'text/plain';
212 this.code_mirror.setOption('mode', default_mode);
210 this.code_mirror.setOption('mode', default_mode);
213 };
211 };
214
212
215 IPython.Cell = Cell;
213 IPython.Cell = Cell;
216
214
217 return IPython;
215 return IPython;
218
216
219 }(IPython));
217 }(IPython));
220
218
@@ -1,343 +1,344 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // CodeCell
9 // CodeCell
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13 "use strict";
13 "use strict";
14
14
15 var utils = IPython.utils;
15 var utils = IPython.utils;
16 var key = IPython.utils.keycodes;
16 var key = IPython.utils.keycodes;
17 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
17 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
18
18
19 var CodeCell = function (kernel) {
19 var CodeCell = function (kernel) {
20 // The kernel doesn't have to be set at creation time, in that case
20 // The kernel doesn't have to be set at creation time, in that case
21 // it will be null and set_kernel has to be called later.
21 // it will be null and set_kernel has to be called later.
22 this.kernel = kernel || null;
22 this.kernel = kernel || null;
23 this.code_mirror = null;
23 this.code_mirror = null;
24 this.input_prompt_number = null;
24 this.input_prompt_number = null;
25 this.tooltip_on_tab = true;
25 this.tooltip_on_tab = true;
26 this.collapsed = false;
26 this.collapsed = false;
27 this.default_mode = 'python';
27 this.default_mode = 'python';
28 IPython.Cell.apply(this, arguments);
28 IPython.Cell.apply(this, arguments);
29
29
30 var that = this;
30 var that = this;
31 this.element.focusout(
31 this.element.focusout(
32 function() { that.auto_highlight(); }
32 function() { that.auto_highlight(); }
33 );
33 );
34 };
34 };
35
35
36
36
37 CodeCell.prototype = new IPython.Cell();
37 CodeCell.prototype = new IPython.Cell();
38
38
39
39
40 CodeCell.prototype.auto_highlight = function () {
40 CodeCell.prototype.auto_highlight = function () {
41 this._auto_highlight(IPython.config.cell_magic_highlight)
41 this._auto_highlight(IPython.config.cell_magic_highlight)
42 };
42 };
43
43
44 CodeCell.prototype.create_element = function () {
44 CodeCell.prototype.create_element = function () {
45 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
45 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
46 cell.attr('tabindex','2');
46 cell.attr('tabindex','2');
47 var input = $('<div></div>').addClass('input hbox');
47 var input = $('<div></div>').addClass('input hbox');
48 input.append($('<div/>').addClass('prompt input_prompt'));
48 input.append($('<div/>').addClass('prompt input_prompt'));
49 var input_area = $('<div/>').addClass('input_area box-flex1');
49 var input_area = $('<div/>').addClass('input_area box-flex1');
50 this.code_mirror = CodeMirror(input_area.get(0), {
50 this.code_mirror = CodeMirror(input_area.get(0), {
51 indentUnit : 4,
51 indentUnit : 4,
52 mode: 'python',
52 mode: 'python',
53 theme: 'ipython',
53 theme: 'ipython',
54 readOnly: this.read_only,
54 readOnly: this.read_only,
55 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
55 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
56 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
56 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
57 });
57 });
58 input.append(input_area);
58 input.append(input_area);
59 var output = $('<div></div>');
59 var output = $('<div></div>');
60 cell.append(input).append(output);
60 cell.append(input).append(output);
61 this.element = cell;
61 this.element = cell;
62 this.output_area = new IPython.OutputArea(output, true);
62 this.output_area = new IPython.OutputArea(output, true);
63
63
64 // construct a completer only if class exist
64 // construct a completer only if class exist
65 // otherwise no print view
65 // otherwise no print view
66 if (IPython.Completer !== undefined)
66 if (IPython.Completer !== undefined)
67 {
67 {
68 this.completer = new IPython.Completer(this);
68 this.completer = new IPython.Completer(this);
69 }
69 }
70 };
70 };
71
71
72 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
72 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
73 // This method gets called in CodeMirror's onKeyDown/onKeyPress
73 // This method gets called in CodeMirror's onKeyDown/onKeyPress
74 // handlers and is used to provide custom key handling. Its return
74 // handlers and is used to provide custom key handling. Its return
75 // value is used to determine if CodeMirror should ignore the event:
75 // value is used to determine if CodeMirror should ignore the event:
76 // true = ignore, false = don't ignore.
76 // true = ignore, false = don't ignore.
77
77
78 if (this.read_only){
78 if (this.read_only){
79 return false;
79 return false;
80 }
80 }
81
81
82 var that = this;
82 var that = this;
83 // whatever key is pressed, first, cancel the tooltip request before
83 // whatever key is pressed, first, cancel the tooltip request before
84 // they are sent, and remove tooltip if any, except for tab again
84 // they are sent, and remove tooltip if any, except for tab again
85 if (event.type === 'keydown' && event.which != key.TAB ) {
85 if (event.type === 'keydown' && event.which != key.TAB ) {
86 IPython.tooltip.remove_and_cancel_tooltip();
86 IPython.tooltip.remove_and_cancel_tooltip();
87 };
87 };
88
88
89 var cur = editor.getCursor();
89 var cur = editor.getCursor();
90 if (event.keyCode === key.ENTER){
90 if (event.keyCode === key.ENTER){
91 this.auto_highlight();
91 this.auto_highlight();
92 }
92 }
93
93
94 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
94 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
95 // Always ignore shift-enter in CodeMirror as we handle it.
95 // Always ignore shift-enter in CodeMirror as we handle it.
96 return true;
96 return true;
97 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
97 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
98 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
98 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
99 // browser and keyboard layout !
99 // browser and keyboard layout !
100 // Pressing '(' , request tooltip, don't forget to reappend it
100 // Pressing '(' , request tooltip, don't forget to reappend it
101 IPython.tooltip.pending(that);
101 IPython.tooltip.pending(that);
102 } else if (event.which === key.UPARROW && event.type === 'keydown') {
102 } else if (event.which === key.UPARROW && event.type === 'keydown') {
103 // If we are not at the top, let CM handle the up arrow and
103 // If we are not at the top, let CM handle the up arrow and
104 // prevent the global keydown handler from handling it.
104 // prevent the global keydown handler from handling it.
105 if (!that.at_top()) {
105 if (!that.at_top()) {
106 event.stop();
106 event.stop();
107 return false;
107 return false;
108 } else {
108 } else {
109 return true;
109 return true;
110 };
110 };
111 } else if (event.which === key.ESC) {
111 } else if (event.which === key.ESC) {
112 IPython.tooltip.remove_and_cancel_tooltip(true);
112 IPython.tooltip.remove_and_cancel_tooltip(true);
113 return true;
113 return true;
114 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
114 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
115 // If we are not at the bottom, let CM handle the down arrow and
115 // If we are not at the bottom, let CM handle the down arrow and
116 // prevent the global keydown handler from handling it.
116 // prevent the global keydown handler from handling it.
117 if (!that.at_bottom()) {
117 if (!that.at_bottom()) {
118 event.stop();
118 event.stop();
119 return false;
119 return false;
120 } else {
120 } else {
121 return true;
121 return true;
122 };
122 };
123 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
123 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
124 // Tab completion.
124 // Tab completion.
125 //Do not trim here because of tooltip
125 //Do not trim here because of tooltip
126 if (editor.somethingSelected()){return false}
126 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
127 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
127 if (pre_cursor.trim() === "") {
128 if (pre_cursor.trim() === "") {
128 // Don't autocomplete if the part of the line before the cursor
129 // Don't autocomplete if the part of the line before the cursor
129 // is empty. In this case, let CodeMirror handle indentation.
130 // is empty. In this case, let CodeMirror handle indentation.
130 return false;
131 return false;
131 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && that.tooltip_on_tab ) {
132 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && that.tooltip_on_tab ) {
132 IPython.tooltip.request(that);
133 IPython.tooltip.request(that);
133 // Prevent the event from bubbling up.
134 // Prevent the event from bubbling up.
134 event.stop();
135 event.stop();
135 // Prevent CodeMirror from handling the tab.
136 // Prevent CodeMirror from handling the tab.
136 return true;
137 return true;
137 } else {
138 } else {
138 event.stop();
139 event.stop();
139 this.completer.startCompletion();
140 this.completer.startCompletion();
140 return true;
141 return true;
141 };
142 };
142 } else {
143 } else {
143 // keypress/keyup also trigger on TAB press, and we don't want to
144 // keypress/keyup also trigger on TAB press, and we don't want to
144 // use those to disable tab completion.
145 // use those to disable tab completion.
145 return false;
146 return false;
146 };
147 };
147 return false;
148 return false;
148 };
149 };
149
150
150
151
151 // Kernel related calls.
152 // Kernel related calls.
152
153
153 CodeCell.prototype.set_kernel = function (kernel) {
154 CodeCell.prototype.set_kernel = function (kernel) {
154 this.kernel = kernel;
155 this.kernel = kernel;
155 }
156 }
156
157
157
158
158 CodeCell.prototype.execute = function () {
159 CodeCell.prototype.execute = function () {
159 this.output_area.clear_output(true, true, true);
160 this.output_area.clear_output(true, true, true);
160 this.set_input_prompt('*');
161 this.set_input_prompt('*');
161 this.element.addClass("running");
162 this.element.addClass("running");
162 var callbacks = {
163 var callbacks = {
163 'execute_reply': $.proxy(this._handle_execute_reply, this),
164 'execute_reply': $.proxy(this._handle_execute_reply, this),
164 'output': $.proxy(this.output_area.handle_output, this.output_area),
165 'output': $.proxy(this.output_area.handle_output, this.output_area),
165 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
166 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
166 'set_next_input': $.proxy(this._handle_set_next_input, this)
167 'set_next_input': $.proxy(this._handle_set_next_input, this)
167 };
168 };
168 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
169 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
169 };
170 };
170
171
171
172
172 CodeCell.prototype._handle_execute_reply = function (content) {
173 CodeCell.prototype._handle_execute_reply = function (content) {
173 this.set_input_prompt(content.execution_count);
174 this.set_input_prompt(content.execution_count);
174 this.element.removeClass("running");
175 this.element.removeClass("running");
175 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
176 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
176 }
177 }
177
178
178 CodeCell.prototype._handle_set_next_input = function (text) {
179 CodeCell.prototype._handle_set_next_input = function (text) {
179 var data = {'cell': this, 'text': text}
180 var data = {'cell': this, 'text': text}
180 $([IPython.events]).trigger('set_next_input.Notebook', data);
181 $([IPython.events]).trigger('set_next_input.Notebook', data);
181 }
182 }
182
183
183 // Basic cell manipulation.
184 // Basic cell manipulation.
184
185
185 CodeCell.prototype.select = function () {
186 CodeCell.prototype.select = function () {
186 IPython.Cell.prototype.select.apply(this);
187 IPython.Cell.prototype.select.apply(this);
187 this.code_mirror.refresh();
188 this.code_mirror.refresh();
188 this.code_mirror.focus();
189 this.code_mirror.focus();
189 this.auto_highlight();
190 this.auto_highlight();
190 // We used to need an additional refresh() after the focus, but
191 // We used to need an additional refresh() after the focus, but
191 // it appears that this has been fixed in CM. This bug would show
192 // it appears that this has been fixed in CM. This bug would show
192 // up on FF when a newly loaded markdown cell was edited.
193 // up on FF when a newly loaded markdown cell was edited.
193 };
194 };
194
195
195
196
196 CodeCell.prototype.select_all = function () {
197 CodeCell.prototype.select_all = function () {
197 var start = {line: 0, ch: 0};
198 var start = {line: 0, ch: 0};
198 var nlines = this.code_mirror.lineCount();
199 var nlines = this.code_mirror.lineCount();
199 var last_line = this.code_mirror.getLine(nlines-1);
200 var last_line = this.code_mirror.getLine(nlines-1);
200 var end = {line: nlines-1, ch: last_line.length};
201 var end = {line: nlines-1, ch: last_line.length};
201 this.code_mirror.setSelection(start, end);
202 this.code_mirror.setSelection(start, end);
202 };
203 };
203
204
204
205
205 CodeCell.prototype.collapse = function () {
206 CodeCell.prototype.collapse = function () {
206 this.collapsed = true;
207 this.collapsed = true;
207 this.output_area.collapse();
208 this.output_area.collapse();
208 };
209 };
209
210
210
211
211 CodeCell.prototype.expand = function () {
212 CodeCell.prototype.expand = function () {
212 this.collapsed = false;
213 this.collapsed = false;
213 this.output_area.expand();
214 this.output_area.expand();
214 };
215 };
215
216
216
217
217 CodeCell.prototype.toggle_output = function () {
218 CodeCell.prototype.toggle_output = function () {
218 this.collapsed = Boolean(1 - this.collapsed);
219 this.collapsed = Boolean(1 - this.collapsed);
219 this.output_area.toggle_output();
220 this.output_area.toggle_output();
220 };
221 };
221
222
222
223
223 CodeCell.prototype.toggle_output_scroll = function () {
224 CodeCell.prototype.toggle_output_scroll = function () {
224 this.output_area.toggle_scroll();
225 this.output_area.toggle_scroll();
225 };
226 };
226
227
227
228
228
229
229
230
230
231
231 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
232 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
232 var ns = prompt_value || "&nbsp;";
233 var ns = prompt_value || "&nbsp;";
233 return 'In&nbsp;[' + ns + ']:'
234 return 'In&nbsp;[' + ns + ']:'
234 };
235 };
235
236
236 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
237 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
237 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
238 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
238 for(var i=1; i < lines_number; i++){html.push(['...:'])};
239 for(var i=1; i < lines_number; i++){html.push(['...:'])};
239 return html.join('</br>')
240 return html.join('</br>')
240 };
241 };
241
242
242 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
243 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
243
244
244
245
245 CodeCell.prototype.set_input_prompt = function (number) {
246 CodeCell.prototype.set_input_prompt = function (number) {
246 var nline = 1
247 var nline = 1
247 if( this.code_mirror != undefined) {
248 if( this.code_mirror != undefined) {
248 nline = this.code_mirror.lineCount();
249 nline = this.code_mirror.lineCount();
249 }
250 }
250 this.input_prompt_number = number;
251 this.input_prompt_number = number;
251 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
252 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
252 this.element.find('div.input_prompt').html(prompt_html);
253 this.element.find('div.input_prompt').html(prompt_html);
253 };
254 };
254
255
255
256
256 CodeCell.prototype.clear_input = function () {
257 CodeCell.prototype.clear_input = function () {
257 this.code_mirror.setValue('');
258 this.code_mirror.setValue('');
258 };
259 };
259
260
260
261
261 CodeCell.prototype.get_text = function () {
262 CodeCell.prototype.get_text = function () {
262 return this.code_mirror.getValue();
263 return this.code_mirror.getValue();
263 };
264 };
264
265
265
266
266 CodeCell.prototype.set_text = function (code) {
267 CodeCell.prototype.set_text = function (code) {
267 return this.code_mirror.setValue(code);
268 return this.code_mirror.setValue(code);
268 };
269 };
269
270
270
271
271 CodeCell.prototype.at_top = function () {
272 CodeCell.prototype.at_top = function () {
272 var cursor = this.code_mirror.getCursor();
273 var cursor = this.code_mirror.getCursor();
273 if (cursor.line === 0 && cursor.ch === 0) {
274 if (cursor.line === 0 && cursor.ch === 0) {
274 return true;
275 return true;
275 } else {
276 } else {
276 return false;
277 return false;
277 }
278 }
278 };
279 };
279
280
280
281
281 CodeCell.prototype.at_bottom = function () {
282 CodeCell.prototype.at_bottom = function () {
282 var cursor = this.code_mirror.getCursor();
283 var cursor = this.code_mirror.getCursor();
283 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
284 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
284 return true;
285 return true;
285 } else {
286 } else {
286 return false;
287 return false;
287 }
288 }
288 };
289 };
289
290
290
291
291 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
292 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
292 this.output_area.clear_output(stdout, stderr, other);
293 this.output_area.clear_output(stdout, stderr, other);
293 };
294 };
294
295
295
296
296 // JSON serialization
297 // JSON serialization
297
298
298 CodeCell.prototype.fromJSON = function (data) {
299 CodeCell.prototype.fromJSON = function (data) {
299 IPython.Cell.prototype.fromJSON.apply(this, arguments);
300 IPython.Cell.prototype.fromJSON.apply(this, arguments);
300 if (data.cell_type === 'code') {
301 if (data.cell_type === 'code') {
301 if (data.input !== undefined) {
302 if (data.input !== undefined) {
302 this.set_text(data.input);
303 this.set_text(data.input);
303 // make this value the starting point, so that we can only undo
304 // make this value the starting point, so that we can only undo
304 // to this state, instead of a blank cell
305 // to this state, instead of a blank cell
305 this.code_mirror.clearHistory();
306 this.code_mirror.clearHistory();
306 this.auto_highlight();
307 this.auto_highlight();
307 }
308 }
308 if (data.prompt_number !== undefined) {
309 if (data.prompt_number !== undefined) {
309 this.set_input_prompt(data.prompt_number);
310 this.set_input_prompt(data.prompt_number);
310 } else {
311 } else {
311 this.set_input_prompt();
312 this.set_input_prompt();
312 };
313 };
313 this.output_area.fromJSON(data.outputs);
314 this.output_area.fromJSON(data.outputs);
314 if (data.collapsed !== undefined) {
315 if (data.collapsed !== undefined) {
315 if (data.collapsed) {
316 if (data.collapsed) {
316 this.collapse();
317 this.collapse();
317 } else {
318 } else {
318 this.expand();
319 this.expand();
319 };
320 };
320 };
321 };
321 };
322 };
322 };
323 };
323
324
324
325
325 CodeCell.prototype.toJSON = function () {
326 CodeCell.prototype.toJSON = function () {
326 var data = IPython.Cell.prototype.toJSON.apply(this);
327 var data = IPython.Cell.prototype.toJSON.apply(this);
327 data.input = this.get_text();
328 data.input = this.get_text();
328 data.cell_type = 'code';
329 data.cell_type = 'code';
329 if (this.input_prompt_number) {
330 if (this.input_prompt_number) {
330 data.prompt_number = this.input_prompt_number;
331 data.prompt_number = this.input_prompt_number;
331 };
332 };
332 var outputs = this.output_area.toJSON();
333 var outputs = this.output_area.toJSON();
333 data.outputs = outputs;
334 data.outputs = outputs;
334 data.language = 'python';
335 data.language = 'python';
335 data.collapsed = this.collapsed;
336 data.collapsed = this.collapsed;
336 return data;
337 return data;
337 };
338 };
338
339
339
340
340 IPython.CodeCell = CodeCell;
341 IPython.CodeCell = CodeCell;
341
342
342 return IPython;
343 return IPython;
343 }(IPython));
344 }(IPython));
@@ -1,391 +1,394 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Kernel
9 // Kernel
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 // Initialization and connection.
16 // Initialization and connection.
17
17
18 var Kernel = function (base_url) {
18 var Kernel = function (base_url) {
19 this.kernel_id = null;
19 this.kernel_id = null;
20 this.shell_channel = null;
20 this.shell_channel = null;
21 this.iopub_channel = null;
21 this.iopub_channel = null;
22 this.base_url = base_url;
22 this.base_url = base_url;
23 this.running = false;
23 this.running = false;
24 this.username = "username";
24 this.username = "username";
25 this.session_id = utils.uuid();
25 this.session_id = utils.uuid();
26 this._msg_callbacks = {};
26 this._msg_callbacks = {};
27
27
28 if (typeof(WebSocket) !== 'undefined') {
28 if (typeof(WebSocket) !== 'undefined') {
29 this.WebSocket = WebSocket;
29 this.WebSocket = WebSocket;
30 } else if (typeof(MozWebSocket) !== 'undefined') {
30 } else if (typeof(MozWebSocket) !== 'undefined') {
31 this.WebSocket = MozWebSocket;
31 this.WebSocket = MozWebSocket;
32 } else {
32 } else {
33 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox ≥ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
33 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox ≥ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
34 };
34 };
35 };
35 };
36
36
37
37
38 Kernel.prototype._get_msg = function (msg_type, content) {
38 Kernel.prototype._get_msg = function (msg_type, content) {
39 var msg = {
39 var msg = {
40 header : {
40 header : {
41 msg_id : utils.uuid(),
41 msg_id : utils.uuid(),
42 username : this.username,
42 username : this.username,
43 session : this.session_id,
43 session : this.session_id,
44 msg_type : msg_type
44 msg_type : msg_type
45 },
45 },
46 metadata : {},
46 metadata : {},
47 content : content,
47 content : content,
48 parent_header : {}
48 parent_header : {}
49 };
49 };
50 return msg;
50 return msg;
51 };
51 };
52
52
53 Kernel.prototype.start = function (notebook_id) {
53 Kernel.prototype.start = function (notebook_id) {
54 var that = this;
54 var that = this;
55 if (!this.running) {
55 if (!this.running) {
56 var qs = $.param({notebook:notebook_id});
56 var qs = $.param({notebook:notebook_id});
57 var url = this.base_url + '?' + qs;
57 var url = this.base_url + '?' + qs;
58 $.post(url,
58 $.post(url,
59 $.proxy(that._kernel_started,that),
59 $.proxy(that._kernel_started,that),
60 'json'
60 'json'
61 );
61 );
62 };
62 };
63 };
63 };
64
64
65
65
66 Kernel.prototype.restart = function () {
66 Kernel.prototype.restart = function () {
67 $([IPython.events]).trigger({type: 'status_restarting.Kernel', kernel: this});
67 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
68 var that = this;
68 var that = this;
69 if (this.running) {
69 if (this.running) {
70 this.stop_channels();
70 this.stop_channels();
71 var url = this.kernel_url + "/restart";
71 var url = this.kernel_url + "/restart";
72 $.post(url,
72 $.post(url,
73 $.proxy(that._kernel_started, that),
73 $.proxy(that._kernel_started, that),
74 'json'
74 'json'
75 );
75 );
76 };
76 };
77 };
77 };
78
78
79
79
80 Kernel.prototype._kernel_started = function (json) {
80 Kernel.prototype._kernel_started = function (json) {
81 console.log("Kernel started: ", json.kernel_id);
81 console.log("Kernel started: ", json.kernel_id);
82 this.running = true;
82 this.running = true;
83 this.kernel_id = json.kernel_id;
83 this.kernel_id = json.kernel_id;
84 this.ws_url = json.ws_url;
84 this.ws_url = json.ws_url;
85 this.kernel_url = this.base_url + "/" + this.kernel_id;
85 this.kernel_url = this.base_url + "/" + this.kernel_id;
86 this.start_channels();
86 this.start_channels();
87 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
87 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
88 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
88 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
89 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
89 };
90 };
90
91
91
92
92 Kernel.prototype._websocket_closed = function(ws_url, early){
93 Kernel.prototype._websocket_closed = function(ws_url, early){
93 var msg;
94 var msg;
94 var parent_item = $('body');
95 var parent_item = $('body');
95 if (early) {
96 if (early) {
96 msg = "Websocket connection to " + ws_url + " could not be established." +
97 msg = "Websocket connection to " + ws_url + " could not be established." +
97 " You will NOT be able to run code." +
98 " You will NOT be able to run code." +
98 " Your browser may not be compatible with the websocket version in the server," +
99 " Your browser may not be compatible with the websocket version in the server," +
99 " or if the url does not look right, there could be an error in the" +
100 " or if the url does not look right, there could be an error in the" +
100 " server's configuration.";
101 " server's configuration.";
101 } else {
102 } else {
102 IPython.notification_area.widget('kernel').set_message('Reconnecting Websockets', 1000);
103 IPython.notification_area.widget('kernel').set_message('Reconnecting Websockets', 1000);
103 this.start_channels();
104 this.start_channels();
104 return;
105 return;
105 }
106 }
106 var dialog = $('<div/>');
107 var dialog = $('<div/>');
107 dialog.html(msg);
108 dialog.html(msg);
108 parent_item.append(dialog);
109 parent_item.append(dialog);
109 dialog.dialog({
110 dialog.dialog({
110 resizable: false,
111 resizable: false,
111 modal: true,
112 modal: true,
112 title: "Websocket closed",
113 title: "Websocket closed",
113 closeText: "",
114 closeText: "",
114 close: function(event, ui) {$(this).dialog('destroy').remove();},
115 close: function(event, ui) {$(this).dialog('destroy').remove();},
115 buttons : {
116 buttons : {
116 "OK": function () {
117 "OK": function () {
117 $(this).dialog('close');
118 $(this).dialog('close');
118 }
119 }
119 }
120 }
120 });
121 });
121
122
122 };
123 };
123
124
124 Kernel.prototype.start_channels = function () {
125 Kernel.prototype.start_channels = function () {
125 var that = this;
126 var that = this;
126 this.stop_channels();
127 this.stop_channels();
127 var ws_url = this.ws_url + this.kernel_url;
128 var ws_url = this.ws_url + this.kernel_url;
128 console.log("Starting WS:", ws_url);
129 console.log("Starting WS:", ws_url);
129 this.shell_channel = new this.WebSocket(ws_url + "/shell");
130 this.shell_channel = new this.WebSocket(ws_url + "/shell");
130 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
131 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
131 send_cookie = function(){
132 send_cookie = function(){
132 this.send(document.cookie);
133 this.send(document.cookie);
133 };
134 };
134 var already_called_onclose = false; // only alert once
135 var already_called_onclose = false; // only alert once
135 ws_closed_early = function(evt){
136 ws_closed_early = function(evt){
136 if (already_called_onclose){
137 if (already_called_onclose){
137 return;
138 return;
138 }
139 }
139 already_called_onclose = true;
140 already_called_onclose = true;
140 if ( ! evt.wasClean ){
141 if ( ! evt.wasClean ){
141 that._websocket_closed(ws_url, true);
142 that._websocket_closed(ws_url, true);
142 }
143 }
143 };
144 };
144 ws_closed_late = function(evt){
145 ws_closed_late = function(evt){
145 if (already_called_onclose){
146 if (already_called_onclose){
146 return;
147 return;
147 }
148 }
148 already_called_onclose = true;
149 already_called_onclose = true;
149 if ( ! evt.wasClean ){
150 if ( ! evt.wasClean ){
150 that._websocket_closed(ws_url, false);
151 that._websocket_closed(ws_url, false);
151 }
152 }
152 };
153 };
153 this.shell_channel.onopen = send_cookie;
154 this.shell_channel.onopen = send_cookie;
154 this.shell_channel.onclose = ws_closed_early;
155 this.shell_channel.onclose = ws_closed_early;
155 this.iopub_channel.onopen = send_cookie;
156 this.iopub_channel.onopen = send_cookie;
156 this.iopub_channel.onclose = ws_closed_early;
157 this.iopub_channel.onclose = ws_closed_early;
157 // switch from early-close to late-close message after 1s
158 // switch from early-close to late-close message after 1s
158 setTimeout(function(){
159 setTimeout(function(){
159 that.shell_channel.onclose = ws_closed_late;
160 that.shell_channel.onclose = ws_closed_late;
160 that.iopub_channel.onclose = ws_closed_late;
161 that.iopub_channel.onclose = ws_closed_late;
161 }, 1000);
162 }, 1000);
162 };
163 };
163
164
164
165
165 Kernel.prototype.stop_channels = function () {
166 Kernel.prototype.stop_channels = function () {
166 if (this.shell_channel !== null) {
167 if (this.shell_channel !== null) {
167 this.shell_channel.onclose = function (evt) {};
168 this.shell_channel.onclose = function (evt) {};
168 this.shell_channel.close();
169 this.shell_channel.close();
169 this.shell_channel = null;
170 this.shell_channel = null;
170 };
171 };
171 if (this.iopub_channel !== null) {
172 if (this.iopub_channel !== null) {
172 this.iopub_channel.onclose = function (evt) {};
173 this.iopub_channel.onclose = function (evt) {};
173 this.iopub_channel.close();
174 this.iopub_channel.close();
174 this.iopub_channel = null;
175 this.iopub_channel = null;
175 };
176 };
176 };
177 };
177
178
178 // Main public methods.
179 // Main public methods.
179
180
180 Kernel.prototype.object_info_request = function (objname, callbacks) {
181 Kernel.prototype.object_info_request = function (objname, callbacks) {
181 // When calling this method pass a callbacks structure of the form:
182 // When calling this method pass a callbacks structure of the form:
182 //
183 //
183 // callbacks = {
184 // callbacks = {
184 // 'object_info_reply': object_into_reply_callback
185 // 'object_info_reply': object_into_reply_callback
185 // }
186 // }
186 //
187 //
187 // The object_info_reply_callback will be passed the content object of the
188 // The object_info_reply_callback will be passed the content object of the
188 // object_into_reply message documented here:
189 // object_into_reply message documented here:
189 //
190 //
190 // http://ipython.org/ipython-doc/dev/development/messaging.html#object-information
191 // http://ipython.org/ipython-doc/dev/development/messaging.html#object-information
191 if(typeof(objname)!=null && objname!=null)
192 if(typeof(objname)!=null && objname!=null)
192 {
193 {
193 var content = {
194 var content = {
194 oname : objname.toString(),
195 oname : objname.toString(),
195 };
196 };
196 var msg = this._get_msg("object_info_request", content);
197 var msg = this._get_msg("object_info_request", content);
197 this.shell_channel.send(JSON.stringify(msg));
198 this.shell_channel.send(JSON.stringify(msg));
198 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
199 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
199 return msg.header.msg_id;
200 return msg.header.msg_id;
200 }
201 }
201 return;
202 return;
202 }
203 }
203
204
204 Kernel.prototype.execute = function (code, callbacks, options) {
205 Kernel.prototype.execute = function (code, callbacks, options) {
205 // The options object should contain the options for the execute call. Its default
206 // The options object should contain the options for the execute call. Its default
206 // values are:
207 // values are:
207 //
208 //
208 // options = {
209 // options = {
209 // silent : true,
210 // silent : true,
210 // user_variables : [],
211 // user_variables : [],
211 // user_expressions : {},
212 // user_expressions : {},
212 // allow_stdin : false
213 // allow_stdin : false
213 // }
214 // }
214 //
215 //
215 // When calling this method pass a callbacks structure of the form:
216 // When calling this method pass a callbacks structure of the form:
216 //
217 //
217 // callbacks = {
218 // callbacks = {
218 // 'execute_reply': execute_reply_callback,
219 // 'execute_reply': execute_reply_callback,
219 // 'output': output_callback,
220 // 'output': output_callback,
220 // 'clear_output': clear_output_callback,
221 // 'clear_output': clear_output_callback,
221 // 'set_next_input': set_next_input_callback
222 // 'set_next_input': set_next_input_callback
222 // }
223 // }
223 //
224 //
224 // The execute_reply_callback will be passed the content and metadata objects of the execute_reply
225 // The execute_reply_callback will be passed the content and metadata objects of the execute_reply
225 // message documented here:
226 // message documented here:
226 //
227 //
227 // http://ipython.org/ipython-doc/dev/development/messaging.html#execute
228 // http://ipython.org/ipython-doc/dev/development/messaging.html#execute
228 //
229 //
229 // The output_callback will be passed msg_type ('stream','display_data','pyout','pyerr')
230 // The output_callback will be passed msg_type ('stream','display_data','pyout','pyerr')
230 // of the output and the content and metadata objects of the PUB/SUB channel that contains the
231 // of the output and the content and metadata objects of the PUB/SUB channel that contains the
231 // output:
232 // output:
232 //
233 //
233 // http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
234 // http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
234 //
235 //
235 // The clear_output_callback will be passed a content object that contains
236 // The clear_output_callback will be passed a content object that contains
236 // stdout, stderr and other fields that are booleans, as well as the metadata object.
237 // stdout, stderr and other fields that are booleans, as well as the metadata object.
237 //
238 //
238 // The set_next_input_callback will be passed the text that should become the next
239 // The set_next_input_callback will be passed the text that should become the next
239 // input cell.
240 // input cell.
240
241
241 var content = {
242 var content = {
242 code : code,
243 code : code,
243 silent : true,
244 silent : true,
244 user_variables : [],
245 user_variables : [],
245 user_expressions : {},
246 user_expressions : {},
246 allow_stdin : false
247 allow_stdin : false
247 };
248 };
248 $.extend(true, content, options)
249 $.extend(true, content, options)
250 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
249 var msg = this._get_msg("execute_request", content);
251 var msg = this._get_msg("execute_request", content);
250 this.shell_channel.send(JSON.stringify(msg));
252 this.shell_channel.send(JSON.stringify(msg));
251 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
253 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
252 return msg.header.msg_id;
254 return msg.header.msg_id;
253 };
255 };
254
256
255
257
256 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
258 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
257 // When calling this method pass a callbacks structure of the form:
259 // When calling this method pass a callbacks structure of the form:
258 //
260 //
259 // callbacks = {
261 // callbacks = {
260 // 'complete_reply': complete_reply_callback
262 // 'complete_reply': complete_reply_callback
261 // }
263 // }
262 //
264 //
263 // The complete_reply_callback will be passed the content object of the
265 // The complete_reply_callback will be passed the content object of the
264 // complete_reply message documented here:
266 // complete_reply message documented here:
265 //
267 //
266 // http://ipython.org/ipython-doc/dev/development/messaging.html#complete
268 // http://ipython.org/ipython-doc/dev/development/messaging.html#complete
267 callbacks = callbacks || {};
269 callbacks = callbacks || {};
268 var content = {
270 var content = {
269 text : '',
271 text : '',
270 line : line,
272 line : line,
271 cursor_pos : cursor_pos
273 cursor_pos : cursor_pos
272 };
274 };
273 var msg = this._get_msg("complete_request", content);
275 var msg = this._get_msg("complete_request", content);
274 this.shell_channel.send(JSON.stringify(msg));
276 this.shell_channel.send(JSON.stringify(msg));
275 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
277 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
276 return msg.header.msg_id;
278 return msg.header.msg_id;
277 };
279 };
278
280
279
281
280 Kernel.prototype.interrupt = function () {
282 Kernel.prototype.interrupt = function () {
281 if (this.running) {
283 if (this.running) {
282 $([IPython.events]).trigger({type: 'status_interrupting.Kernel', kernel: this});
284 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
283 $.post(this.kernel_url + "/interrupt");
285 $.post(this.kernel_url + "/interrupt");
284 };
286 };
285 };
287 };
286
288
287
289
288 Kernel.prototype.kill = function () {
290 Kernel.prototype.kill = function () {
289 if (this.running) {
291 if (this.running) {
290 this.running = false;
292 this.running = false;
291 var settings = {
293 var settings = {
292 cache : false,
294 cache : false,
293 type : "DELETE"
295 type : "DELETE"
294 };
296 };
295 $.ajax(this.kernel_url, settings);
297 $.ajax(this.kernel_url, settings);
296 };
298 };
297 };
299 };
298
300
299
301
300 // Reply handlers.
302 // Reply handlers.
301
303
302 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
304 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
303 var callbacks = this._msg_callbacks[msg_id];
305 var callbacks = this._msg_callbacks[msg_id];
304 return callbacks;
306 return callbacks;
305 };
307 };
306
308
307
309
308 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
310 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
309 this._msg_callbacks[msg_id] = callbacks || {};
311 this._msg_callbacks[msg_id] = callbacks || {};
310 }
312 }
311
313
312
314
313 Kernel.prototype._handle_shell_reply = function (e) {
315 Kernel.prototype._handle_shell_reply = function (e) {
314 reply = $.parseJSON(e.data);
316 reply = $.parseJSON(e.data);
317 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
315 var header = reply.header;
318 var header = reply.header;
316 var content = reply.content;
319 var content = reply.content;
317 var metadata = reply.metadata;
320 var metadata = reply.metadata;
318 var msg_type = header.msg_type;
321 var msg_type = header.msg_type;
319 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
322 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
320 if (callbacks !== undefined) {
323 if (callbacks !== undefined) {
321 var cb = callbacks[msg_type];
324 var cb = callbacks[msg_type];
322 if (cb !== undefined) {
325 if (cb !== undefined) {
323 cb(content, metadata);
326 cb(content, metadata);
324 }
327 }
325 };
328 };
326
329
327 if (content.payload !== undefined) {
330 if (content.payload !== undefined) {
328 var payload = content.payload || [];
331 var payload = content.payload || [];
329 this._handle_payload(callbacks, payload);
332 this._handle_payload(callbacks, payload);
330 }
333 }
331 };
334 };
332
335
333
336
334 Kernel.prototype._handle_payload = function (callbacks, payload) {
337 Kernel.prototype._handle_payload = function (callbacks, payload) {
335 var l = payload.length;
338 var l = payload.length;
336 // Payloads are handled by triggering events because we don't want the Kernel
339 // Payloads are handled by triggering events because we don't want the Kernel
337 // to depend on the Notebook or Pager classes.
340 // to depend on the Notebook or Pager classes.
338 for (var i=0; i<l; i++) {
341 for (var i=0; i<l; i++) {
339 if (payload[i].source === 'IPython.zmq.page.page') {
342 if (payload[i].source === 'IPython.zmq.page.page') {
340 var data = {'text':payload[i].text}
343 var data = {'text':payload[i].text}
341 $([IPython.events]).trigger('open_with_text.Pager', data);
344 $([IPython.events]).trigger('open_with_text.Pager', data);
342 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
345 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
343 if (callbacks.set_next_input !== undefined) {
346 if (callbacks.set_next_input !== undefined) {
344 callbacks.set_next_input(payload[i].text)
347 callbacks.set_next_input(payload[i].text)
345 }
348 }
346 }
349 }
347 };
350 };
348 };
351 };
349
352
350
353
351 Kernel.prototype._handle_iopub_reply = function (e) {
354 Kernel.prototype._handle_iopub_reply = function (e) {
352 var reply = $.parseJSON(e.data);
355 var reply = $.parseJSON(e.data);
353 var content = reply.content;
356 var content = reply.content;
354 var msg_type = reply.header.msg_type;
357 var msg_type = reply.header.msg_type;
355 var metadata = reply.metadata;
358 var metadata = reply.metadata;
356 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
359 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
357 if (msg_type !== 'status' && callbacks === undefined) {
360 if (msg_type !== 'status' && callbacks === undefined) {
358 // Message not from one of this notebook's cells and there are no
361 // Message not from one of this notebook's cells and there are no
359 // callbacks to handle it.
362 // callbacks to handle it.
360 return;
363 return;
361 }
364 }
362 var output_types = ['stream','display_data','pyout','pyerr'];
365 var output_types = ['stream','display_data','pyout','pyerr'];
363 if (output_types.indexOf(msg_type) >= 0) {
366 if (output_types.indexOf(msg_type) >= 0) {
364 var cb = callbacks['output'];
367 var cb = callbacks['output'];
365 if (cb !== undefined) {
368 if (cb !== undefined) {
366 cb(msg_type, content, metadata);
369 cb(msg_type, content, metadata);
367 }
370 }
368 } else if (msg_type === 'status') {
371 } else if (msg_type === 'status') {
369 if (content.execution_state === 'busy') {
372 if (content.execution_state === 'busy') {
370 $([IPython.events]).trigger({type: 'status_busy.Kernel', kernel: this});
373 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
371 } else if (content.execution_state === 'idle') {
374 } else if (content.execution_state === 'idle') {
372 $([IPython.events]).trigger({type: 'status_idle.Kernel', kernel: this});
375 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
373 } else if (content.execution_state === 'dead') {
376 } else if (content.execution_state === 'dead') {
374 this.stop_channels();
377 this.stop_channels();
375 $([IPython.events]).trigger({type: 'status_dead.Kernel', kernel: this});
378 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
376 };
379 };
377 } else if (msg_type === 'clear_output') {
380 } else if (msg_type === 'clear_output') {
378 var cb = callbacks['clear_output'];
381 var cb = callbacks['clear_output'];
379 if (cb !== undefined) {
382 if (cb !== undefined) {
380 cb(content, metadata);
383 cb(content, metadata);
381 }
384 }
382 };
385 };
383 };
386 };
384
387
385
388
386 IPython.Kernel = Kernel;
389 IPython.Kernel = Kernel;
387
390
388 return IPython;
391 return IPython;
389
392
390 }(IPython));
393 }(IPython));
391
394
@@ -1,179 +1,179 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
2 // Copyright (C) 2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // ToolBar
9 // ToolBar
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var MainToolBar = function (selector) {
14 var MainToolBar = function (selector) {
15 this.selector = selector;
15 this.selector = selector;
16 IPython.ToolBar.apply(this, arguments);
16 IPython.ToolBar.apply(this, arguments);
17 this.construct();
17 this.construct();
18 this.add_drop_down_list();
18 this.add_drop_down_list();
19 this.bind_events();
19 this.bind_events();
20 };
20 };
21
21
22 MainToolBar.prototype = new IPython.ToolBar();
22 MainToolBar.prototype = new IPython.ToolBar();
23
23
24 MainToolBar.prototype.construct = function () {
24 MainToolBar.prototype.construct = function () {
25 this.add_buttons_group([
25 this.add_buttons_group([
26 {
26 {
27 id : 'save_b',
27 id : 'save_b',
28 label : 'Save',
28 label : 'Save',
29 icon : 'ui-icon-disk',
29 icon : 'ui-icon-disk',
30 callback : function () {
30 callback : function () {
31 IPython.notebook.save_notebook();
31 IPython.notebook.save_notebook();
32 }
32 }
33 }
33 }
34 ]);
34 ]);
35 this.add_buttons_group([
35 this.add_buttons_group([
36 {
36 {
37 id : 'cut_b',
37 id : 'cut_b',
38 label : 'Cut Cell',
38 label : 'Cut Cell',
39 icon : 'ui-icon-scissors',
39 icon : 'ui-icon-scissors',
40 callback : function () {
40 callback : function () {
41 IPython.notebook.cut_cell();
41 IPython.notebook.cut_cell();
42 }
42 }
43 },
43 },
44 {
44 {
45 id : 'copy_b',
45 id : 'copy_b',
46 label : 'Copy Cell',
46 label : 'Copy Cell',
47 icon : 'ui-icon-copy',
47 icon : 'ui-icon-copy',
48 callback : function () {
48 callback : function () {
49 IPython.notebook.copy_cell();
49 IPython.notebook.copy_cell();
50 }
50 }
51 },
51 },
52 {
52 {
53 id : 'paste_b',
53 id : 'paste_b',
54 label : 'Paste Cell',
54 label : 'Paste Cell Below',
55 icon : 'ui-icon-clipboard',
55 icon : 'ui-icon-clipboard',
56 callback : function () {
56 callback : function () {
57 IPython.notebook.paste_cell();
57 IPython.notebook.paste_cell_below();
58 }
58 }
59 }
59 }
60 ],'cut_copy_paste');
60 ],'cut_copy_paste');
61
61
62 this.add_buttons_group([
62 this.add_buttons_group([
63 {
63 {
64 id : 'move_up_b',
64 id : 'move_up_b',
65 label : 'Move Cell Up',
65 label : 'Move Cell Up',
66 icon : 'ui-icon-arrowthick-1-n',
66 icon : 'ui-icon-arrowthick-1-n',
67 callback : function () {
67 callback : function () {
68 IPython.notebook.move_cell_up();
68 IPython.notebook.move_cell_up();
69 }
69 }
70 },
70 },
71 {
71 {
72 id : 'move_down_b',
72 id : 'move_down_b',
73 label : 'Move Cell Down',
73 label : 'Move Cell Down',
74 icon : 'ui-icon-arrowthick-1-s',
74 icon : 'ui-icon-arrowthick-1-s',
75 callback : function () {
75 callback : function () {
76 IPython.notebook.move_cell_down();
76 IPython.notebook.move_cell_down();
77 }
77 }
78 }
78 }
79 ],'move_up_down');
79 ],'move_up_down');
80
80
81 this.add_buttons_group([
81 this.add_buttons_group([
82 {
82 {
83 id : 'insert_above_b',
83 id : 'insert_above_b',
84 label : 'Insert Cell Above',
84 label : 'Insert Cell Above',
85 icon : 'ui-icon-arrowthickstop-1-n',
85 icon : 'ui-icon-arrowthickstop-1-n',
86 callback : function () {
86 callback : function () {
87 IPython.notebook.insert_cell_above('code');
87 IPython.notebook.insert_cell_above('code');
88 }
88 }
89 },
89 },
90 {
90 {
91 id : 'insert_below_b',
91 id : 'insert_below_b',
92 label : 'Insert Cell Below',
92 label : 'Insert Cell Below',
93 icon : 'ui-icon-arrowthickstop-1-s',
93 icon : 'ui-icon-arrowthickstop-1-s',
94 callback : function () {
94 callback : function () {
95 IPython.notebook.insert_cell_below('code');
95 IPython.notebook.insert_cell_below('code');
96 }
96 }
97 }
97 }
98 ],'insert_above_below');
98 ],'insert_above_below');
99
99
100 this.add_buttons_group([
100 this.add_buttons_group([
101 {
101 {
102 id : 'run_b',
102 id : 'run_b',
103 label : 'Run Cell',
103 label : 'Run Cell',
104 icon : 'ui-icon-play',
104 icon : 'ui-icon-play',
105 callback : function () {
105 callback : function () {
106 IPython.notebook.execute_selected_cell();
106 IPython.notebook.execute_selected_cell();
107 }
107 }
108 },
108 },
109 {
109 {
110 id : 'interrupt_b',
110 id : 'interrupt_b',
111 label : 'Interrupt',
111 label : 'Interrupt',
112 icon : 'ui-icon-stop',
112 icon : 'ui-icon-stop',
113 callback : function () {
113 callback : function () {
114 IPython.notebook.kernel.interrupt();
114 IPython.notebook.kernel.interrupt();
115 }
115 }
116 }
116 }
117 ],'run_int');
117 ],'run_int');
118
118
119
119
120 };
120 };
121
121
122 MainToolBar.prototype.add_drop_down_list = function () {
122 MainToolBar.prototype.add_drop_down_list = function () {
123 var select = $(this.selector)
123 var select = $(this.selector)
124 .append($('<select/>')
124 .append($('<select/>')
125 .attr('id','cell_type')
125 .attr('id','cell_type')
126 .addClass('ui-widget ui-widget-content')
126 .addClass('ui-widget ui-widget-content')
127 .append($('<option/>').attr('value','code').text('Code'))
127 .append($('<option/>').attr('value','code').text('Code'))
128 .append($('<option/>').attr('value','markdown').text('Markdown'))
128 .append($('<option/>').attr('value','markdown').text('Markdown'))
129 .append($('<option/>').attr('value','raw').text('Raw Text'))
129 .append($('<option/>').attr('value','raw').text('Raw Text'))
130 .append($('<option/>').attr('value','heading1').text('Heading 1'))
130 .append($('<option/>').attr('value','heading1').text('Heading 1'))
131 .append($('<option/>').attr('value','heading2').text('Heading 2'))
131 .append($('<option/>').attr('value','heading2').text('Heading 2'))
132 .append($('<option/>').attr('value','heading3').text('Heading 3'))
132 .append($('<option/>').attr('value','heading3').text('Heading 3'))
133 .append($('<option/>').attr('value','heading4').text('Heading 4'))
133 .append($('<option/>').attr('value','heading4').text('Heading 4'))
134 .append($('<option/>').attr('value','heading5').text('Heading 5'))
134 .append($('<option/>').attr('value','heading5').text('Heading 5'))
135 .append($('<option/>').attr('value','heading6').text('Heading 6'))
135 .append($('<option/>').attr('value','heading6').text('Heading 6'))
136 .append($('<option/>').attr('value','heading7').text('Heading 7'))
136 .append($('<option/>').attr('value','heading7').text('Heading 7'))
137 .append($('<option/>').attr('value','heading8').text('Heading 8'))
137 .append($('<option/>').attr('value','heading8').text('Heading 8'))
138 );
138 );
139 };
139 };
140
140
141 MainToolBar.prototype.bind_events = function () {
141 MainToolBar.prototype.bind_events = function () {
142 var that = this;
142 var that = this;
143
143
144 this.element.find('#cell_type').change(function () {
144 this.element.find('#cell_type').change(function () {
145 var cell_type = $(this).val();
145 var cell_type = $(this).val();
146 if (cell_type === 'code') {
146 if (cell_type === 'code') {
147 IPython.notebook.to_code();
147 IPython.notebook.to_code();
148 } else if (cell_type === 'markdown') {
148 } else if (cell_type === 'markdown') {
149 IPython.notebook.to_markdown();
149 IPython.notebook.to_markdown();
150 } else if (cell_type === 'raw') {
150 } else if (cell_type === 'raw') {
151 IPython.notebook.to_raw();
151 IPython.notebook.to_raw();
152 } else if (cell_type === 'heading1') {
152 } else if (cell_type === 'heading1') {
153 IPython.notebook.to_heading(undefined, 1);
153 IPython.notebook.to_heading(undefined, 1);
154 } else if (cell_type === 'heading2') {
154 } else if (cell_type === 'heading2') {
155 IPython.notebook.to_heading(undefined, 2);
155 IPython.notebook.to_heading(undefined, 2);
156 } else if (cell_type === 'heading3') {
156 } else if (cell_type === 'heading3') {
157 IPython.notebook.to_heading(undefined, 3);
157 IPython.notebook.to_heading(undefined, 3);
158 } else if (cell_type === 'heading4') {
158 } else if (cell_type === 'heading4') {
159 IPython.notebook.to_heading(undefined, 4);
159 IPython.notebook.to_heading(undefined, 4);
160 } else if (cell_type === 'heading5') {
160 } else if (cell_type === 'heading5') {
161 IPython.notebook.to_heading(undefined, 5);
161 IPython.notebook.to_heading(undefined, 5);
162 } else if (cell_type === 'heading6') {
162 } else if (cell_type === 'heading6') {
163 IPython.notebook.to_heading(undefined, 6);
163 IPython.notebook.to_heading(undefined, 6);
164 }
164 }
165 });
165 });
166 $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) {
166 $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) {
167 if (data.cell_type === 'heading') {
167 if (data.cell_type === 'heading') {
168 that.element.find('#cell_type').val(data.cell_type+data.level);
168 that.element.find('#cell_type').val(data.cell_type+data.level);
169 } else {
169 } else {
170 that.element.find('#cell_type').val(data.cell_type);
170 that.element.find('#cell_type').val(data.cell_type);
171 }
171 }
172 });
172 });
173 };
173 };
174
174
175 IPython.MainToolBar = MainToolBar;
175 IPython.MainToolBar = MainToolBar;
176
176
177 return IPython;
177 return IPython;
178
178
179 }(IPython));
179 }(IPython));
@@ -1,243 +1,241 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2012 The IPython Development Team
2 // Copyright (C) 2008-2012 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // MathJax utility functions
9 // MathJax utility functions
10 //============================================================================
10 //============================================================================
11
11
12 IPython.namespace('IPython.mathjaxutils');
12 IPython.namespace('IPython.mathjaxutils');
13
13
14 IPython.mathjaxutils = (function (IPython) {
14 IPython.mathjaxutils = (function (IPython) {
15
15
16 var init = function () {
16 var init = function () {
17 if (window.MathJax) {
17 if (window.MathJax) {
18 // MathJax loaded
18 // MathJax loaded
19 MathJax.Hub.Config({
19 MathJax.Hub.Config({
20 TeX: { equationNumbers: { autoNumber: "AMS", useLabelIds: true } },
21 tex2jax: {
20 tex2jax: {
22 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
21 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
23 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
22 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
24 processEnvironments: true
23 processEnvironments: true
25 },
24 },
26 displayAlign: 'left', // Change this to 'center' to center equations.
25 displayAlign: 'left', // Change this to 'center' to center equations.
27 "HTML-CSS": {
26 "HTML-CSS": {
28 styles: {'.MathJax_Display': {"margin": 0}}
27 styles: {'.MathJax_Display': {"margin": 0}}
29 }
28 }
30 });
29 });
30 MathJax.Hub.Configured();
31 } else if (window.mathjax_url != "") {
31 } else if (window.mathjax_url != "") {
32 // Don't have MathJax, but should. Show dialog.
32 // Don't have MathJax, but should. Show dialog.
33 var dialog = $('<div></div>')
33 var dialog = $('<div></div>')
34 .append(
34 .append(
35 $("<p></p>").addClass('dialog').html(
35 $("<p></p>").addClass('dialog').html(
36 "Math/LaTeX rendering will be disabled."
36 "Math/LaTeX rendering will be disabled."
37 )
37 )
38 ).append(
38 ).append(
39 $("<p></p>").addClass('dialog').html(
39 $("<p></p>").addClass('dialog').html(
40 "If you have administrative access to the notebook server and" +
40 "If you have administrative access to the notebook server and" +
41 " a working internet connection, you can install a local copy" +
41 " a working internet connection, you can install a local copy" +
42 " of MathJax for offline use with the following command on the server" +
42 " of MathJax for offline use with the following command on the server" +
43 " at a Python or IPython prompt:"
43 " at a Python or IPython prompt:"
44 )
44 )
45 ).append(
45 ).append(
46 $("<pre></pre>").addClass('dialog').html(
46 $("<pre></pre>").addClass('dialog').html(
47 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
47 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
48 )
48 )
49 ).append(
49 ).append(
50 $("<p></p>").addClass('dialog').html(
50 $("<p></p>").addClass('dialog').html(
51 "This will try to install MathJax into the IPython source directory."
51 "This will try to install MathJax into the IPython source directory."
52 )
52 )
53 ).append(
53 ).append(
54 $("<p></p>").addClass('dialog').html(
54 $("<p></p>").addClass('dialog').html(
55 "If IPython is installed to a location that requires" +
55 "If IPython is installed to a location that requires" +
56 " administrative privileges to write, you will need to make this call as" +
56 " administrative privileges to write, you will need to make this call as" +
57 " an administrator, via 'sudo'."
57 " an administrator, via 'sudo'."
58 )
58 )
59 ).append(
59 ).append(
60 $("<p></p>").addClass('dialog').html(
60 $("<p></p>").addClass('dialog').html(
61 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
61 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
62 )
62 )
63 ).append(
63 ).append(
64 $("<pre></pre>").addClass('dialog').html(
64 $("<pre></pre>").addClass('dialog').html(
65 "$ ipython notebook --no-mathjax"
65 "$ ipython notebook --no-mathjax"
66 )
66 )
67 ).append(
67 ).append(
68 $("<p></p>").addClass('dialog').html(
68 $("<p></p>").addClass('dialog').html(
69 "which will prevent this dialog from appearing."
69 "which will prevent this dialog from appearing."
70 )
70 )
71 ).dialog({
71 ).dialog({
72 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
72 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
73 width: "70%",
73 width: "70%",
74 modal: true,
74 modal: true,
75 })
75 })
76 } else {
76 } else {
77 // No MathJax, but none expected. No dialog.
77 // No MathJax, but none expected. No dialog.
78 };
78 };
79 };
79 };
80
80
81 // Some magic for deferring mathematical expressions to MathJax
81 // Some magic for deferring mathematical expressions to MathJax
82 // by hiding them from the Markdown parser.
82 // by hiding them from the Markdown parser.
83 // Some of the code here is adapted with permission from Davide Cervone
83 // Some of the code here is adapted with permission from Davide Cervone
84 // under the terms of the Apache2 license governing the MathJax project.
84 // under the terms of the Apache2 license governing the MathJax project.
85 // Other minor modifications are also due to StackExchange and are used with
85 // Other minor modifications are also due to StackExchange and are used with
86 // permission.
86 // permission.
87
87
88 var inline = "$"; // the inline math delimiter
88 var inline = "$"; // the inline math delimiter
89 var blocks, start, end, last, braces; // used in searching for math
89 var blocks, start, end, last, braces; // used in searching for math
90 var math; // stores math until pagedown (Markdown parser) is done
90 var math; // stores math until pagedown (Markdown parser) is done
91 var HUB = MathJax.Hub;
92
91
93 // MATHSPLIT contains the pattern for math delimiters and special symbols
92 // MATHSPLIT contains the pattern for math delimiters and special symbols
94 // needed for searching for math in the text input.
93 // needed for searching for math in the text input.
95 var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
94 var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
96
95
97 // The math is in blocks i through j, so
96 // The math is in blocks i through j, so
98 // collect it into one block and clear the others.
97 // collect it into one block and clear the others.
99 // Replace &, <, and > by named entities.
98 // Replace &, <, and > by named entities.
100 // For IE, put <br> at the ends of comments since IE removes \n.
99 // For IE, put <br> at the ends of comments since IE removes \n.
101 // Clear the current math positions and store the index of the
100 // Clear the current math positions and store the index of the
102 // math, then push the math string onto the storage array.
101 // math, then push the math string onto the storage array.
103 // The preProcess function is called on all blocks if it has been passed in
102 // The preProcess function is called on all blocks if it has been passed in
104 var process_math = function (i, j, pre_process) {
103 var process_math = function (i, j, pre_process) {
104 var hub = MathJax.Hub;
105 var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
105 var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
106 .replace(/</g, "&lt;") // use HTML entity for <
106 .replace(/</g, "&lt;") // use HTML entity for <
107 .replace(/>/g, "&gt;") // use HTML entity for >
107 .replace(/>/g, "&gt;") // use HTML entity for >
108 ;
108 ;
109 if (HUB.Browser.isMSIE) {
109 if (hub.Browser.isMSIE) {
110 block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n")
110 block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n")
111 }
111 }
112 while (j > i) {
112 while (j > i) {
113 blocks[j] = "";
113 blocks[j] = "";
114 j--;
114 j--;
115 }
115 }
116 blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later
116 blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later
117 if (pre_process)
117 if (pre_process)
118 block = pre_process(block);
118 block = pre_process(block);
119 math.push(block);
119 math.push(block);
120 start = end = last = null;
120 start = end = last = null;
121 }
121 }
122
122
123 // Break up the text into its component parts and search
123 // Break up the text into its component parts and search
124 // through them for math delimiters, braces, linebreaks, etc.
124 // through them for math delimiters, braces, linebreaks, etc.
125 // Math delimiters must match and braces must balance.
125 // Math delimiters must match and braces must balance.
126 // Don't allow math to pass through a double linebreak
126 // Don't allow math to pass through a double linebreak
127 // (which will be a paragraph).
127 // (which will be a paragraph).
128 //
128 //
129 var remove_math = function (text) {
129 var remove_math = function (text) {
130 if (!window.MathJax) {
131 return text;
132 }
133
130 start = end = last = null; // for tracking math delimiters
134 start = end = last = null; // for tracking math delimiters
131 math = []; // stores math strings for later
135 math = []; // stores math strings for later
132
136
133 // Except for extreme edge cases, this should catch precisely those pieces of the markdown
137 // Except for extreme edge cases, this should catch precisely those pieces of the markdown
134 // source that will later be turned into code spans. While MathJax will not TeXify code spans,
138 // source that will later be turned into code spans. While MathJax will not TeXify code spans,
135 // we still have to consider them at this point; the following issue has happened several times:
139 // we still have to consider them at this point; the following issue has happened several times:
136 //
140 //
137 // `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables.
141 // `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables.
138
142
139 var hasCodeSpans = /`/.test(text),
143 var hasCodeSpans = /`/.test(text),
140 de_tilde;
144 de_tilde;
141 if (hasCodeSpans) {
145 if (hasCodeSpans) {
142 text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) {
146 text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) {
143 return wholematch.replace(/\$/g, "~D");
147 return wholematch.replace(/\$/g, "~D");
144 });
148 });
145 de_tilde = function (text) { return text.replace(/~([TD])/g, function (wholematch, character) { return { T: "~", D: "$" }[character]; }) };
149 de_tilde = function (text) { return text.replace(/~([TD])/g, function (wholematch, character) { return { T: "~", D: "$" }[character]; }) };
146 } else {
150 } else {
147 de_tilde = function (text) { return text; };
151 de_tilde = function (text) { return text; };
148 }
152 }
149
153
150 blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT);
154 blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT);
151
155
152 for (var i = 1, m = blocks.length; i < m; i += 2) {
156 for (var i = 1, m = blocks.length; i < m; i += 2) {
153 var block = blocks[i];
157 var block = blocks[i];
154 if (block.charAt(0) === "@") {
158 if (block.charAt(0) === "@") {
155 //
159 //
156 // Things that look like our math markers will get
160 // Things that look like our math markers will get
157 // stored and then retrieved along with the math.
161 // stored and then retrieved along with the math.
158 //
162 //
159 blocks[i] = "@@" + math.length + "@@";
163 blocks[i] = "@@" + math.length + "@@";
160 math.push(block);
164 math.push(block);
161 }
165 }
162 else if (start) {
166 else if (start) {
163 //
167 //
164 // If we are in math, look for the end delimiter,
168 // If we are in math, look for the end delimiter,
165 // but don't go past double line breaks, and
169 // but don't go past double line breaks, and
166 // and balance braces within the math.
170 // and balance braces within the math.
167 //
171 //
168 if (block === end) {
172 if (block === end) {
169 if (braces) {
173 if (braces) {
170 last = i
174 last = i
171 }
175 }
172 else {
176 else {
173 process_math(start, i, de_tilde)
177 process_math(start, i, de_tilde)
174 }
178 }
175 }
179 }
176 else if (block.match(/\n.*\n/)) {
180 else if (block.match(/\n.*\n/)) {
177 if (last) {
181 if (last) {
178 i = last;
182 i = last;
179 process_math(start, i, de_tilde)
183 process_math(start, i, de_tilde)
180 }
184 }
181 start = end = last = null;
185 start = end = last = null;
182 braces = 0;
186 braces = 0;
183 }
187 }
184 else if (block === "{") {
188 else if (block === "{") {
185 braces++
189 braces++
186 }
190 }
187 else if (block === "}" && braces) {
191 else if (block === "}" && braces) {
188 braces--
192 braces--
189 }
193 }
190 }
194 }
191 else {
195 else {
192 //
196 //
193 // Look for math start delimiters and when
197 // Look for math start delimiters and when
194 // found, set up the end delimiter.
198 // found, set up the end delimiter.
195 //
199 //
196 if (block === inline || block === "$$") {
200 if (block === inline || block === "$$") {
197 start = i;
201 start = i;
198 end = block;
202 end = block;
199 braces = 0;
203 braces = 0;
200 }
204 }
201 else if (block.substr(1, 5) === "begin") {
205 else if (block.substr(1, 5) === "begin") {
202 start = i;
206 start = i;
203 end = "\\end" + block.substr(6);
207 end = "\\end" + block.substr(6);
204 braces = 0;
208 braces = 0;
205 }
209 }
206 }
210 }
207 }
211 }
208 if (last) {
212 if (last) {
209 process_math(start, last, de_tilde)
213 process_math(start, last, de_tilde)
210 }
214 }
211 return de_tilde(blocks.join(""));
215 return de_tilde(blocks.join(""));
212 }
216 }
213
217
214 //
218 //
215 // Put back the math strings that were saved,
219 // Put back the math strings that were saved,
216 // and clear the math array (no need to keep it around).
220 // and clear the math array (no need to keep it around).
217 //
221 //
218 var replace_math = function (text) {
222 var replace_math = function (text) {
223 if (!window.MathJax) {
224 return text;
225 }
226
219 text = text.replace(/@@(\d+)@@/g, function (match, n) {
227 text = text.replace(/@@(\d+)@@/g, function (match, n) {
220 return math[n]
228 return math[n]
221 });
229 });
222 math = null;
230 math = null;
223 return text;
231 return text;
224 }
232 }
225
233
226 var queue_render = function () {
227 // see https://groups.google.com/forum/?fromgroups=#!topic/mathjax-users/cpwy5eCH1ZQ
228 MathJax.Hub.Queue(
229 ["resetEquationNumbers",MathJax.InputJax.TeX],
230 ["PreProcess",MathJax.Hub],
231 ["Reprocess",MathJax.Hub]
232 );
233 }
234
235 return {
234 return {
236 init : init,
235 init : init,
237 process_math : process_math,
236 process_math : process_math,
238 remove_math : remove_math,
237 remove_math : remove_math,
239 replace_math : replace_math,
238 replace_math : replace_math
240 queue_render : queue_render
241 };
239 };
242
240
243 }(IPython)); No newline at end of file
241 }(IPython));
@@ -1,192 +1,198 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // MenuBar
9 // MenuBar
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var MenuBar = function (selector) {
14 var MenuBar = function (selector) {
15 this.selector = selector;
15 this.selector = selector;
16 if (this.selector !== undefined) {
16 if (this.selector !== undefined) {
17 this.element = $(selector);
17 this.element = $(selector);
18 this.style();
18 this.style();
19 this.bind_events();
19 this.bind_events();
20 }
20 }
21 };
21 };
22
22
23
23
24 MenuBar.prototype.style = function () {
24 MenuBar.prototype.style = function () {
25 this.element.addClass('border-box-sizing');
25 this.element.addClass('border-box-sizing');
26 $('ul#menus').menubar({
26 $('ul#menus').menubar({
27 select : function (event, ui) {
27 select : function (event, ui) {
28 // The selected cell loses focus when the menu is entered, so we
28 // The selected cell loses focus when the menu is entered, so we
29 // re-select it upon selection.
29 // re-select it upon selection.
30 var i = IPython.notebook.get_selected_index();
30 var i = IPython.notebook.get_selected_index();
31 IPython.notebook.select(i);
31 IPython.notebook.select(i);
32 }
32 }
33 });
33 });
34 };
34 };
35
35
36
36
37 MenuBar.prototype.bind_events = function () {
37 MenuBar.prototype.bind_events = function () {
38 // File
38 // File
39 this.element.find('#new_notebook').click(function () {
39 this.element.find('#new_notebook').click(function () {
40 window.open($('body').data('baseProjectUrl')+'new');
40 window.open($('body').data('baseProjectUrl')+'new');
41 });
41 });
42 this.element.find('#open_notebook').click(function () {
42 this.element.find('#open_notebook').click(function () {
43 window.open($('body').data('baseProjectUrl'));
43 window.open($('body').data('baseProjectUrl'));
44 });
44 });
45 this.element.find('#rename_notebook').click(function () {
45 this.element.find('#rename_notebook').click(function () {
46 IPython.save_widget.rename_notebook();
46 IPython.save_widget.rename_notebook();
47 });
47 });
48 this.element.find('#copy_notebook').click(function () {
48 this.element.find('#copy_notebook').click(function () {
49 var notebook_id = IPython.notebook.get_notebook_id();
49 var notebook_id = IPython.notebook.get_notebook_id();
50 var url = $('body').data('baseProjectUrl') + notebook_id + '/copy';
50 var url = $('body').data('baseProjectUrl') + notebook_id + '/copy';
51 window.open(url,'_blank');
51 window.open(url,'_blank');
52 return false;
52 return false;
53 });
53 });
54 this.element.find('#save_notebook').click(function () {
54 this.element.find('#save_notebook').click(function () {
55 IPython.notebook.save_notebook();
55 IPython.notebook.save_notebook();
56 });
56 });
57 this.element.find('#download_ipynb').click(function () {
57 this.element.find('#download_ipynb').click(function () {
58 var notebook_id = IPython.notebook.get_notebook_id();
58 var notebook_id = IPython.notebook.get_notebook_id();
59 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
59 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
60 notebook_id + '?format=json';
60 notebook_id + '?format=json';
61 window.open(url,'_newtab');
61 window.open(url,'_newtab');
62 });
62 });
63 this.element.find('#download_py').click(function () {
63 this.element.find('#download_py').click(function () {
64 var notebook_id = IPython.notebook.get_notebook_id();
64 var notebook_id = IPython.notebook.get_notebook_id();
65 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
65 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
66 notebook_id + '?format=py';
66 notebook_id + '?format=py';
67 window.open(url,'_newtab');
67 window.open(url,'_newtab');
68 });
68 });
69 this.element.find('button#print_notebook').click(function () {
69 this.element.find('button#print_notebook').click(function () {
70 IPython.print_widget.print_notebook();
70 IPython.print_widget.print_notebook();
71 });
71 });
72 this.element.find('#kill_and_exit').click(function () {
72 this.element.find('#kill_and_exit').click(function () {
73 IPython.notebook.kernel.kill();
73 IPython.notebook.kernel.kill();
74 setTimeout(function(){window.close();}, 200);
74 setTimeout(function(){window.close();}, 200);
75 });
75 });
76 // Edit
76 // Edit
77 this.element.find('#cut_cell').click(function () {
77 this.element.find('#cut_cell').click(function () {
78 IPython.notebook.cut_cell();
78 IPython.notebook.cut_cell();
79 });
79 });
80 this.element.find('#copy_cell').click(function () {
80 this.element.find('#copy_cell').click(function () {
81 IPython.notebook.copy_cell();
81 IPython.notebook.copy_cell();
82 });
82 });
83 this.element.find('#delete_cell').click(function () {
83 this.element.find('#delete_cell').click(function () {
84 IPython.notebook.delete_cell();
84 IPython.notebook.delete_cell();
85 });
85 });
86 this.element.find('#split_cell').click(function () {
86 this.element.find('#split_cell').click(function () {
87 IPython.notebook.split_cell();
87 IPython.notebook.split_cell();
88 });
88 });
89 this.element.find('#merge_cell_above').click(function () {
89 this.element.find('#merge_cell_above').click(function () {
90 IPython.notebook.merge_cell_above();
90 IPython.notebook.merge_cell_above();
91 });
91 });
92 this.element.find('#merge_cell_below').click(function () {
92 this.element.find('#merge_cell_below').click(function () {
93 IPython.notebook.merge_cell_below();
93 IPython.notebook.merge_cell_below();
94 });
94 });
95 this.element.find('#move_cell_up').click(function () {
95 this.element.find('#move_cell_up').click(function () {
96 IPython.notebook.move_cell_up();
96 IPython.notebook.move_cell_up();
97 });
97 });
98 this.element.find('#move_cell_down').click(function () {
98 this.element.find('#move_cell_down').click(function () {
99 IPython.notebook.move_cell_down();
99 IPython.notebook.move_cell_down();
100 });
100 });
101 this.element.find('#select_previous').click(function () {
101 this.element.find('#select_previous').click(function () {
102 IPython.notebook.select_prev();
102 IPython.notebook.select_prev();
103 });
103 });
104 this.element.find('#select_next').click(function () {
104 this.element.find('#select_next').click(function () {
105 IPython.notebook.select_next();
105 IPython.notebook.select_next();
106 });
106 });
107 // View
107 // View
108 this.element.find('#toggle_header').click(function () {
108 this.element.find('#toggle_header').click(function () {
109 $('div#header').toggle();
109 $('div#header').toggle();
110 IPython.layout_manager.do_resize();
110 IPython.layout_manager.do_resize();
111 });
111 });
112 this.element.find('#toggle_toolbar').click(function () {
112 this.element.find('#toggle_toolbar').click(function () {
113 IPython.toolbar.toggle();
113 IPython.toolbar.toggle();
114 });
114 });
115 // Insert
115 // Insert
116 this.element.find('#insert_cell_above').click(function () {
116 this.element.find('#insert_cell_above').click(function () {
117 IPython.notebook.insert_cell_above('code');
117 IPython.notebook.insert_cell_above('code');
118 });
118 });
119 this.element.find('#insert_cell_below').click(function () {
119 this.element.find('#insert_cell_below').click(function () {
120 IPython.notebook.insert_cell_below('code');
120 IPython.notebook.insert_cell_below('code');
121 });
121 });
122 // Cell
122 // Cell
123 this.element.find('#run_cell').click(function () {
123 this.element.find('#run_cell').click(function () {
124 IPython.notebook.execute_selected_cell();
124 IPython.notebook.execute_selected_cell();
125 });
125 });
126 this.element.find('#run_cell_in_place').click(function () {
126 this.element.find('#run_cell_in_place').click(function () {
127 IPython.notebook.execute_selected_cell({terminal:true});
127 IPython.notebook.execute_selected_cell({terminal:true});
128 });
128 });
129 this.element.find('#run_all_cells').click(function () {
129 this.element.find('#run_all_cells').click(function () {
130 IPython.notebook.execute_all_cells();
130 IPython.notebook.execute_all_cells();
131 });
131 }).attr('title', 'Run all cells in the notebook');
132 this.element.find('#run_all_cells_above').click(function () {
133 IPython.notebook.execute_cells_above();
134 }).attr('title', 'Run all cells above (but not including) this cell');
135 this.element.find('#run_all_cells_below').click(function () {
136 IPython.notebook.execute_cells_below();
137 }).attr('title', 'Run this cell and all cells below it');
132 this.element.find('#to_code').click(function () {
138 this.element.find('#to_code').click(function () {
133 IPython.notebook.to_code();
139 IPython.notebook.to_code();
134 });
140 });
135 this.element.find('#to_markdown').click(function () {
141 this.element.find('#to_markdown').click(function () {
136 IPython.notebook.to_markdown();
142 IPython.notebook.to_markdown();
137 });
143 });
138 this.element.find('#to_raw').click(function () {
144 this.element.find('#to_raw').click(function () {
139 IPython.notebook.to_raw();
145 IPython.notebook.to_raw();
140 });
146 });
141 this.element.find('#to_heading1').click(function () {
147 this.element.find('#to_heading1').click(function () {
142 IPython.notebook.to_heading(undefined, 1);
148 IPython.notebook.to_heading(undefined, 1);
143 });
149 });
144 this.element.find('#to_heading2').click(function () {
150 this.element.find('#to_heading2').click(function () {
145 IPython.notebook.to_heading(undefined, 2);
151 IPython.notebook.to_heading(undefined, 2);
146 });
152 });
147 this.element.find('#to_heading3').click(function () {
153 this.element.find('#to_heading3').click(function () {
148 IPython.notebook.to_heading(undefined, 3);
154 IPython.notebook.to_heading(undefined, 3);
149 });
155 });
150 this.element.find('#to_heading4').click(function () {
156 this.element.find('#to_heading4').click(function () {
151 IPython.notebook.to_heading(undefined, 4);
157 IPython.notebook.to_heading(undefined, 4);
152 });
158 });
153 this.element.find('#to_heading5').click(function () {
159 this.element.find('#to_heading5').click(function () {
154 IPython.notebook.to_heading(undefined, 5);
160 IPython.notebook.to_heading(undefined, 5);
155 });
161 });
156 this.element.find('#to_heading6').click(function () {
162 this.element.find('#to_heading6').click(function () {
157 IPython.notebook.to_heading(undefined, 6);
163 IPython.notebook.to_heading(undefined, 6);
158 });
164 });
159 this.element.find('#toggle_output').click(function () {
165 this.element.find('#toggle_output').click(function () {
160 IPython.notebook.toggle_output();
166 IPython.notebook.toggle_output();
161 });
167 });
162 this.element.find('#collapse_all_output').click(function () {
168 this.element.find('#collapse_all_output').click(function () {
163 IPython.notebook.collapse_all_output();
169 IPython.notebook.collapse_all_output();
164 });
170 });
165 this.element.find('#scroll_all_output').click(function () {
171 this.element.find('#scroll_all_output').click(function () {
166 IPython.notebook.scroll_all_output();
172 IPython.notebook.scroll_all_output();
167 });
173 });
168 this.element.find('#expand_all_output').click(function () {
174 this.element.find('#expand_all_output').click(function () {
169 IPython.notebook.expand_all_output();
175 IPython.notebook.expand_all_output();
170 });
176 });
171 this.element.find('#clear_all_output').click(function () {
177 this.element.find('#clear_all_output').click(function () {
172 IPython.notebook.clear_all_output();
178 IPython.notebook.clear_all_output();
173 });
179 });
174 // Kernel
180 // Kernel
175 this.element.find('#int_kernel').click(function () {
181 this.element.find('#int_kernel').click(function () {
176 IPython.notebook.kernel.interrupt();
182 IPython.notebook.kernel.interrupt();
177 });
183 });
178 this.element.find('#restart_kernel').click(function () {
184 this.element.find('#restart_kernel').click(function () {
179 IPython.notebook.restart_kernel();
185 IPython.notebook.restart_kernel();
180 });
186 });
181 // Help
187 // Help
182 this.element.find('#keyboard_shortcuts').click(function () {
188 this.element.find('#keyboard_shortcuts').click(function () {
183 IPython.quick_help.show_keyboard_shortcuts();
189 IPython.quick_help.show_keyboard_shortcuts();
184 });
190 });
185 };
191 };
186
192
187
193
188 IPython.MenuBar = MenuBar;
194 IPython.MenuBar = MenuBar;
189
195
190 return IPython;
196 return IPython;
191
197
192 }(IPython));
198 }(IPython));
@@ -1,1310 +1,1371 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15 var key = IPython.utils.keycodes;
15 var key = IPython.utils.keycodes;
16
16
17 var Notebook = function (selector) {
17 var Notebook = function (selector) {
18 this.read_only = IPython.read_only;
18 this.read_only = IPython.read_only;
19 this.element = $(selector);
19 this.element = $(selector);
20 this.element.scroll();
20 this.element.scroll();
21 this.element.data("notebook", this);
21 this.element.data("notebook", this);
22 this.next_prompt_number = 1;
22 this.next_prompt_number = 1;
23 this.kernel = null;
23 this.kernel = null;
24 this.clipboard = null;
24 this.clipboard = null;
25 this.undelete_backup = null;
26 this.undelete_index = null;
27 this.undelete_below = false;
25 this.paste_enabled = false;
28 this.paste_enabled = false;
26 this.dirty = false;
29 this.dirty = false;
27 this.metadata = {};
30 this.metadata = {};
28 // single worksheet for now
31 // single worksheet for now
29 this.worksheet_metadata = {};
32 this.worksheet_metadata = {};
30 this.control_key_active = false;
33 this.control_key_active = false;
31 this.notebook_id = null;
34 this.notebook_id = null;
32 this.notebook_name = null;
35 this.notebook_name = null;
33 this.notebook_name_blacklist_re = /[\/\\:]/;
36 this.notebook_name_blacklist_re = /[\/\\:]/;
34 this.nbformat = 3 // Increment this when changing the nbformat
37 this.nbformat = 3 // Increment this when changing the nbformat
35 this.nbformat_minor = 0 // Increment this when changing the nbformat
38 this.nbformat_minor = 0 // Increment this when changing the nbformat
36 this.style();
39 this.style();
37 this.create_elements();
40 this.create_elements();
38 this.bind_events();
41 this.bind_events();
39 };
42 };
40
43
41
44
42 Notebook.prototype.style = function () {
45 Notebook.prototype.style = function () {
43 $('div#notebook').addClass('border-box-sizing');
46 $('div#notebook').addClass('border-box-sizing');
44 };
47 };
45
48
46
49
47 Notebook.prototype.create_elements = function () {
50 Notebook.prototype.create_elements = function () {
48 // We add this end_space div to the end of the notebook div to:
51 // We add this end_space div to the end of the notebook div to:
49 // i) provide a margin between the last cell and the end of the notebook
52 // i) provide a margin between the last cell and the end of the notebook
50 // ii) to prevent the div from scrolling up when the last cell is being
53 // ii) to prevent the div from scrolling up when the last cell is being
51 // edited, but is too low on the page, which browsers will do automatically.
54 // edited, but is too low on the page, which browsers will do automatically.
52 var that = this;
55 var that = this;
53 var end_space = $('<div/>').addClass('end_space').height("30%");
56 var end_space = $('<div/>').addClass('end_space').height("30%");
54 end_space.dblclick(function (e) {
57 end_space.dblclick(function (e) {
55 if (that.read_only) return;
58 if (that.read_only) return;
56 var ncells = that.ncells();
59 var ncells = that.ncells();
57 that.insert_cell_below('code',ncells-1);
60 that.insert_cell_below('code',ncells-1);
58 });
61 });
59 this.element.append(end_space);
62 this.element.append(end_space);
60 $('div#notebook').addClass('border-box-sizing');
63 $('div#notebook').addClass('border-box-sizing');
61 };
64 };
62
65
63
66
64 Notebook.prototype.bind_events = function () {
67 Notebook.prototype.bind_events = function () {
65 var that = this;
68 var that = this;
66
69
67 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
70 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
68 var index = that.find_cell_index(data.cell);
71 var index = that.find_cell_index(data.cell);
69 var new_cell = that.insert_cell_below('code',index);
72 var new_cell = that.insert_cell_below('code',index);
70 new_cell.set_text(data.text);
73 new_cell.set_text(data.text);
71 that.dirty = true;
74 that.dirty = true;
72 });
75 });
73
76
74 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
77 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
75 that.dirty = data.value;
78 that.dirty = data.value;
76 });
79 });
77
80
78 $([IPython.events]).on('select.Cell', function (event, data) {
81 $([IPython.events]).on('select.Cell', function (event, data) {
79 var index = that.find_cell_index(data.cell);
82 var index = that.find_cell_index(data.cell);
80 that.select(index);
83 that.select(index);
81 });
84 });
82
85
83
86
84 $(document).keydown(function (event) {
87 $(document).keydown(function (event) {
85 // console.log(event);
88 // console.log(event);
86 if (that.read_only) return true;
89 if (that.read_only) return true;
87
90
88 // Save (CTRL+S) or (AppleKey+S)
91 // Save (CTRL+S) or (AppleKey+S)
89 //metaKey = applekey on mac
92 //metaKey = applekey on mac
90 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
93 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
91 that.save_notebook();
94 that.save_notebook();
92 event.preventDefault();
95 event.preventDefault();
93 return false;
96 return false;
94 } else if (event.which === key.ESC) {
97 } else if (event.which === key.ESC) {
95 // Intercept escape at highest level to avoid closing
98 // Intercept escape at highest level to avoid closing
96 // websocket connection with firefox
99 // websocket connection with firefox
97 event.preventDefault();
100 event.preventDefault();
98 } else if (event.which === key.SHIFT) {
101 } else if (event.which === key.SHIFT) {
99 // ignore shift keydown
102 // ignore shift keydown
100 return true;
103 return true;
101 }
104 }
102 if (event.which === key.UPARROW && !event.shiftKey) {
105 if (event.which === key.UPARROW && !event.shiftKey) {
103 var cell = that.get_selected_cell();
106 var cell = that.get_selected_cell();
104 if (cell.at_top()) {
107 if (cell.at_top()) {
105 event.preventDefault();
108 event.preventDefault();
106 that.select_prev();
109 that.select_prev();
107 };
110 };
108 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
111 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
109 var cell = that.get_selected_cell();
112 var cell = that.get_selected_cell();
110 if (cell.at_bottom()) {
113 if (cell.at_bottom()) {
111 event.preventDefault();
114 event.preventDefault();
112 that.select_next();
115 that.select_next();
113 };
116 };
114 } else if (event.which === key.ENTER && event.shiftKey) {
117 } else if (event.which === key.ENTER && event.shiftKey) {
115 that.execute_selected_cell();
118 that.execute_selected_cell();
116 return false;
119 return false;
117 } else if (event.which === key.ENTER && event.altKey) {
120 } else if (event.which === key.ENTER && event.altKey) {
118 // Execute code cell, and insert new in place
121 // Execute code cell, and insert new in place
119 that.execute_selected_cell();
122 that.execute_selected_cell();
120 // Only insert a new cell, if we ended up in an already populated cell
123 // Only insert a new cell, if we ended up in an already populated cell
121 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
124 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
122 that.insert_cell_above('code');
125 that.insert_cell_above('code');
123 }
126 }
124 return false;
127 return false;
125 } else if (event.which === key.ENTER && event.ctrlKey) {
128 } else if (event.which === key.ENTER && event.ctrlKey) {
126 that.execute_selected_cell({terminal:true});
129 that.execute_selected_cell({terminal:true});
127 return false;
130 return false;
128 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
131 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
129 that.control_key_active = true;
132 that.control_key_active = true;
130 return false;
133 return false;
131 } else if (event.which === 88 && that.control_key_active) {
134 } else if (event.which === 88 && that.control_key_active) {
132 // Cut selected cell = x
135 // Cut selected cell = x
133 that.cut_cell();
136 that.cut_cell();
134 that.control_key_active = false;
137 that.control_key_active = false;
135 return false;
138 return false;
136 } else if (event.which === 67 && that.control_key_active) {
139 } else if (event.which === 67 && that.control_key_active) {
137 // Copy selected cell = c
140 // Copy selected cell = c
138 that.copy_cell();
141 that.copy_cell();
139 that.control_key_active = false;
142 that.control_key_active = false;
140 return false;
143 return false;
141 } else if (event.which === 86 && that.control_key_active) {
144 } else if (event.which === 86 && that.control_key_active) {
142 // Paste selected cell = v
145 // Paste below selected cell = v
143 that.paste_cell();
146 that.paste_cell_below();
144 that.control_key_active = false;
147 that.control_key_active = false;
145 return false;
148 return false;
146 } else if (event.which === 68 && that.control_key_active) {
149 } else if (event.which === 68 && that.control_key_active) {
147 // Delete selected cell = d
150 // Delete selected cell = d
148 that.delete_cell();
151 that.delete_cell();
149 that.control_key_active = false;
152 that.control_key_active = false;
150 return false;
153 return false;
151 } else if (event.which === 65 && that.control_key_active) {
154 } else if (event.which === 65 && that.control_key_active) {
152 // Insert code cell above selected = a
155 // Insert code cell above selected = a
153 that.insert_cell_above('code');
156 that.insert_cell_above('code');
154 that.control_key_active = false;
157 that.control_key_active = false;
155 return false;
158 return false;
156 } else if (event.which === 66 && that.control_key_active) {
159 } else if (event.which === 66 && that.control_key_active) {
157 // Insert code cell below selected = b
160 // Insert code cell below selected = b
158 that.insert_cell_below('code');
161 that.insert_cell_below('code');
159 that.control_key_active = false;
162 that.control_key_active = false;
160 return false;
163 return false;
161 } else if (event.which === 89 && that.control_key_active) {
164 } else if (event.which === 89 && that.control_key_active) {
162 // To code = y
165 // To code = y
163 that.to_code();
166 that.to_code();
164 that.control_key_active = false;
167 that.control_key_active = false;
165 return false;
168 return false;
166 } else if (event.which === 77 && that.control_key_active) {
169 } else if (event.which === 77 && that.control_key_active) {
167 // To markdown = m
170 // To markdown = m
168 that.to_markdown();
171 that.to_markdown();
169 that.control_key_active = false;
172 that.control_key_active = false;
170 return false;
173 return false;
171 } else if (event.which === 84 && that.control_key_active) {
174 } else if (event.which === 84 && that.control_key_active) {
172 // To Raw = t
175 // To Raw = t
173 that.to_raw();
176 that.to_raw();
174 that.control_key_active = false;
177 that.control_key_active = false;
175 return false;
178 return false;
176 } else if (event.which === 49 && that.control_key_active) {
179 } else if (event.which === 49 && that.control_key_active) {
177 // To Heading 1 = 1
180 // To Heading 1 = 1
178 that.to_heading(undefined, 1);
181 that.to_heading(undefined, 1);
179 that.control_key_active = false;
182 that.control_key_active = false;
180 return false;
183 return false;
181 } else if (event.which === 50 && that.control_key_active) {
184 } else if (event.which === 50 && that.control_key_active) {
182 // To Heading 2 = 2
185 // To Heading 2 = 2
183 that.to_heading(undefined, 2);
186 that.to_heading(undefined, 2);
184 that.control_key_active = false;
187 that.control_key_active = false;
185 return false;
188 return false;
186 } else if (event.which === 51 && that.control_key_active) {
189 } else if (event.which === 51 && that.control_key_active) {
187 // To Heading 3 = 3
190 // To Heading 3 = 3
188 that.to_heading(undefined, 3);
191 that.to_heading(undefined, 3);
189 that.control_key_active = false;
192 that.control_key_active = false;
190 return false;
193 return false;
191 } else if (event.which === 52 && that.control_key_active) {
194 } else if (event.which === 52 && that.control_key_active) {
192 // To Heading 4 = 4
195 // To Heading 4 = 4
193 that.to_heading(undefined, 4);
196 that.to_heading(undefined, 4);
194 that.control_key_active = false;
197 that.control_key_active = false;
195 return false;
198 return false;
196 } else if (event.which === 53 && that.control_key_active) {
199 } else if (event.which === 53 && that.control_key_active) {
197 // To Heading 5 = 5
200 // To Heading 5 = 5
198 that.to_heading(undefined, 5);
201 that.to_heading(undefined, 5);
199 that.control_key_active = false;
202 that.control_key_active = false;
200 return false;
203 return false;
201 } else if (event.which === 54 && that.control_key_active) {
204 } else if (event.which === 54 && that.control_key_active) {
202 // To Heading 6 = 6
205 // To Heading 6 = 6
203 that.to_heading(undefined, 6);
206 that.to_heading(undefined, 6);
204 that.control_key_active = false;
207 that.control_key_active = false;
205 return false;
208 return false;
206 } else if (event.which === 79 && that.control_key_active) {
209 } else if (event.which === 79 && that.control_key_active) {
207 // Toggle output = o
210 // Toggle output = o
208 if (event.shiftKey){
211 if (event.shiftKey){
209 that.toggle_output_scroll();
212 that.toggle_output_scroll();
210 } else {
213 } else {
211 that.toggle_output();
214 that.toggle_output();
212 }
215 }
213 that.control_key_active = false;
216 that.control_key_active = false;
214 return false;
217 return false;
215 } else if (event.which === 83 && that.control_key_active) {
218 } else if (event.which === 83 && that.control_key_active) {
216 // Save notebook = s
219 // Save notebook = s
217 that.save_notebook();
220 that.save_notebook();
218 that.control_key_active = false;
221 that.control_key_active = false;
219 return false;
222 return false;
220 } else if (event.which === 74 && that.control_key_active) {
223 } else if (event.which === 74 && that.control_key_active) {
221 // Move cell down = j
224 // Move cell down = j
222 that.move_cell_down();
225 that.move_cell_down();
223 that.control_key_active = false;
226 that.control_key_active = false;
224 return false;
227 return false;
225 } else if (event.which === 75 && that.control_key_active) {
228 } else if (event.which === 75 && that.control_key_active) {
226 // Move cell up = k
229 // Move cell up = k
227 that.move_cell_up();
230 that.move_cell_up();
228 that.control_key_active = false;
231 that.control_key_active = false;
229 return false;
232 return false;
230 } else if (event.which === 80 && that.control_key_active) {
233 } else if (event.which === 80 && that.control_key_active) {
231 // Select previous = p
234 // Select previous = p
232 that.select_prev();
235 that.select_prev();
233 that.control_key_active = false;
236 that.control_key_active = false;
234 return false;
237 return false;
235 } else if (event.which === 78 && that.control_key_active) {
238 } else if (event.which === 78 && that.control_key_active) {
236 // Select next = n
239 // Select next = n
237 that.select_next();
240 that.select_next();
238 that.control_key_active = false;
241 that.control_key_active = false;
239 return false;
242 return false;
240 } else if (event.which === 76 && that.control_key_active) {
243 } else if (event.which === 76 && that.control_key_active) {
241 // Toggle line numbers = l
244 // Toggle line numbers = l
242 that.cell_toggle_line_numbers();
245 that.cell_toggle_line_numbers();
243 that.control_key_active = false;
246 that.control_key_active = false;
244 return false;
247 return false;
245 } else if (event.which === 73 && that.control_key_active) {
248 } else if (event.which === 73 && that.control_key_active) {
246 // Interrupt kernel = i
249 // Interrupt kernel = i
247 that.kernel.interrupt();
250 that.kernel.interrupt();
248 that.control_key_active = false;
251 that.control_key_active = false;
249 return false;
252 return false;
250 } else if (event.which === 190 && that.control_key_active) {
253 } else if (event.which === 190 && that.control_key_active) {
251 // Restart kernel = . # matches qt console
254 // Restart kernel = . # matches qt console
252 that.restart_kernel();
255 that.restart_kernel();
253 that.control_key_active = false;
256 that.control_key_active = false;
254 return false;
257 return false;
255 } else if (event.which === 72 && that.control_key_active) {
258 } else if (event.which === 72 && that.control_key_active) {
256 // Show keyboard shortcuts = h
259 // Show keyboard shortcuts = h
257 IPython.quick_help.show_keyboard_shortcuts();
260 IPython.quick_help.show_keyboard_shortcuts();
258 that.control_key_active = false;
261 that.control_key_active = false;
259 return false;
262 return false;
263 } else if (event.which === 90 && that.control_key_active) {
264 // Undo last cell delete = z
265 that.undelete();
266 that.control_key_active = false;
267 return false;
260 } else if (that.control_key_active) {
268 } else if (that.control_key_active) {
261 that.control_key_active = false;
269 that.control_key_active = false;
262 return true;
270 return true;
263 };
271 };
264 return true;
272 return true;
265 });
273 });
266
274
267 var collapse_time = function(time){
275 var collapse_time = function(time){
268 var app_height = $('div#main_app').height(); // content height
276 var app_height = $('div#main_app').height(); // content height
269 var splitter_height = $('div#pager_splitter').outerHeight(true);
277 var splitter_height = $('div#pager_splitter').outerHeight(true);
270 var new_height = app_height - splitter_height;
278 var new_height = app_height - splitter_height;
271 that.element.animate({height : new_height + 'px'}, time);
279 that.element.animate({height : new_height + 'px'}, time);
272 }
280 }
273
281
274 this.element.bind('collapse_pager', function (event,extrap) {
282 this.element.bind('collapse_pager', function (event,extrap) {
275 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
283 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
276 collapse_time(time);
284 collapse_time(time);
277 });
285 });
278
286
279 var expand_time = function(time) {
287 var expand_time = function(time) {
280 var app_height = $('div#main_app').height(); // content height
288 var app_height = $('div#main_app').height(); // content height
281 var splitter_height = $('div#pager_splitter').outerHeight(true);
289 var splitter_height = $('div#pager_splitter').outerHeight(true);
282 var pager_height = $('div#pager').outerHeight(true);
290 var pager_height = $('div#pager').outerHeight(true);
283 var new_height = app_height - pager_height - splitter_height;
291 var new_height = app_height - pager_height - splitter_height;
284 that.element.animate({height : new_height + 'px'}, time);
292 that.element.animate({height : new_height + 'px'}, time);
285 }
293 }
286
294
287 this.element.bind('expand_pager', function (event, extrap) {
295 this.element.bind('expand_pager', function (event, extrap) {
288 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
296 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
289 expand_time(time);
297 expand_time(time);
290 });
298 });
291
299
292 $(window).bind('beforeunload', function () {
300 $(window).bind('beforeunload', function () {
293 // TODO: Make killing the kernel configurable.
301 // TODO: Make killing the kernel configurable.
294 var kill_kernel = false;
302 var kill_kernel = false;
295 if (kill_kernel) {
303 if (kill_kernel) {
296 that.kernel.kill();
304 that.kernel.kill();
297 }
305 }
298 if (that.dirty && ! that.read_only) {
306 if (that.dirty && ! that.read_only) {
299 return "You have unsaved changes that will be lost if you leave this page.";
307 return "You have unsaved changes that will be lost if you leave this page.";
300 };
308 };
301 // Null is the *only* return value that will make the browser not
309 // Null is the *only* return value that will make the browser not
302 // pop up the "don't leave" dialog.
310 // pop up the "don't leave" dialog.
303 return null;
311 return null;
304 });
312 });
305 };
313 };
306
314
307 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
315 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
308 var cells = this.get_cells();
316 var cells = this.get_cells();
309 var time = time || 0;
317 var time = time || 0;
310 cell_number = Math.min(cells.length-1,cell_number);
318 cell_number = Math.min(cells.length-1,cell_number);
311 cell_number = Math.max(0 ,cell_number);
319 cell_number = Math.max(0 ,cell_number);
312 scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
320 scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
313 this.element.animate({scrollTop:scroll_value}, time);
321 this.element.animate({scrollTop:scroll_value}, time);
314 return scroll_value;
322 return scroll_value;
315 };
323 };
316
324
317
325
318 Notebook.prototype.scroll_to_bottom = function () {
326 Notebook.prototype.scroll_to_bottom = function () {
319 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
327 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
320 };
328 };
321
329
322
330
323 Notebook.prototype.scroll_to_top = function () {
331 Notebook.prototype.scroll_to_top = function () {
324 this.element.animate({scrollTop:0}, 0);
332 this.element.animate({scrollTop:0}, 0);
325 };
333 };
326
334
327
335
328 // Cell indexing, retrieval, etc.
336 // Cell indexing, retrieval, etc.
329
337
330 Notebook.prototype.get_cell_elements = function () {
338 Notebook.prototype.get_cell_elements = function () {
331 return this.element.children("div.cell");
339 return this.element.children("div.cell");
332 };
340 };
333
341
334
342
335 Notebook.prototype.get_cell_element = function (index) {
343 Notebook.prototype.get_cell_element = function (index) {
336 var result = null;
344 var result = null;
337 var e = this.get_cell_elements().eq(index);
345 var e = this.get_cell_elements().eq(index);
338 if (e.length !== 0) {
346 if (e.length !== 0) {
339 result = e;
347 result = e;
340 }
348 }
341 return result;
349 return result;
342 };
350 };
343
351
344
352
345 Notebook.prototype.ncells = function (cell) {
353 Notebook.prototype.ncells = function (cell) {
346 return this.get_cell_elements().length;
354 return this.get_cell_elements().length;
347 };
355 };
348
356
349
357
350 // TODO: we are often calling cells as cells()[i], which we should optimize
358 // TODO: we are often calling cells as cells()[i], which we should optimize
351 // to cells(i) or a new method.
359 // to cells(i) or a new method.
352 Notebook.prototype.get_cells = function () {
360 Notebook.prototype.get_cells = function () {
353 return this.get_cell_elements().toArray().map(function (e) {
361 return this.get_cell_elements().toArray().map(function (e) {
354 return $(e).data("cell");
362 return $(e).data("cell");
355 });
363 });
356 };
364 };
357
365
358
366
359 Notebook.prototype.get_cell = function (index) {
367 Notebook.prototype.get_cell = function (index) {
360 var result = null;
368 var result = null;
361 var ce = this.get_cell_element(index);
369 var ce = this.get_cell_element(index);
362 if (ce !== null) {
370 if (ce !== null) {
363 result = ce.data('cell');
371 result = ce.data('cell');
364 }
372 }
365 return result;
373 return result;
366 }
374 }
367
375
368
376
369 Notebook.prototype.get_next_cell = function (cell) {
377 Notebook.prototype.get_next_cell = function (cell) {
370 var result = null;
378 var result = null;
371 var index = this.find_cell_index(cell);
379 var index = this.find_cell_index(cell);
372 if (index !== null && index < this.ncells()) {
380 if (index !== null && index < this.ncells()) {
373 result = this.get_cell(index+1);
381 result = this.get_cell(index+1);
374 }
382 }
375 return result;
383 return result;
376 }
384 }
377
385
378
386
379 Notebook.prototype.get_prev_cell = function (cell) {
387 Notebook.prototype.get_prev_cell = function (cell) {
380 var result = null;
388 var result = null;
381 var index = this.find_cell_index(cell);
389 var index = this.find_cell_index(cell);
382 if (index !== null && index > 1) {
390 if (index !== null && index > 1) {
383 result = this.get_cell(index-1);
391 result = this.get_cell(index-1);
384 }
392 }
385 return result;
393 return result;
386 }
394 }
387
395
388 Notebook.prototype.find_cell_index = function (cell) {
396 Notebook.prototype.find_cell_index = function (cell) {
389 var result = null;
397 var result = null;
390 this.get_cell_elements().filter(function (index) {
398 this.get_cell_elements().filter(function (index) {
391 if ($(this).data("cell") === cell) {
399 if ($(this).data("cell") === cell) {
392 result = index;
400 result = index;
393 };
401 };
394 });
402 });
395 return result;
403 return result;
396 };
404 };
397
405
398
406
399 Notebook.prototype.index_or_selected = function (index) {
407 Notebook.prototype.index_or_selected = function (index) {
400 var i;
408 var i;
401 if (index === undefined || index === null) {
409 if (index === undefined || index === null) {
402 i = this.get_selected_index();
410 i = this.get_selected_index();
403 if (i === null) {
411 if (i === null) {
404 i = 0;
412 i = 0;
405 }
413 }
406 } else {
414 } else {
407 i = index;
415 i = index;
408 }
416 }
409 return i;
417 return i;
410 };
418 };
411
419
412
420
413 Notebook.prototype.get_selected_cell = function () {
421 Notebook.prototype.get_selected_cell = function () {
414 var index = this.get_selected_index();
422 var index = this.get_selected_index();
415 return this.get_cell(index);
423 return this.get_cell(index);
416 };
424 };
417
425
418
426
419 Notebook.prototype.is_valid_cell_index = function (index) {
427 Notebook.prototype.is_valid_cell_index = function (index) {
420 if (index !== null && index >= 0 && index < this.ncells()) {
428 if (index !== null && index >= 0 && index < this.ncells()) {
421 return true;
429 return true;
422 } else {
430 } else {
423 return false;
431 return false;
424 };
432 };
425 }
433 }
426
434
427 Notebook.prototype.get_selected_index = function () {
435 Notebook.prototype.get_selected_index = function () {
428 var result = null;
436 var result = null;
429 this.get_cell_elements().filter(function (index) {
437 this.get_cell_elements().filter(function (index) {
430 if ($(this).data("cell").selected === true) {
438 if ($(this).data("cell").selected === true) {
431 result = index;
439 result = index;
432 };
440 };
433 });
441 });
434 return result;
442 return result;
435 };
443 };
436
444
437
445
438 // Cell selection.
446 // Cell selection.
439
447
440 Notebook.prototype.select = function (index) {
448 Notebook.prototype.select = function (index) {
441 if (index !== undefined && index >= 0 && index < this.ncells()) {
449 if (index !== undefined && index >= 0 && index < this.ncells()) {
442 sindex = this.get_selected_index()
450 sindex = this.get_selected_index()
443 if (sindex !== null && index !== sindex) {
451 if (sindex !== null && index !== sindex) {
444 this.get_cell(sindex).unselect();
452 this.get_cell(sindex).unselect();
445 };
453 };
446 var cell = this.get_cell(index)
454 var cell = this.get_cell(index)
447 cell.select();
455 cell.select();
448 if (cell.cell_type === 'heading') {
456 if (cell.cell_type === 'heading') {
449 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
457 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
450 {'cell_type':cell.cell_type,level:cell.level}
458 {'cell_type':cell.cell_type,level:cell.level}
451 );
459 );
452 } else {
460 } else {
453 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
461 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
454 {'cell_type':cell.cell_type}
462 {'cell_type':cell.cell_type}
455 );
463 );
456 };
464 };
457 };
465 };
458 return this;
466 return this;
459 };
467 };
460
468
461
469
462 Notebook.prototype.select_next = function () {
470 Notebook.prototype.select_next = function () {
463 var index = this.get_selected_index();
471 var index = this.get_selected_index();
464 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
472 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
465 this.select(index+1);
473 this.select(index+1);
466 };
474 };
467 return this;
475 return this;
468 };
476 };
469
477
470
478
471 Notebook.prototype.select_prev = function () {
479 Notebook.prototype.select_prev = function () {
472 var index = this.get_selected_index();
480 var index = this.get_selected_index();
473 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
481 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
474 this.select(index-1);
482 this.select(index-1);
475 };
483 };
476 return this;
484 return this;
477 };
485 };
478
486
479
487
480 // Cell movement
488 // Cell movement
481
489
482 Notebook.prototype.move_cell_up = function (index) {
490 Notebook.prototype.move_cell_up = function (index) {
483 var i = this.index_or_selected();
491 var i = this.index_or_selected();
484 if (i !== null && i < this.ncells() && i > 0) {
492 if (i !== null && i < this.ncells() && i > 0) {
485 var pivot = this.get_cell_element(i-1);
493 var pivot = this.get_cell_element(i-1);
486 var tomove = this.get_cell_element(i);
494 var tomove = this.get_cell_element(i);
487 if (pivot !== null && tomove !== null) {
495 if (pivot !== null && tomove !== null) {
488 tomove.detach();
496 tomove.detach();
489 pivot.before(tomove);
497 pivot.before(tomove);
490 this.select(i-1);
498 this.select(i-1);
491 };
499 };
492 };
500 };
493 this.dirty = true;
501 this.dirty = true;
494 return this;
502 return this;
495 };
503 };
496
504
497
505
498 Notebook.prototype.move_cell_down = function (index) {
506 Notebook.prototype.move_cell_down = function (index) {
499 var i = this.index_or_selected();
507 var i = this.index_or_selected();
500 if (i !== null && i < (this.ncells()-1) && i >= 0) {
508 if (i !== null && i < (this.ncells()-1) && i >= 0) {
501 var pivot = this.get_cell_element(i+1);
509 var pivot = this.get_cell_element(i+1);
502 var tomove = this.get_cell_element(i);
510 var tomove = this.get_cell_element(i);
503 if (pivot !== null && tomove !== null) {
511 if (pivot !== null && tomove !== null) {
504 tomove.detach();
512 tomove.detach();
505 pivot.after(tomove);
513 pivot.after(tomove);
506 this.select(i+1);
514 this.select(i+1);
507 };
515 };
508 };
516 };
509 this.dirty = true;
517 this.dirty = true;
510 return this;
518 return this;
511 };
519 };
512
520
513
521
514 Notebook.prototype.sort_cells = function () {
522 Notebook.prototype.sort_cells = function () {
515 // This is not working right now. Calling this will actually crash
523 // This is not working right now. Calling this will actually crash
516 // the browser. I think there is an infinite loop in here...
524 // the browser. I think there is an infinite loop in here...
517 var ncells = this.ncells();
525 var ncells = this.ncells();
518 var sindex = this.get_selected_index();
526 var sindex = this.get_selected_index();
519 var swapped;
527 var swapped;
520 do {
528 do {
521 swapped = false;
529 swapped = false;
522 for (var i=1; i<ncells; i++) {
530 for (var i=1; i<ncells; i++) {
523 current = this.get_cell(i);
531 current = this.get_cell(i);
524 previous = this.get_cell(i-1);
532 previous = this.get_cell(i-1);
525 if (previous.input_prompt_number > current.input_prompt_number) {
533 if (previous.input_prompt_number > current.input_prompt_number) {
526 this.move_cell_up(i);
534 this.move_cell_up(i);
527 swapped = true;
535 swapped = true;
528 };
536 };
529 };
537 };
530 } while (swapped);
538 } while (swapped);
531 this.select(sindex);
539 this.select(sindex);
532 return this;
540 return this;
533 };
541 };
534
542
535 // Insertion, deletion.
543 // Insertion, deletion.
536
544
537 Notebook.prototype.delete_cell = function (index) {
545 Notebook.prototype.delete_cell = function (index) {
538 var i = this.index_or_selected(index);
546 var i = this.index_or_selected(index);
547 var cell = this.get_selected_cell();
548 this.undelete_backup = cell.toJSON();
539 if (this.is_valid_cell_index(i)) {
549 if (this.is_valid_cell_index(i)) {
540 var ce = this.get_cell_element(i);
550 var ce = this.get_cell_element(i);
541 ce.remove();
551 ce.remove();
542 if (i === (this.ncells())) {
552 if (i === (this.ncells())) {
543 this.select(i-1);
553 this.select(i-1);
554 this.undelete_index = i - 1;
555 this.undelete_below = true;
544 } else {
556 } else {
545 this.select(i);
557 this.select(i);
558 this.undelete_index = i;
559 this.undelete_below = false;
546 };
560 };
547 this.dirty = true;
561 this.dirty = true;
548 };
562 };
549 return this;
563 return this;
550 };
564 };
551
565
552
566
553 Notebook.prototype.insert_cell_at_bottom = function (type){
567 Notebook.prototype.insert_cell_at_bottom = function (type){
554 var len = this.ncells();
568 var len = this.ncells();
555 return this.insert_cell_below(type,len-1);
569 return this.insert_cell_below(type,len-1);
556 }
570 }
557
571
558 Notebook.prototype.insert_cell_below = function (type, index) {
572 Notebook.prototype.insert_cell_below = function (type, index) {
559 // type = ('code','html','markdown')
573 // type = ('code','html','markdown')
560 // index = cell index or undefined to insert below selected
574 // index = cell index or undefined to insert below selected
561 index = this.index_or_selected(index);
575 index = this.index_or_selected(index);
562 var cell = null;
576 var cell = null;
577 // This is intentionally < rather than <= for the sake of more
578 // sensible behavior in some cases.
579 if (this.undelete_index !== null && index < this.undelete_index) {
580 this.undelete_index = this.undelete_index + 1;
581 }
563 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
582 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
564 if (type === 'code') {
583 if (type === 'code') {
565 cell = new IPython.CodeCell(this.kernel);
584 cell = new IPython.CodeCell(this.kernel);
566 cell.set_input_prompt();
585 cell.set_input_prompt();
567 } else if (type === 'markdown') {
586 } else if (type === 'markdown') {
568 cell = new IPython.MarkdownCell();
587 cell = new IPython.MarkdownCell();
569 } else if (type === 'html') {
588 } else if (type === 'html') {
570 cell = new IPython.HTMLCell();
589 cell = new IPython.HTMLCell();
571 } else if (type === 'raw') {
590 } else if (type === 'raw') {
572 cell = new IPython.RawCell();
591 cell = new IPython.RawCell();
573 } else if (type === 'heading') {
592 } else if (type === 'heading') {
574 cell = new IPython.HeadingCell();
593 cell = new IPython.HeadingCell();
575 };
594 };
576 if (cell !== null) {
595 if (cell !== null) {
577 if (this.ncells() === 0) {
596 if (this.ncells() === 0) {
578 this.element.find('div.end_space').before(cell.element);
597 this.element.find('div.end_space').before(cell.element);
579 } else if (this.is_valid_cell_index(index)) {
598 } else if (this.is_valid_cell_index(index)) {
580 this.get_cell_element(index).after(cell.element);
599 this.get_cell_element(index).after(cell.element);
581 };
600 };
582 cell.render();
601 cell.render();
583 this.select(this.find_cell_index(cell));
602 this.select(this.find_cell_index(cell));
584 this.dirty = true;
603 this.dirty = true;
585 return cell;
604 return cell;
586 };
605 };
587 };
606 };
588 return cell;
607 return cell;
589 };
608 };
590
609
591
610
592 Notebook.prototype.insert_cell_above = function (type, index) {
611 Notebook.prototype.insert_cell_above = function (type, index) {
593 // type = ('code','html','markdown')
612 // type = ('code','html','markdown')
594 // index = cell index or undefined to insert above selected
613 // index = cell index or undefined to insert above selected
595 index = this.index_or_selected(index);
614 index = this.index_or_selected(index);
596 var cell = null;
615 var cell = null;
616 if (this.undelete_index !== null && index <= this.undelete_index) {
617 this.undelete_index = this.undelete_index + 1;
618 }
597 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
619 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
598 if (type === 'code') {
620 if (type === 'code') {
599 cell = new IPython.CodeCell(this.kernel);
621 cell = new IPython.CodeCell(this.kernel);
600 cell.set_input_prompt();
622 cell.set_input_prompt();
601 } else if (type === 'markdown') {
623 } else if (type === 'markdown') {
602 cell = new IPython.MarkdownCell();
624 cell = new IPython.MarkdownCell();
603 } else if (type === 'html') {
625 } else if (type === 'html') {
604 cell = new IPython.HTMLCell();
626 cell = new IPython.HTMLCell();
605 } else if (type === 'raw') {
627 } else if (type === 'raw') {
606 cell = new IPython.RawCell();
628 cell = new IPython.RawCell();
607 } else if (type === 'heading') {
629 } else if (type === 'heading') {
608 cell = new IPython.HeadingCell();
630 cell = new IPython.HeadingCell();
609 };
631 };
610 if (cell !== null) {
632 if (cell !== null) {
611 if (this.ncells() === 0) {
633 if (this.ncells() === 0) {
612 this.element.find('div.end_space').before(cell.element);
634 this.element.find('div.end_space').before(cell.element);
613 } else if (this.is_valid_cell_index(index)) {
635 } else if (this.is_valid_cell_index(index)) {
614 this.get_cell_element(index).before(cell.element);
636 this.get_cell_element(index).before(cell.element);
615 };
637 };
616 cell.render();
638 cell.render();
617 this.select(this.find_cell_index(cell));
639 this.select(this.find_cell_index(cell));
618 this.dirty = true;
640 this.dirty = true;
619 return cell;
641 return cell;
620 };
642 };
621 };
643 };
622 return cell;
644 return cell;
623 };
645 };
624
646
625
647
626 Notebook.prototype.to_code = function (index) {
648 Notebook.prototype.to_code = function (index) {
627 var i = this.index_or_selected(index);
649 var i = this.index_or_selected(index);
628 if (this.is_valid_cell_index(i)) {
650 if (this.is_valid_cell_index(i)) {
629 var source_element = this.get_cell_element(i);
651 var source_element = this.get_cell_element(i);
630 var source_cell = source_element.data("cell");
652 var source_cell = source_element.data("cell");
631 if (!(source_cell instanceof IPython.CodeCell)) {
653 if (!(source_cell instanceof IPython.CodeCell)) {
632 target_cell = this.insert_cell_below('code',i);
654 target_cell = this.insert_cell_below('code',i);
633 var text = source_cell.get_text();
655 var text = source_cell.get_text();
634 if (text === source_cell.placeholder) {
656 if (text === source_cell.placeholder) {
635 text = '';
657 text = '';
636 }
658 }
637 target_cell.set_text(text);
659 target_cell.set_text(text);
638 // make this value the starting point, so that we can only undo
660 // make this value the starting point, so that we can only undo
639 // to this state, instead of a blank cell
661 // to this state, instead of a blank cell
640 target_cell.code_mirror.clearHistory();
662 target_cell.code_mirror.clearHistory();
641 source_element.remove();
663 source_element.remove();
642 this.dirty = true;
664 this.dirty = true;
643 };
665 };
644 };
666 };
645 };
667 };
646
668
647
669
648 Notebook.prototype.to_markdown = function (index) {
670 Notebook.prototype.to_markdown = function (index) {
649 var i = this.index_or_selected(index);
671 var i = this.index_or_selected(index);
650 if (this.is_valid_cell_index(i)) {
672 if (this.is_valid_cell_index(i)) {
651 var source_element = this.get_cell_element(i);
673 var source_element = this.get_cell_element(i);
652 var source_cell = source_element.data("cell");
674 var source_cell = source_element.data("cell");
653 if (!(source_cell instanceof IPython.MarkdownCell)) {
675 if (!(source_cell instanceof IPython.MarkdownCell)) {
654 target_cell = this.insert_cell_below('markdown',i);
676 target_cell = this.insert_cell_below('markdown',i);
655 var text = source_cell.get_text();
677 var text = source_cell.get_text();
656 if (text === source_cell.placeholder) {
678 if (text === source_cell.placeholder) {
657 text = '';
679 text = '';
658 };
680 };
659 // The edit must come before the set_text.
681 // The edit must come before the set_text.
660 target_cell.edit();
682 target_cell.edit();
661 target_cell.set_text(text);
683 target_cell.set_text(text);
662 // make this value the starting point, so that we can only undo
684 // make this value the starting point, so that we can only undo
663 // to this state, instead of a blank cell
685 // to this state, instead of a blank cell
664 target_cell.code_mirror.clearHistory();
686 target_cell.code_mirror.clearHistory();
665 source_element.remove();
687 source_element.remove();
666 this.dirty = true;
688 this.dirty = true;
667 };
689 };
668 };
690 };
669 };
691 };
670
692
671
693
672 Notebook.prototype.to_html = function (index) {
694 Notebook.prototype.to_html = function (index) {
673 var i = this.index_or_selected(index);
695 var i = this.index_or_selected(index);
674 if (this.is_valid_cell_index(i)) {
696 if (this.is_valid_cell_index(i)) {
675 var source_element = this.get_cell_element(i);
697 var source_element = this.get_cell_element(i);
676 var source_cell = source_element.data("cell");
698 var source_cell = source_element.data("cell");
677 var target_cell = null;
699 var target_cell = null;
678 if (!(source_cell instanceof IPython.HTMLCell)) {
700 if (!(source_cell instanceof IPython.HTMLCell)) {
679 target_cell = this.insert_cell_below('html',i);
701 target_cell = this.insert_cell_below('html',i);
680 var text = source_cell.get_text();
702 var text = source_cell.get_text();
681 if (text === source_cell.placeholder) {
703 if (text === source_cell.placeholder) {
682 text = '';
704 text = '';
683 };
705 };
684 // The edit must come before the set_text.
706 // The edit must come before the set_text.
685 target_cell.edit();
707 target_cell.edit();
686 target_cell.set_text(text);
708 target_cell.set_text(text);
687 // make this value the starting point, so that we can only undo
709 // make this value the starting point, so that we can only undo
688 // to this state, instead of a blank cell
710 // to this state, instead of a blank cell
689 target_cell.code_mirror.clearHistory();
711 target_cell.code_mirror.clearHistory();
690 source_element.remove();
712 source_element.remove();
691 this.dirty = true;
713 this.dirty = true;
692 };
714 };
693 };
715 };
694 };
716 };
695
717
696
718
697 Notebook.prototype.to_raw = function (index) {
719 Notebook.prototype.to_raw = function (index) {
698 var i = this.index_or_selected(index);
720 var i = this.index_or_selected(index);
699 if (this.is_valid_cell_index(i)) {
721 if (this.is_valid_cell_index(i)) {
700 var source_element = this.get_cell_element(i);
722 var source_element = this.get_cell_element(i);
701 var source_cell = source_element.data("cell");
723 var source_cell = source_element.data("cell");
702 var target_cell = null;
724 var target_cell = null;
703 if (!(source_cell instanceof IPython.RawCell)) {
725 if (!(source_cell instanceof IPython.RawCell)) {
704 target_cell = this.insert_cell_below('raw',i);
726 target_cell = this.insert_cell_below('raw',i);
705 var text = source_cell.get_text();
727 var text = source_cell.get_text();
706 if (text === source_cell.placeholder) {
728 if (text === source_cell.placeholder) {
707 text = '';
729 text = '';
708 };
730 };
709 // The edit must come before the set_text.
731 // The edit must come before the set_text.
710 target_cell.edit();
732 target_cell.edit();
711 target_cell.set_text(text);
733 target_cell.set_text(text);
712 // make this value the starting point, so that we can only undo
734 // make this value the starting point, so that we can only undo
713 // to this state, instead of a blank cell
735 // to this state, instead of a blank cell
714 target_cell.code_mirror.clearHistory();
736 target_cell.code_mirror.clearHistory();
715 source_element.remove();
737 source_element.remove();
716 this.dirty = true;
738 this.dirty = true;
717 };
739 };
718 };
740 };
719 };
741 };
720
742
721
743
722 Notebook.prototype.to_heading = function (index, level) {
744 Notebook.prototype.to_heading = function (index, level) {
723 level = level || 1;
745 level = level || 1;
724 var i = this.index_or_selected(index);
746 var i = this.index_or_selected(index);
725 if (this.is_valid_cell_index(i)) {
747 if (this.is_valid_cell_index(i)) {
726 var source_element = this.get_cell_element(i);
748 var source_element = this.get_cell_element(i);
727 var source_cell = source_element.data("cell");
749 var source_cell = source_element.data("cell");
728 var target_cell = null;
750 var target_cell = null;
729 if (source_cell instanceof IPython.HeadingCell) {
751 if (source_cell instanceof IPython.HeadingCell) {
730 source_cell.set_level(level);
752 source_cell.set_level(level);
731 } else {
753 } else {
732 target_cell = this.insert_cell_below('heading',i);
754 target_cell = this.insert_cell_below('heading',i);
733 var text = source_cell.get_text();
755 var text = source_cell.get_text();
734 if (text === source_cell.placeholder) {
756 if (text === source_cell.placeholder) {
735 text = '';
757 text = '';
736 };
758 };
737 // The edit must come before the set_text.
759 // The edit must come before the set_text.
738 target_cell.set_level(level);
760 target_cell.set_level(level);
739 target_cell.edit();
761 target_cell.edit();
740 target_cell.set_text(text);
762 target_cell.set_text(text);
741 // make this value the starting point, so that we can only undo
763 // make this value the starting point, so that we can only undo
742 // to this state, instead of a blank cell
764 // to this state, instead of a blank cell
743 target_cell.code_mirror.clearHistory();
765 target_cell.code_mirror.clearHistory();
744 source_element.remove();
766 source_element.remove();
745 this.dirty = true;
767 this.dirty = true;
746 };
768 };
747 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
769 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
748 {'cell_type':'heading',level:level}
770 {'cell_type':'heading',level:level}
749 );
771 );
750 };
772 };
751 };
773 };
752
774
753
775
754 // Cut/Copy/Paste
776 // Cut/Copy/Paste
755
777
756 Notebook.prototype.enable_paste = function () {
778 Notebook.prototype.enable_paste = function () {
757 var that = this;
779 var that = this;
758 if (!this.paste_enabled) {
780 if (!this.paste_enabled) {
759 $('#paste_cell').removeClass('ui-state-disabled')
781 $('#paste_cell_replace').removeClass('ui-state-disabled')
760 .on('click', function () {that.paste_cell();});
782 .on('click', function () {that.paste_cell_replace();});
761 $('#paste_cell_above').removeClass('ui-state-disabled')
783 $('#paste_cell_above').removeClass('ui-state-disabled')
762 .on('click', function () {that.paste_cell_above();});
784 .on('click', function () {that.paste_cell_above();});
763 $('#paste_cell_below').removeClass('ui-state-disabled')
785 $('#paste_cell_below').removeClass('ui-state-disabled')
764 .on('click', function () {that.paste_cell_below();});
786 .on('click', function () {that.paste_cell_below();});
765 this.paste_enabled = true;
787 this.paste_enabled = true;
766 };
788 };
767 };
789 };
768
790
769
791
770 Notebook.prototype.disable_paste = function () {
792 Notebook.prototype.disable_paste = function () {
771 if (this.paste_enabled) {
793 if (this.paste_enabled) {
772 $('#paste_cell').addClass('ui-state-disabled').off('click');
794 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
773 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
795 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
774 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
796 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
775 this.paste_enabled = false;
797 this.paste_enabled = false;
776 };
798 };
777 };
799 };
778
800
779
801
780 Notebook.prototype.cut_cell = function () {
802 Notebook.prototype.cut_cell = function () {
781 this.copy_cell();
803 this.copy_cell();
782 this.delete_cell();
804 this.delete_cell();
783 }
805 }
784
806
785 Notebook.prototype.copy_cell = function () {
807 Notebook.prototype.copy_cell = function () {
786 var cell = this.get_selected_cell();
808 var cell = this.get_selected_cell();
787 this.clipboard = cell.toJSON();
809 this.clipboard = cell.toJSON();
788 this.enable_paste();
810 this.enable_paste();
789 };
811 };
790
812
791
813
792 Notebook.prototype.paste_cell = function () {
814 Notebook.prototype.paste_cell_replace = function () {
793 if (this.clipboard !== null && this.paste_enabled) {
815 if (this.clipboard !== null && this.paste_enabled) {
794 var cell_data = this.clipboard;
816 var cell_data = this.clipboard;
795 var new_cell = this.insert_cell_above(cell_data.cell_type);
817 var new_cell = this.insert_cell_above(cell_data.cell_type);
796 new_cell.fromJSON(cell_data);
818 new_cell.fromJSON(cell_data);
797 old_cell = this.get_next_cell(new_cell);
819 old_cell = this.get_next_cell(new_cell);
798 this.delete_cell(this.find_cell_index(old_cell));
820 this.delete_cell(this.find_cell_index(old_cell));
799 this.select(this.find_cell_index(new_cell));
821 this.select(this.find_cell_index(new_cell));
800 };
822 };
801 };
823 };
802
824
803
825
804 Notebook.prototype.paste_cell_above = function () {
826 Notebook.prototype.paste_cell_above = function () {
805 if (this.clipboard !== null && this.paste_enabled) {
827 if (this.clipboard !== null && this.paste_enabled) {
806 var cell_data = this.clipboard;
828 var cell_data = this.clipboard;
807 var new_cell = this.insert_cell_above(cell_data.cell_type);
829 var new_cell = this.insert_cell_above(cell_data.cell_type);
808 new_cell.fromJSON(cell_data);
830 new_cell.fromJSON(cell_data);
809 };
831 };
810 };
832 };
811
833
812
834
813 Notebook.prototype.paste_cell_below = function () {
835 Notebook.prototype.paste_cell_below = function () {
814 if (this.clipboard !== null && this.paste_enabled) {
836 if (this.clipboard !== null && this.paste_enabled) {
815 var cell_data = this.clipboard;
837 var cell_data = this.clipboard;
816 var new_cell = this.insert_cell_below(cell_data.cell_type);
838 var new_cell = this.insert_cell_below(cell_data.cell_type);
817 new_cell.fromJSON(cell_data);
839 new_cell.fromJSON(cell_data);
818 };
840 };
819 };
841 };
820
842
843 // Cell undelete
844
845 Notebook.prototype.undelete = function() {
846 if (this.undelete_backup !== null && this.undelete_index !== null) {
847 var current_index = this.get_selected_index();
848 if (this.undelete_index < current_index) {
849 current_index = current_index + 1;
850 }
851 if (this.undelete_index >= this.ncells()) {
852 this.select(this.ncells() - 1);
853 }
854 else {
855 this.select(this.undelete_index);
856 }
857 var cell_data = this.undelete_backup;
858 var new_cell = null;
859 if (this.undelete_below) {
860 new_cell = this.insert_cell_below(cell_data.cell_type);
861 } else {
862 new_cell = this.insert_cell_above(cell_data.cell_type);
863 }
864 new_cell.fromJSON(cell_data);
865 this.select(current_index);
866 this.undelete_backup = null;
867 this.undelete_index = null;
868 }
869 }
821
870
822 // Split/merge
871 // Split/merge
823
872
824 Notebook.prototype.split_cell = function () {
873 Notebook.prototype.split_cell = function () {
825 // Todo: implement spliting for other cell types.
874 // Todo: implement spliting for other cell types.
826 var cell = this.get_selected_cell();
875 var cell = this.get_selected_cell();
827 if (cell.is_splittable()) {
876 if (cell.is_splittable()) {
828 texta = cell.get_pre_cursor();
877 texta = cell.get_pre_cursor();
829 textb = cell.get_post_cursor();
878 textb = cell.get_post_cursor();
830 if (cell instanceof IPython.CodeCell) {
879 if (cell instanceof IPython.CodeCell) {
831 cell.set_text(texta);
880 cell.set_text(texta);
832 var new_cell = this.insert_cell_below('code');
881 var new_cell = this.insert_cell_below('code');
833 new_cell.set_text(textb);
882 new_cell.set_text(textb);
834 } else if (cell instanceof IPython.MarkdownCell) {
883 } else if (cell instanceof IPython.MarkdownCell) {
835 cell.set_text(texta);
884 cell.set_text(texta);
836 cell.render();
885 cell.render();
837 var new_cell = this.insert_cell_below('markdown');
886 var new_cell = this.insert_cell_below('markdown');
838 new_cell.edit(); // editor must be visible to call set_text
887 new_cell.edit(); // editor must be visible to call set_text
839 new_cell.set_text(textb);
888 new_cell.set_text(textb);
840 new_cell.render();
889 new_cell.render();
841 } else if (cell instanceof IPython.HTMLCell) {
890 } else if (cell instanceof IPython.HTMLCell) {
842 cell.set_text(texta);
891 cell.set_text(texta);
843 cell.render();
892 cell.render();
844 var new_cell = this.insert_cell_below('html');
893 var new_cell = this.insert_cell_below('html');
845 new_cell.edit(); // editor must be visible to call set_text
894 new_cell.edit(); // editor must be visible to call set_text
846 new_cell.set_text(textb);
895 new_cell.set_text(textb);
847 new_cell.render();
896 new_cell.render();
848 };
897 };
849 };
898 };
850 };
899 };
851
900
852
901
853 Notebook.prototype.merge_cell_above = function () {
902 Notebook.prototype.merge_cell_above = function () {
854 var index = this.get_selected_index();
903 var index = this.get_selected_index();
855 var cell = this.get_cell(index);
904 var cell = this.get_cell(index);
856 if (index > 0) {
905 if (index > 0) {
857 upper_cell = this.get_cell(index-1);
906 upper_cell = this.get_cell(index-1);
858 upper_text = upper_cell.get_text();
907 upper_text = upper_cell.get_text();
859 text = cell.get_text();
908 text = cell.get_text();
860 if (cell instanceof IPython.CodeCell) {
909 if (cell instanceof IPython.CodeCell) {
861 cell.set_text(upper_text+'\n'+text);
910 cell.set_text(upper_text+'\n'+text);
862 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
911 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
863 cell.edit();
912 cell.edit();
864 cell.set_text(upper_text+'\n'+text);
913 cell.set_text(upper_text+'\n'+text);
865 cell.render();
914 cell.render();
866 };
915 };
867 this.delete_cell(index-1);
916 this.delete_cell(index-1);
868 this.select(this.find_cell_index(cell));
917 this.select(this.find_cell_index(cell));
869 };
918 };
870 };
919 };
871
920
872
921
873 Notebook.prototype.merge_cell_below = function () {
922 Notebook.prototype.merge_cell_below = function () {
874 var index = this.get_selected_index();
923 var index = this.get_selected_index();
875 var cell = this.get_cell(index);
924 var cell = this.get_cell(index);
876 if (index < this.ncells()-1) {
925 if (index < this.ncells()-1) {
877 lower_cell = this.get_cell(index+1);
926 lower_cell = this.get_cell(index+1);
878 lower_text = lower_cell.get_text();
927 lower_text = lower_cell.get_text();
879 text = cell.get_text();
928 text = cell.get_text();
880 if (cell instanceof IPython.CodeCell) {
929 if (cell instanceof IPython.CodeCell) {
881 cell.set_text(text+'\n'+lower_text);
930 cell.set_text(text+'\n'+lower_text);
882 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
931 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
883 cell.edit();
932 cell.edit();
884 cell.set_text(text+'\n'+lower_text);
933 cell.set_text(text+'\n'+lower_text);
885 cell.render();
934 cell.render();
886 };
935 };
887 this.delete_cell(index+1);
936 this.delete_cell(index+1);
888 this.select(this.find_cell_index(cell));
937 this.select(this.find_cell_index(cell));
889 };
938 };
890 };
939 };
891
940
892
941
893 // Cell collapsing and output clearing
942 // Cell collapsing and output clearing
894
943
895 Notebook.prototype.collapse = function (index) {
944 Notebook.prototype.collapse = function (index) {
896 var i = this.index_or_selected(index);
945 var i = this.index_or_selected(index);
897 this.get_cell(i).collapse();
946 this.get_cell(i).collapse();
898 this.dirty = true;
947 this.dirty = true;
899 };
948 };
900
949
901
950
902 Notebook.prototype.expand = function (index) {
951 Notebook.prototype.expand = function (index) {
903 var i = this.index_or_selected(index);
952 var i = this.index_or_selected(index);
904 this.get_cell(i).expand();
953 this.get_cell(i).expand();
905 this.dirty = true;
954 this.dirty = true;
906 };
955 };
907
956
908
957
909 Notebook.prototype.toggle_output = function (index) {
958 Notebook.prototype.toggle_output = function (index) {
910 var i = this.index_or_selected(index);
959 var i = this.index_or_selected(index);
911 this.get_cell(i).toggle_output();
960 this.get_cell(i).toggle_output();
912 this.dirty = true;
961 this.dirty = true;
913 };
962 };
914
963
915
964
916 Notebook.prototype.toggle_output_scroll = function (index) {
965 Notebook.prototype.toggle_output_scroll = function (index) {
917 var i = this.index_or_selected(index);
966 var i = this.index_or_selected(index);
918 this.get_cell(i).toggle_output_scroll();
967 this.get_cell(i).toggle_output_scroll();
919 };
968 };
920
969
921
970
922 Notebook.prototype.collapse_all_output = function () {
971 Notebook.prototype.collapse_all_output = function () {
923 var ncells = this.ncells();
972 var ncells = this.ncells();
924 var cells = this.get_cells();
973 var cells = this.get_cells();
925 for (var i=0; i<ncells; i++) {
974 for (var i=0; i<ncells; i++) {
926 if (cells[i] instanceof IPython.CodeCell) {
975 if (cells[i] instanceof IPython.CodeCell) {
927 cells[i].output_area.collapse();
976 cells[i].output_area.collapse();
928 }
977 }
929 };
978 };
930 // this should not be set if the `collapse` key is removed from nbformat
979 // this should not be set if the `collapse` key is removed from nbformat
931 this.dirty = true;
980 this.dirty = true;
932 };
981 };
933
982
934
983
935 Notebook.prototype.scroll_all_output = function () {
984 Notebook.prototype.scroll_all_output = function () {
936 var ncells = this.ncells();
985 var ncells = this.ncells();
937 var cells = this.get_cells();
986 var cells = this.get_cells();
938 for (var i=0; i<ncells; i++) {
987 for (var i=0; i<ncells; i++) {
939 if (cells[i] instanceof IPython.CodeCell) {
988 if (cells[i] instanceof IPython.CodeCell) {
940 cells[i].output_area.expand();
989 cells[i].output_area.expand();
941 cells[i].output_area.scroll_if_long(20);
990 cells[i].output_area.scroll_if_long(20);
942 }
991 }
943 };
992 };
944 // this should not be set if the `collapse` key is removed from nbformat
993 // this should not be set if the `collapse` key is removed from nbformat
945 this.dirty = true;
994 this.dirty = true;
946 };
995 };
947
996
948
997
949 Notebook.prototype.expand_all_output = function () {
998 Notebook.prototype.expand_all_output = function () {
950 var ncells = this.ncells();
999 var ncells = this.ncells();
951 var cells = this.get_cells();
1000 var cells = this.get_cells();
952 for (var i=0; i<ncells; i++) {
1001 for (var i=0; i<ncells; i++) {
953 if (cells[i] instanceof IPython.CodeCell) {
1002 if (cells[i] instanceof IPython.CodeCell) {
954 cells[i].output_area.expand();
1003 cells[i].output_area.expand();
955 cells[i].output_area.unscroll_area();
1004 cells[i].output_area.unscroll_area();
956 }
1005 }
957 };
1006 };
958 // this should not be set if the `collapse` key is removed from nbformat
1007 // this should not be set if the `collapse` key is removed from nbformat
959 this.dirty = true;
1008 this.dirty = true;
960 };
1009 };
961
1010
962
1011
963 Notebook.prototype.clear_all_output = function () {
1012 Notebook.prototype.clear_all_output = function () {
964 var ncells = this.ncells();
1013 var ncells = this.ncells();
965 var cells = this.get_cells();
1014 var cells = this.get_cells();
966 for (var i=0; i<ncells; i++) {
1015 for (var i=0; i<ncells; i++) {
967 if (cells[i] instanceof IPython.CodeCell) {
1016 if (cells[i] instanceof IPython.CodeCell) {
968 cells[i].clear_output(true,true,true);
1017 cells[i].clear_output(true,true,true);
969 // Make all In[] prompts blank, as well
1018 // Make all In[] prompts blank, as well
970 // TODO: make this configurable (via checkbox?)
1019 // TODO: make this configurable (via checkbox?)
971 cells[i].set_input_prompt();
1020 cells[i].set_input_prompt();
972 }
1021 }
973 };
1022 };
974 this.dirty = true;
1023 this.dirty = true;
975 };
1024 };
976
1025
977
1026
978 // Other cell functions: line numbers, ...
1027 // Other cell functions: line numbers, ...
979
1028
980 Notebook.prototype.cell_toggle_line_numbers = function() {
1029 Notebook.prototype.cell_toggle_line_numbers = function() {
981 this.get_selected_cell().toggle_line_numbers();
1030 this.get_selected_cell().toggle_line_numbers();
982 };
1031 };
983
1032
984 // Kernel related things
1033 // Kernel related things
985
1034
986 Notebook.prototype.start_kernel = function () {
1035 Notebook.prototype.start_kernel = function () {
987 var base_url = $('body').data('baseKernelUrl') + "kernels";
1036 var base_url = $('body').data('baseKernelUrl') + "kernels";
988 this.kernel = new IPython.Kernel(base_url);
1037 this.kernel = new IPython.Kernel(base_url);
989 this.kernel.start(this.notebook_id);
1038 this.kernel.start(this.notebook_id);
990 // Now that the kernel has been created, tell the CodeCells about it.
1039 // Now that the kernel has been created, tell the CodeCells about it.
991 var ncells = this.ncells();
1040 var ncells = this.ncells();
992 for (var i=0; i<ncells; i++) {
1041 for (var i=0; i<ncells; i++) {
993 var cell = this.get_cell(i);
1042 var cell = this.get_cell(i);
994 if (cell instanceof IPython.CodeCell) {
1043 if (cell instanceof IPython.CodeCell) {
995 cell.set_kernel(this.kernel)
1044 cell.set_kernel(this.kernel)
996 };
1045 };
997 };
1046 };
998 };
1047 };
999
1048
1000
1049
1001 Notebook.prototype.restart_kernel = function () {
1050 Notebook.prototype.restart_kernel = function () {
1002 var that = this;
1051 var that = this;
1003 var dialog = $('<div/>');
1052 var dialog = $('<div/>');
1004 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1053 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1005 $(document).append(dialog);
1054 $(document).append(dialog);
1006 dialog.dialog({
1055 dialog.dialog({
1007 resizable: false,
1056 resizable: false,
1008 modal: true,
1057 modal: true,
1009 title: "Restart kernel or continue running?",
1058 title: "Restart kernel or continue running?",
1010 closeText: '',
1059 closeText: '',
1011 buttons : {
1060 buttons : {
1012 "Restart": function () {
1061 "Restart": function () {
1013 that.kernel.restart();
1062 that.kernel.restart();
1014 $(this).dialog('close');
1063 $(this).dialog('close');
1015 },
1064 },
1016 "Continue running": function () {
1065 "Continue running": function () {
1017 $(this).dialog('close');
1066 $(this).dialog('close');
1018 }
1067 }
1019 }
1068 }
1020 });
1069 });
1021 };
1070 };
1022
1071
1023
1072
1024 Notebook.prototype.execute_selected_cell = function (options) {
1073 Notebook.prototype.execute_selected_cell = function (options) {
1025 // add_new: should a new cell be added if we are at the end of the nb
1074 // add_new: should a new cell be added if we are at the end of the nb
1026 // terminal: execute in terminal mode, which stays in the current cell
1075 // terminal: execute in terminal mode, which stays in the current cell
1027 default_options = {terminal: false, add_new: true};
1076 default_options = {terminal: false, add_new: true};
1028 $.extend(default_options, options);
1077 $.extend(default_options, options);
1029 var that = this;
1078 var that = this;
1030 var cell = that.get_selected_cell();
1079 var cell = that.get_selected_cell();
1031 var cell_index = that.find_cell_index(cell);
1080 var cell_index = that.find_cell_index(cell);
1032 if (cell instanceof IPython.CodeCell) {
1081 if (cell instanceof IPython.CodeCell) {
1033 cell.execute();
1082 cell.execute();
1034 } else if (cell instanceof IPython.HTMLCell) {
1083 } else if (cell instanceof IPython.HTMLCell) {
1035 cell.render();
1084 cell.render();
1036 }
1085 }
1037 if (default_options.terminal) {
1086 if (default_options.terminal) {
1038 cell.select_all();
1087 cell.select_all();
1039 } else {
1088 } else {
1040 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1089 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1041 that.insert_cell_below('code');
1090 that.insert_cell_below('code');
1042 // If we are adding a new cell at the end, scroll down to show it.
1091 // If we are adding a new cell at the end, scroll down to show it.
1043 that.scroll_to_bottom();
1092 that.scroll_to_bottom();
1044 } else {
1093 } else {
1045 that.select(cell_index+1);
1094 that.select(cell_index+1);
1046 };
1095 };
1047 };
1096 };
1048 this.dirty = true;
1097 this.dirty = true;
1049 };
1098 };
1050
1099
1051
1100
1101 Notebook.prototype.execute_cells_below = function () {
1102 this.execute_cell_range(this.get_selected_index(), this.ncells());
1103 that.scroll_to_bottom();
1104 };
1105
1106 Notebook.prototype.execute_cells_above = function () {
1107 this.execute_cell_range(0, this.get_selected_index());
1108 };
1109
1052 Notebook.prototype.execute_all_cells = function () {
1110 Notebook.prototype.execute_all_cells = function () {
1053 var ncells = this.ncells();
1111 this.execute_cell_range(0, this.ncells());
1054 for (var i=0; i<ncells; i++) {
1112 that.scroll_to_bottom();
1113 };
1114
1115 Notebook.prototype.execute_cell_range = function (start, end) {
1116 for (var i=start; i<end; i++) {
1055 this.select(i);
1117 this.select(i);
1056 this.execute_selected_cell({add_new:false});
1118 this.execute_selected_cell({add_new:false});
1057 };
1119 };
1058 this.scroll_to_bottom();
1059 };
1120 };
1060
1121
1061 // Persistance and loading
1122 // Persistance and loading
1062
1123
1063 Notebook.prototype.get_notebook_id = function () {
1124 Notebook.prototype.get_notebook_id = function () {
1064 return this.notebook_id;
1125 return this.notebook_id;
1065 };
1126 };
1066
1127
1067
1128
1068 Notebook.prototype.get_notebook_name = function () {
1129 Notebook.prototype.get_notebook_name = function () {
1069 return this.notebook_name;
1130 return this.notebook_name;
1070 };
1131 };
1071
1132
1072
1133
1073 Notebook.prototype.set_notebook_name = function (name) {
1134 Notebook.prototype.set_notebook_name = function (name) {
1074 this.notebook_name = name;
1135 this.notebook_name = name;
1075 };
1136 };
1076
1137
1077
1138
1078 Notebook.prototype.test_notebook_name = function (nbname) {
1139 Notebook.prototype.test_notebook_name = function (nbname) {
1079 nbname = nbname || '';
1140 nbname = nbname || '';
1080 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1141 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1081 return true;
1142 return true;
1082 } else {
1143 } else {
1083 return false;
1144 return false;
1084 };
1145 };
1085 };
1146 };
1086
1147
1087
1148
1088 Notebook.prototype.fromJSON = function (data) {
1149 Notebook.prototype.fromJSON = function (data) {
1089 var ncells = this.ncells();
1150 var ncells = this.ncells();
1090 var i;
1151 var i;
1091 for (i=0; i<ncells; i++) {
1152 for (i=0; i<ncells; i++) {
1092 // Always delete cell 0 as they get renumbered as they are deleted.
1153 // Always delete cell 0 as they get renumbered as they are deleted.
1093 this.delete_cell(0);
1154 this.delete_cell(0);
1094 };
1155 };
1095 // Save the metadata and name.
1156 // Save the metadata and name.
1096 this.metadata = data.metadata;
1157 this.metadata = data.metadata;
1097 this.notebook_name = data.metadata.name;
1158 this.notebook_name = data.metadata.name;
1098 // Only handle 1 worksheet for now.
1159 // Only handle 1 worksheet for now.
1099 var worksheet = data.worksheets[0];
1160 var worksheet = data.worksheets[0];
1100 if (worksheet !== undefined) {
1161 if (worksheet !== undefined) {
1101 if (worksheet.metadata) {
1162 if (worksheet.metadata) {
1102 this.worksheet_metadata = worksheet.metadata;
1163 this.worksheet_metadata = worksheet.metadata;
1103 }
1164 }
1104 var new_cells = worksheet.cells;
1165 var new_cells = worksheet.cells;
1105 ncells = new_cells.length;
1166 ncells = new_cells.length;
1106 var cell_data = null;
1167 var cell_data = null;
1107 var new_cell = null;
1168 var new_cell = null;
1108 for (i=0; i<ncells; i++) {
1169 for (i=0; i<ncells; i++) {
1109 cell_data = new_cells[i];
1170 cell_data = new_cells[i];
1110 // VERSIONHACK: plaintext -> raw
1171 // VERSIONHACK: plaintext -> raw
1111 // handle never-released plaintext name for raw cells
1172 // handle never-released plaintext name for raw cells
1112 if (cell_data.cell_type === 'plaintext'){
1173 if (cell_data.cell_type === 'plaintext'){
1113 cell_data.cell_type = 'raw';
1174 cell_data.cell_type = 'raw';
1114 }
1175 }
1115
1176
1116 new_cell = this.insert_cell_below(cell_data.cell_type);
1177 new_cell = this.insert_cell_below(cell_data.cell_type);
1117 new_cell.fromJSON(cell_data);
1178 new_cell.fromJSON(cell_data);
1118 };
1179 };
1119 };
1180 };
1120 if (data.worksheets.length > 1) {
1181 if (data.worksheets.length > 1) {
1121 var dialog = $('<div/>');
1182 var dialog = $('<div/>');
1122 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1183 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1123 "but this version of IPython can only handle the first. " +
1184 "but this version of IPython can only handle the first. " +
1124 "If you save this notebook, worksheets after the first will be lost."
1185 "If you save this notebook, worksheets after the first will be lost."
1125 );
1186 );
1126 this.element.append(dialog);
1187 this.element.append(dialog);
1127 dialog.dialog({
1188 dialog.dialog({
1128 resizable: false,
1189 resizable: false,
1129 modal: true,
1190 modal: true,
1130 title: "Multiple worksheets",
1191 title: "Multiple worksheets",
1131 closeText: "",
1192 closeText: "",
1132 close: function(event, ui) {$(this).dialog('destroy').remove();},
1193 close: function(event, ui) {$(this).dialog('destroy').remove();},
1133 buttons : {
1194 buttons : {
1134 "OK": function () {
1195 "OK": function () {
1135 $(this).dialog('close');
1196 $(this).dialog('close');
1136 }
1197 }
1137 },
1198 },
1138 width: 400
1199 width: 400
1139 });
1200 });
1140 }
1201 }
1141 };
1202 };
1142
1203
1143
1204
1144 Notebook.prototype.toJSON = function () {
1205 Notebook.prototype.toJSON = function () {
1145 var cells = this.get_cells();
1206 var cells = this.get_cells();
1146 var ncells = cells.length;
1207 var ncells = cells.length;
1147 var cell_array = new Array(ncells);
1208 var cell_array = new Array(ncells);
1148 for (var i=0; i<ncells; i++) {
1209 for (var i=0; i<ncells; i++) {
1149 cell_array[i] = cells[i].toJSON();
1210 cell_array[i] = cells[i].toJSON();
1150 };
1211 };
1151 var data = {
1212 var data = {
1152 // Only handle 1 worksheet for now.
1213 // Only handle 1 worksheet for now.
1153 worksheets : [{
1214 worksheets : [{
1154 cells: cell_array,
1215 cells: cell_array,
1155 metadata: this.worksheet_metadata
1216 metadata: this.worksheet_metadata
1156 }],
1217 }],
1157 metadata : this.metadata
1218 metadata : this.metadata
1158 };
1219 };
1159 return data;
1220 return data;
1160 };
1221 };
1161
1222
1162 Notebook.prototype.save_notebook = function () {
1223 Notebook.prototype.save_notebook = function () {
1163 // We may want to move the name/id/nbformat logic inside toJSON?
1224 // We may want to move the name/id/nbformat logic inside toJSON?
1164 var data = this.toJSON();
1225 var data = this.toJSON();
1165 data.metadata.name = this.notebook_name;
1226 data.metadata.name = this.notebook_name;
1166 data.nbformat = this.nbformat;
1227 data.nbformat = this.nbformat;
1167 data.nbformat_minor = this.nbformat_minor;
1228 data.nbformat_minor = this.nbformat_minor;
1168 // We do the call with settings so we can set cache to false.
1229 // We do the call with settings so we can set cache to false.
1169 var settings = {
1230 var settings = {
1170 processData : false,
1231 processData : false,
1171 cache : false,
1232 cache : false,
1172 type : "PUT",
1233 type : "PUT",
1173 data : JSON.stringify(data),
1234 data : JSON.stringify(data),
1174 headers : {'Content-Type': 'application/json'},
1235 headers : {'Content-Type': 'application/json'},
1175 success : $.proxy(this.save_notebook_success,this),
1236 success : $.proxy(this.save_notebook_success,this),
1176 error : $.proxy(this.save_notebook_error,this)
1237 error : $.proxy(this.save_notebook_error,this)
1177 };
1238 };
1178 $([IPython.events]).trigger('notebook_saving.Notebook');
1239 $([IPython.events]).trigger('notebook_saving.Notebook');
1179 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1240 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1180 $.ajax(url, settings);
1241 $.ajax(url, settings);
1181 };
1242 };
1182
1243
1183
1244
1184 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1245 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1185 this.dirty = false;
1246 this.dirty = false;
1186 $([IPython.events]).trigger('notebook_saved.Notebook');
1247 $([IPython.events]).trigger('notebook_saved.Notebook');
1187 };
1248 };
1188
1249
1189
1250
1190 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1251 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1191 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1252 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1192 };
1253 };
1193
1254
1194
1255
1195 Notebook.prototype.load_notebook = function (notebook_id) {
1256 Notebook.prototype.load_notebook = function (notebook_id) {
1196 var that = this;
1257 var that = this;
1197 this.notebook_id = notebook_id;
1258 this.notebook_id = notebook_id;
1198 // We do the call with settings so we can set cache to false.
1259 // We do the call with settings so we can set cache to false.
1199 var settings = {
1260 var settings = {
1200 processData : false,
1261 processData : false,
1201 cache : false,
1262 cache : false,
1202 type : "GET",
1263 type : "GET",
1203 dataType : "json",
1264 dataType : "json",
1204 success : $.proxy(this.load_notebook_success,this),
1265 success : $.proxy(this.load_notebook_success,this),
1205 error : $.proxy(this.load_notebook_error,this),
1266 error : $.proxy(this.load_notebook_error,this),
1206 };
1267 };
1207 $([IPython.events]).trigger('notebook_loading.Notebook');
1268 $([IPython.events]).trigger('notebook_loading.Notebook');
1208 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1269 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1209 $.ajax(url, settings);
1270 $.ajax(url, settings);
1210 };
1271 };
1211
1272
1212
1273
1213 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1274 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1214 this.fromJSON(data);
1275 this.fromJSON(data);
1215 if (this.ncells() === 0) {
1276 if (this.ncells() === 0) {
1216 this.insert_cell_below('code');
1277 this.insert_cell_below('code');
1217 };
1278 };
1218 this.dirty = false;
1279 this.dirty = false;
1219 this.select(0);
1280 this.select(0);
1220 this.scroll_to_top();
1281 this.scroll_to_top();
1221 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1282 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1222 msg = "This notebook has been converted from an older " +
1283 msg = "This notebook has been converted from an older " +
1223 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1284 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1224 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1285 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1225 "newer notebook format will be used and older verions of IPython " +
1286 "newer notebook format will be used and older verions of IPython " +
1226 "may not be able to read it. To keep the older version, close the " +
1287 "may not be able to read it. To keep the older version, close the " +
1227 "notebook without saving it.";
1288 "notebook without saving it.";
1228 var dialog = $('<div/>');
1289 var dialog = $('<div/>');
1229 dialog.html(msg);
1290 dialog.html(msg);
1230 this.element.append(dialog);
1291 this.element.append(dialog);
1231 dialog.dialog({
1292 dialog.dialog({
1232 resizable: false,
1293 resizable: false,
1233 modal: true,
1294 modal: true,
1234 title: "Notebook converted",
1295 title: "Notebook converted",
1235 closeText: "",
1296 closeText: "",
1236 close: function(event, ui) {$(this).dialog('destroy').remove();},
1297 close: function(event, ui) {$(this).dialog('destroy').remove();},
1237 buttons : {
1298 buttons : {
1238 "OK": function () {
1299 "OK": function () {
1239 $(this).dialog('close');
1300 $(this).dialog('close');
1240 }
1301 }
1241 },
1302 },
1242 width: 400
1303 width: 400
1243 });
1304 });
1244 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1305 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1245 var that = this;
1306 var that = this;
1246 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1307 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1247 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1308 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1248 msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1309 msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1249 this_vs + ". You can still work with this notebook, but some features " +
1310 this_vs + ". You can still work with this notebook, but some features " +
1250 "introduced in later notebook versions may not be available."
1311 "introduced in later notebook versions may not be available."
1251
1312
1252 var dialog = $('<div/>');
1313 var dialog = $('<div/>');
1253 dialog.html(msg);
1314 dialog.html(msg);
1254 this.element.append(dialog);
1315 this.element.append(dialog);
1255 dialog.dialog({
1316 dialog.dialog({
1256 resizable: false,
1317 resizable: false,
1257 modal: true,
1318 modal: true,
1258 title: "Newer Notebook",
1319 title: "Newer Notebook",
1259 closeText: "",
1320 closeText: "",
1260 close: function(event, ui) {$(this).dialog('destroy').remove();},
1321 close: function(event, ui) {$(this).dialog('destroy').remove();},
1261 buttons : {
1322 buttons : {
1262 "OK": function () {
1323 "OK": function () {
1263 $(this).dialog('close');
1324 $(this).dialog('close');
1264 }
1325 }
1265 },
1326 },
1266 width: 400
1327 width: 400
1267 });
1328 });
1268
1329
1269 }
1330 }
1270 // Create the kernel after the notebook is completely loaded to prevent
1331 // Create the kernel after the notebook is completely loaded to prevent
1271 // code execution upon loading, which is a security risk.
1332 // code execution upon loading, which is a security risk.
1272 if (! this.read_only) {
1333 if (! this.read_only) {
1273 this.start_kernel();
1334 this.start_kernel();
1274 }
1335 }
1275 $([IPython.events]).trigger('notebook_loaded.Notebook');
1336 $([IPython.events]).trigger('notebook_loaded.Notebook');
1276 };
1337 };
1277
1338
1278
1339
1279 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1340 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1280 if (xhr.status === 500) {
1341 if (xhr.status === 500) {
1281 msg = "An error occurred while loading this notebook. Most likely " +
1342 msg = "An error occurred while loading this notebook. Most likely " +
1282 "this notebook is in a newer format than is supported by this " +
1343 "this notebook is in a newer format than is supported by this " +
1283 "version of IPython. This version can load notebook formats " +
1344 "version of IPython. This version can load notebook formats " +
1284 "v"+this.nbformat+" or earlier.";
1345 "v"+this.nbformat+" or earlier.";
1285 var dialog = $('<div/>');
1346 var dialog = $('<div/>');
1286 dialog.html(msg);
1347 dialog.html(msg);
1287 this.element.append(dialog);
1348 this.element.append(dialog);
1288 dialog.dialog({
1349 dialog.dialog({
1289 resizable: false,
1350 resizable: false,
1290 modal: true,
1351 modal: true,
1291 title: "Error loading notebook",
1352 title: "Error loading notebook",
1292 closeText: "",
1353 closeText: "",
1293 close: function(event, ui) {$(this).dialog('destroy').remove();},
1354 close: function(event, ui) {$(this).dialog('destroy').remove();},
1294 buttons : {
1355 buttons : {
1295 "OK": function () {
1356 "OK": function () {
1296 $(this).dialog('close');
1357 $(this).dialog('close');
1297 }
1358 }
1298 },
1359 },
1299 width: 400
1360 width: 400
1300 });
1361 });
1301 }
1362 }
1302 }
1363 }
1303
1364
1304 IPython.Notebook = Notebook;
1365 IPython.Notebook = Notebook;
1305
1366
1306
1367
1307 return IPython;
1368 return IPython;
1308
1369
1309 }(IPython));
1370 }(IPython));
1310
1371
@@ -1,557 +1,559 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // OutputArea
9 // OutputArea
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13 "use strict";
13 "use strict";
14
14
15 var utils = IPython.utils;
15 var utils = IPython.utils;
16
16
17 var OutputArea = function (selector, prompt_area) {
17 var OutputArea = function (selector, prompt_area) {
18 this.selector = selector;
18 this.selector = selector;
19 this.wrapper = $(selector);
19 this.wrapper = $(selector);
20 this.outputs = [];
20 this.outputs = [];
21 this.collapsed = false;
21 this.collapsed = false;
22 this.scrolled = false;
22 this.scrolled = false;
23 this.clear_out_timeout = null;
23 this.clear_out_timeout = null;
24 if (prompt_area === undefined) {
24 if (prompt_area === undefined) {
25 this.prompt_area = true;
25 this.prompt_area = true;
26 } else {
26 } else {
27 this.prompt_area = prompt_area;
27 this.prompt_area = prompt_area;
28 };
28 };
29 this.create_elements();
29 this.create_elements();
30 this.style();
30 this.style();
31 this.bind_events();
31 this.bind_events();
32 };
32 };
33
33
34 OutputArea.prototype.create_elements = function () {
34 OutputArea.prototype.create_elements = function () {
35 this.element = $("<div/>");
35 this.element = $("<div/>");
36 this.collapse_button = $("<div/>");
36 this.collapse_button = $("<div/>");
37 this.prompt_overlay = $("<div/>");
37 this.prompt_overlay = $("<div/>");
38 this.wrapper.append(this.prompt_overlay);
38 this.wrapper.append(this.prompt_overlay);
39 this.wrapper.append(this.element);
39 this.wrapper.append(this.element);
40 this.wrapper.append(this.collapse_button);
40 this.wrapper.append(this.collapse_button);
41 };
41 };
42
42
43
43
44 OutputArea.prototype.style = function () {
44 OutputArea.prototype.style = function () {
45 this.collapse_button.hide();
45 this.collapse_button.hide();
46 this.prompt_overlay.hide();
46 this.prompt_overlay.hide();
47
47
48 this.wrapper.addClass('output_wrapper');
48 this.wrapper.addClass('output_wrapper');
49 this.element.addClass('output vbox');
49 this.element.addClass('output vbox');
50
50
51 this.collapse_button.button();
51 this.collapse_button.button();
52 this.collapse_button.addClass('output_collapsed vbox');
52 this.collapse_button.addClass('output_collapsed vbox');
53 this.collapse_button.attr('title', 'click to expand outout');
53 this.collapse_button.attr('title', 'click to expand outout');
54 this.collapse_button.html('. . .');
54 this.collapse_button.html('. . .');
55
55
56 this.prompt_overlay.addClass('out_prompt_overlay prompt');
56 this.prompt_overlay.addClass('out_prompt_overlay prompt');
57 this.prompt_overlay.attr('title', 'click to expand outout; double click to hide output');
57 this.prompt_overlay.attr('title', 'click to expand outout; double click to hide output');
58
58
59 this.collapse();
59 this.collapse();
60 };
60 };
61
61
62
62
63 OutputArea.prototype._should_scroll = function (lines) {
63 OutputArea.prototype._should_scroll = function (lines) {
64 if (!lines) {
64 if (!lines) {
65 lines = 100;
65 lines = 100;
66 }
66 }
67 // line-height from http://stackoverflow.com/questions/1185151
67 // line-height from http://stackoverflow.com/questions/1185151
68 var fontSize = this.element.css('font-size');
68 var fontSize = this.element.css('font-size');
69 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
69 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
70
70
71 return (this.element.height() > lines * lineHeight);
71 return (this.element.height() > lines * lineHeight);
72 };
72 };
73
73
74
74
75 OutputArea.prototype.bind_events = function () {
75 OutputArea.prototype.bind_events = function () {
76 var that = this;
76 var that = this;
77 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
77 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
78 this.prompt_overlay.click(function () { that.toggle_scroll(); });
78 this.prompt_overlay.click(function () { that.toggle_scroll(); });
79
79
80 this.element.resize(function () {
80 this.element.resize(function () {
81 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
81 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
82 if ( $.browser.mozilla ) {
82 if ( $.browser.mozilla ) {
83 return;
83 return;
84 }
84 }
85 // maybe scroll output,
85 // maybe scroll output,
86 // if it's grown large enough and hasn't already been scrolled.
86 // if it's grown large enough and hasn't already been scrolled.
87 if ( !that.scrolled && that._should_scroll()) {
87 if ( !that.scrolled && that._should_scroll()) {
88 that.scroll_area();
88 that.scroll_area();
89 }
89 }
90 });
90 });
91 this.collapse_button.click(function () {
91 this.collapse_button.click(function () {
92 that.expand();
92 that.expand();
93 });
93 });
94 this.collapse_button.hover(function () {
94 this.collapse_button.hover(function () {
95 $(this).addClass("ui-state-hover");
95 $(this).addClass("ui-state-hover");
96 }, function () {
96 }, function () {
97 $(this).removeClass("ui-state-hover");
97 $(this).removeClass("ui-state-hover");
98 });
98 });
99 };
99 };
100
100
101
101
102 OutputArea.prototype.collapse = function () {
102 OutputArea.prototype.collapse = function () {
103 if (!this.collapsed) {
103 if (!this.collapsed) {
104 this.element.hide();
104 this.element.hide();
105 this.prompt_overlay.hide();
105 this.prompt_overlay.hide();
106 if (this.element.html()){
106 if (this.element.html()){
107 this.collapse_button.show();
107 this.collapse_button.show();
108 }
108 }
109 this.collapsed = true;
109 this.collapsed = true;
110 };
110 };
111 };
111 };
112
112
113
113
114 OutputArea.prototype.expand = function () {
114 OutputArea.prototype.expand = function () {
115 if (this.collapsed) {
115 if (this.collapsed) {
116 this.collapse_button.hide();
116 this.collapse_button.hide();
117 this.element.show();
117 this.element.show();
118 this.prompt_overlay.show();
118 this.prompt_overlay.show();
119 this.collapsed = false;
119 this.collapsed = false;
120 };
120 };
121 };
121 };
122
122
123
123
124 OutputArea.prototype.toggle_output = function () {
124 OutputArea.prototype.toggle_output = function () {
125 if (this.collapsed) {
125 if (this.collapsed) {
126 this.expand();
126 this.expand();
127 } else {
127 } else {
128 this.collapse();
128 this.collapse();
129 };
129 };
130 };
130 };
131
131
132
132
133 OutputArea.prototype.scroll_area = function () {
133 OutputArea.prototype.scroll_area = function () {
134 this.element.addClass('output_scroll');
134 this.element.addClass('output_scroll');
135 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
135 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
136 this.scrolled = true;
136 this.scrolled = true;
137 };
137 };
138
138
139
139
140 OutputArea.prototype.unscroll_area = function () {
140 OutputArea.prototype.unscroll_area = function () {
141 this.element.removeClass('output_scroll');
141 this.element.removeClass('output_scroll');
142 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
142 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
143 this.scrolled = false;
143 this.scrolled = false;
144 };
144 };
145
145
146
146
147 OutputArea.prototype.scroll_if_long = function (lines) {
147 OutputArea.prototype.scroll_if_long = function (lines) {
148 if (this._should_scroll(lines)) {
148 if (this._should_scroll(lines)) {
149 // only allow scrolling long-enough output
149 // only allow scrolling long-enough output
150 this.scroll_area();
150 this.scroll_area();
151 };
151 };
152 };
152 };
153
153
154
154
155 OutputArea.prototype.toggle_scroll = function () {
155 OutputArea.prototype.toggle_scroll = function () {
156 if (this.scrolled) {
156 if (this.scrolled) {
157 this.unscroll_area();
157 this.unscroll_area();
158 } else {
158 } else {
159 // only allow scrolling long-enough output
159 // only allow scrolling long-enough output
160 this.scroll_if_long(20);
160 this.scroll_if_long(20);
161 };
161 };
162 };
162 };
163
163
164
164
165 // typeset with MathJax if MathJax is available
165 // typeset with MathJax if MathJax is available
166 OutputArea.prototype.typeset = function () {
166 OutputArea.prototype.typeset = function () {
167 if (window.MathJax){
167 if (window.MathJax){
168 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
168 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
169 }
169 }
170 };
170 };
171
171
172
172
173 OutputArea.prototype.handle_output = function (msg_type, content) {
173 OutputArea.prototype.handle_output = function (msg_type, content) {
174 var json = {};
174 var json = {};
175 json.output_type = msg_type;
175 json.output_type = msg_type;
176 if (msg_type === "stream") {
176 if (msg_type === "stream") {
177 json.text = content.data;
177 json.text = content.data;
178 json.stream = content.name;
178 json.stream = content.name;
179 } else if (msg_type === "display_data") {
179 } else if (msg_type === "display_data") {
180 json = this.convert_mime_types(json, content.data);
180 json = this.convert_mime_types(json, content.data);
181 } else if (msg_type === "pyout") {
181 } else if (msg_type === "pyout") {
182 json.prompt_number = content.execution_count;
182 json.prompt_number = content.execution_count;
183 json = this.convert_mime_types(json, content.data);
183 json = this.convert_mime_types(json, content.data);
184 } else if (msg_type === "pyerr") {
184 } else if (msg_type === "pyerr") {
185 json.ename = content.ename;
185 json.ename = content.ename;
186 json.evalue = content.evalue;
186 json.evalue = content.evalue;
187 json.traceback = content.traceback;
187 json.traceback = content.traceback;
188 };
188 };
189 // append with dynamic=true
189 // append with dynamic=true
190 this.append_output(json, true);
190 this.append_output(json, true);
191 };
191 };
192
192
193
193
194 OutputArea.prototype.convert_mime_types = function (json, data) {
194 OutputArea.prototype.convert_mime_types = function (json, data) {
195 if (data['text/plain'] !== undefined) {
195 if (data['text/plain'] !== undefined) {
196 json.text = data['text/plain'];
196 json.text = data['text/plain'];
197 };
197 };
198 if (data['text/html'] !== undefined) {
198 if (data['text/html'] !== undefined) {
199 json.html = data['text/html'];
199 json.html = data['text/html'];
200 };
200 };
201 if (data['image/svg+xml'] !== undefined) {
201 if (data['image/svg+xml'] !== undefined) {
202 json.svg = data['image/svg+xml'];
202 json.svg = data['image/svg+xml'];
203 };
203 };
204 if (data['image/png'] !== undefined) {
204 if (data['image/png'] !== undefined) {
205 json.png = data['image/png'];
205 json.png = data['image/png'];
206 };
206 };
207 if (data['image/jpeg'] !== undefined) {
207 if (data['image/jpeg'] !== undefined) {
208 json.jpeg = data['image/jpeg'];
208 json.jpeg = data['image/jpeg'];
209 };
209 };
210 if (data['text/latex'] !== undefined) {
210 if (data['text/latex'] !== undefined) {
211 json.latex = data['text/latex'];
211 json.latex = data['text/latex'];
212 };
212 };
213 if (data['application/json'] !== undefined) {
213 if (data['application/json'] !== undefined) {
214 json.json = data['application/json'];
214 json.json = data['application/json'];
215 };
215 };
216 if (data['application/javascript'] !== undefined) {
216 if (data['application/javascript'] !== undefined) {
217 json.javascript = data['application/javascript'];
217 json.javascript = data['application/javascript'];
218 }
218 }
219 return json;
219 return json;
220 };
220 };
221
221
222
222
223 OutputArea.prototype.append_output = function (json, dynamic) {
223 OutputArea.prototype.append_output = function (json, dynamic) {
224 // If dynamic is true, javascript output will be eval'd.
224 // If dynamic is true, javascript output will be eval'd.
225 this.expand();
225 this.expand();
226 this.flush_clear_timeout();
226 this.flush_clear_timeout();
227 if (json.output_type === 'pyout') {
227 if (json.output_type === 'pyout') {
228 this.append_pyout(json, dynamic);
228 this.append_pyout(json, dynamic);
229 } else if (json.output_type === 'pyerr') {
229 } else if (json.output_type === 'pyerr') {
230 this.append_pyerr(json);
230 this.append_pyerr(json);
231 } else if (json.output_type === 'display_data') {
231 } else if (json.output_type === 'display_data') {
232 this.append_display_data(json, dynamic);
232 this.append_display_data(json, dynamic);
233 } else if (json.output_type === 'stream') {
233 } else if (json.output_type === 'stream') {
234 this.append_stream(json);
234 this.append_stream(json);
235 };
235 };
236 this.outputs.push(json);
236 this.outputs.push(json);
237 var that = this;
237 var that = this;
238 setTimeout(function(){that.element.trigger('resize');}, 100);
238 setTimeout(function(){that.element.trigger('resize');}, 100);
239 };
239 };
240
240
241
241
242 OutputArea.prototype.create_output_area = function () {
242 OutputArea.prototype.create_output_area = function () {
243 var oa = $("<div/>").addClass("hbox output_area");
243 var oa = $("<div/>").addClass("hbox output_area");
244 if (this.prompt_area) {
244 if (this.prompt_area) {
245 oa.append($('<div/>').addClass('prompt'));
245 oa.append($('<div/>').addClass('prompt'));
246 }
246 }
247 return oa;
247 return oa;
248 };
248 };
249
249
250
250
251 OutputArea.prototype.append_pyout = function (json, dynamic) {
251 OutputArea.prototype.append_pyout = function (json, dynamic) {
252 var n = json.prompt_number || ' ';
252 var n = json.prompt_number || ' ';
253 var toinsert = this.create_output_area();
253 var toinsert = this.create_output_area();
254 if (this.prompt_area) {
254 if (this.prompt_area) {
255 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
255 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
256 }
256 }
257 this.append_mime_type(json, toinsert, dynamic);
257 this.append_mime_type(json, toinsert, dynamic);
258 this.element.append(toinsert);
258 this.element.append(toinsert);
259 // If we just output latex, typeset it.
259 // If we just output latex, typeset it.
260 if ((json.latex !== undefined) || (json.html !== undefined)) {
260 if ((json.latex !== undefined) || (json.html !== undefined)) {
261 this.typeset();
261 this.typeset();
262 };
262 };
263 };
263 };
264
264
265
265
266 OutputArea.prototype.append_pyerr = function (json) {
266 OutputArea.prototype.append_pyerr = function (json) {
267 var tb = json.traceback;
267 var tb = json.traceback;
268 if (tb !== undefined && tb.length > 0) {
268 if (tb !== undefined && tb.length > 0) {
269 var s = '';
269 var s = '';
270 var len = tb.length;
270 var len = tb.length;
271 for (var i=0; i<len; i++) {
271 for (var i=0; i<len; i++) {
272 s = s + tb[i] + '\n';
272 s = s + tb[i] + '\n';
273 }
273 }
274 s = s + '\n';
274 s = s + '\n';
275 var toinsert = this.create_output_area();
275 var toinsert = this.create_output_area();
276 this.append_text(s, toinsert);
276 this.append_text(s, toinsert);
277 this.element.append(toinsert);
277 this.element.append(toinsert);
278 };
278 };
279 };
279 };
280
280
281
281
282 OutputArea.prototype.append_stream = function (json) {
282 OutputArea.prototype.append_stream = function (json) {
283 // temporary fix: if stream undefined (json file written prior to this patch),
283 // temporary fix: if stream undefined (json file written prior to this patch),
284 // default to most likely stdout:
284 // default to most likely stdout:
285 if (json.stream == undefined){
285 if (json.stream == undefined){
286 json.stream = 'stdout';
286 json.stream = 'stdout';
287 }
287 }
288 var text = json.text;
288 var text = json.text;
289 var subclass = "output_"+json.stream;
289 var subclass = "output_"+json.stream;
290 if (this.outputs.length > 0){
290 if (this.outputs.length > 0){
291 // have at least one output to consider
291 // have at least one output to consider
292 var last = this.outputs[this.outputs.length-1];
292 var last = this.outputs[this.outputs.length-1];
293 if (last.output_type == 'stream' && json.stream == last.stream){
293 if (last.output_type == 'stream' && json.stream == last.stream){
294 // latest output was in the same stream,
294 // latest output was in the same stream,
295 // so append directly into its pre tag
295 // so append directly into its pre tag
296 // escape ANSI & HTML specials:
296 // escape ANSI & HTML specials:
297 var pre = this.element.find('div.'+subclass).last().find('pre');
297 var pre = this.element.find('div.'+subclass).last().find('pre');
298 var html = utils.fixCarriageReturn(
298 var html = utils.fixCarriageReturn(
299 pre.html() + utils.fixConsole(text));
299 pre.html() + utils.fixConsole(text));
300 pre.html(html);
300 pre.html(html);
301 return;
301 return;
302 }
302 }
303 }
303 }
304
304
305 if (!text.replace("\r", "")) {
305 if (!text.replace("\r", "")) {
306 // text is nothing (empty string, \r, etc.)
306 // text is nothing (empty string, \r, etc.)
307 // so don't append any elements, which might add undesirable space
307 // so don't append any elements, which might add undesirable space
308 return;
308 return;
309 }
309 }
310
310
311 // If we got here, attach a new div
311 // If we got here, attach a new div
312 var toinsert = this.create_output_area();
312 var toinsert = this.create_output_area();
313 this.append_text(text, toinsert, "output_stream "+subclass);
313 this.append_text(text, toinsert, "output_stream "+subclass);
314 this.element.append(toinsert);
314 this.element.append(toinsert);
315 };
315 };
316
316
317
317
318 OutputArea.prototype.append_display_data = function (json, dynamic) {
318 OutputArea.prototype.append_display_data = function (json, dynamic) {
319 var toinsert = this.create_output_area();
319 var toinsert = this.create_output_area();
320 this.append_mime_type(json, toinsert, dynamic);
320 this.append_mime_type(json, toinsert, dynamic);
321 this.element.append(toinsert);
321 this.element.append(toinsert);
322 // If we just output latex, typeset it.
322 // If we just output latex, typeset it.
323 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
323 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
324 this.typeset();
324 this.typeset();
325 };
325 };
326 };
326 };
327
327
328
328
329 OutputArea.prototype.append_mime_type = function (json, element, dynamic) {
329 OutputArea.prototype.append_mime_type = function (json, element, dynamic) {
330 if (json.javascript !== undefined && dynamic) {
330 if (json.javascript !== undefined && dynamic) {
331 this.append_javascript(json.javascript, element, dynamic);
331 this.append_javascript(json.javascript, element, dynamic);
332 } else if (json.html !== undefined) {
332 } else if (json.html !== undefined) {
333 this.append_html(json.html, element);
333 this.append_html(json.html, element);
334 } else if (json.latex !== undefined) {
334 } else if (json.latex !== undefined) {
335 this.append_latex(json.latex, element);
335 this.append_latex(json.latex, element);
336 } else if (json.svg !== undefined) {
336 } else if (json.svg !== undefined) {
337 this.append_svg(json.svg, element);
337 this.append_svg(json.svg, element);
338 } else if (json.png !== undefined) {
338 } else if (json.png !== undefined) {
339 this.append_png(json.png, element);
339 this.append_png(json.png, element);
340 } else if (json.jpeg !== undefined) {
340 } else if (json.jpeg !== undefined) {
341 this.append_jpeg(json.jpeg, element);
341 this.append_jpeg(json.jpeg, element);
342 } else if (json.text !== undefined) {
342 } else if (json.text !== undefined) {
343 this.append_text(json.text, element);
343 this.append_text(json.text, element);
344 };
344 };
345 };
345 };
346
346
347
347
348 OutputArea.prototype.append_html = function (html, element) {
348 OutputArea.prototype.append_html = function (html, element) {
349 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_html rendered_html");
349 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_html rendered_html");
350 toinsert.append(html);
350 toinsert.append(html);
351 element.append(toinsert);
351 element.append(toinsert);
352 };
352 };
353
353
354
354
355 OutputArea.prototype.append_javascript = function (js, container) {
355 OutputArea.prototype.append_javascript = function (js, container) {
356 // We just eval the JS code, element appears in the local scope.
356 // We just eval the JS code, element appears in the local scope.
357 var element = $("<div/>").addClass("box-flex1 output_subarea");
357 var element = $("<div/>").addClass("box-flex1 output_subarea");
358 container.append(element);
358 container.append(element);
359 // Div for js shouldn't be drawn, as it will add empty height to the area.
359 // Div for js shouldn't be drawn, as it will add empty height to the area.
360 container.hide();
360 container.hide();
361 // If the Javascript appends content to `element` that should be drawn, then
361 // If the Javascript appends content to `element` that should be drawn, then
362 // it must also call `container.show()`.
362 // it must also call `container.show()`.
363 try {
363 try {
364 eval(js);
364 eval(js);
365 } catch(err) {
365 } catch(err) {
366 console.log('Error in Javascript!');
366 console.log('Error in Javascript!');
367 console.log(err);
367 console.log(err);
368 container.show();
368 container.show();
369 element.append($('<div/>')
369 element.append($('<div/>')
370 .html("Error in Javascript !<br/>"+
370 .html("Error in Javascript !<br/>"+
371 err.toString()+
371 err.toString()+
372 '<br/>See your browser Javascript console for more details.')
372 '<br/>See your browser Javascript console for more details.')
373 .addClass('js-error')
373 .addClass('js-error')
374 );
374 );
375 }
375 }
376 }
376 }
377
377
378
378
379 OutputArea.prototype.append_text = function (data, element, extra_class) {
379 OutputArea.prototype.append_text = function (data, element, extra_class) {
380 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_text");
380 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_text");
381 // escape ANSI & HTML specials in plaintext:
381 // escape ANSI & HTML specials in plaintext:
382 data = utils.wrapUrls(data);
382 data = utils.fixConsole(data);
383 data = utils.fixConsole(data);
383 data = utils.fixCarriageReturn(data);
384 data = utils.fixCarriageReturn(data);
385 data = utils.autoLinkUrls(data);
384 if (extra_class){
386 if (extra_class){
385 toinsert.addClass(extra_class);
387 toinsert.addClass(extra_class);
386 }
388 }
387 toinsert.append($("<pre/>").html(data));
389 toinsert.append($("<pre/>").html(data));
388 element.append(toinsert);
390 element.append(toinsert);
389 };
391 };
390
392
391
393
392 OutputArea.prototype.append_svg = function (svg, element) {
394 OutputArea.prototype.append_svg = function (svg, element) {
393 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_svg");
395 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_svg");
394 toinsert.append(svg);
396 toinsert.append(svg);
395 element.append(toinsert);
397 element.append(toinsert);
396 };
398 };
397
399
398
400
399 OutputArea.prototype._dblclick_to_reset_size = function (img) {
401 OutputArea.prototype._dblclick_to_reset_size = function (img) {
400 // schedule wrapping image in resizable after a delay,
402 // schedule wrapping image in resizable after a delay,
401 // so we don't end up calling resize on a zero-size object
403 // so we don't end up calling resize on a zero-size object
402 var that = this;
404 var that = this;
403 setTimeout(function () {
405 setTimeout(function () {
404 var h0 = img.height();
406 var h0 = img.height();
405 var w0 = img.width();
407 var w0 = img.width();
406 if (!(h0 && w0)) {
408 if (!(h0 && w0)) {
407 // zero size, schedule another timeout
409 // zero size, schedule another timeout
408 that._dblclick_to_reset_size(img);
410 that._dblclick_to_reset_size(img);
409 return
411 return
410 }
412 }
411 img.resizable({
413 img.resizable({
412 aspectRatio: true,
414 aspectRatio: true,
413 autoHide: true
415 autoHide: true
414 });
416 });
415 img.dblclick(function () {
417 img.dblclick(function () {
416 // resize wrapper & image together for some reason:
418 // resize wrapper & image together for some reason:
417 img.parent().height(h0);
419 img.parent().height(h0);
418 img.height(h0);
420 img.height(h0);
419 img.parent().width(w0);
421 img.parent().width(w0);
420 img.width(w0);
422 img.width(w0);
421 });
423 });
422 }, 250);
424 }, 250);
423 }
425 }
424
426
425
427
426 OutputArea.prototype.append_png = function (png, element) {
428 OutputArea.prototype.append_png = function (png, element) {
427 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_png");
429 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_png");
428 var img = $("<img/>").attr('src','data:image/png;base64,'+png);
430 var img = $("<img/>").attr('src','data:image/png;base64,'+png);
429 this._dblclick_to_reset_size(img);
431 this._dblclick_to_reset_size(img);
430 toinsert.append(img);
432 toinsert.append(img);
431 element.append(toinsert);
433 element.append(toinsert);
432 };
434 };
433
435
434
436
435 OutputArea.prototype.append_jpeg = function (jpeg, element) {
437 OutputArea.prototype.append_jpeg = function (jpeg, element) {
436 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_jpeg");
438 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_jpeg");
437 var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
439 var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
438 this._dblclick_to_reset_size(img);
440 this._dblclick_to_reset_size(img);
439 toinsert.append(img);
441 toinsert.append(img);
440 element.append(toinsert);
442 element.append(toinsert);
441 };
443 };
442
444
443
445
444 OutputArea.prototype.append_latex = function (latex, element) {
446 OutputArea.prototype.append_latex = function (latex, element) {
445 // This method cannot do the typesetting because the latex first has to
447 // This method cannot do the typesetting because the latex first has to
446 // be on the page.
448 // be on the page.
447 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_latex");
449 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_latex");
448 toinsert.append(latex);
450 toinsert.append(latex);
449 element.append(toinsert);
451 element.append(toinsert);
450 };
452 };
451
453
452
454
453 OutputArea.prototype.handle_clear_output = function (content) {
455 OutputArea.prototype.handle_clear_output = function (content) {
454 this.clear_output(content.stdout, content.stderr, content.other);
456 this.clear_output(content.stdout, content.stderr, content.other);
455 }
457 }
456
458
457
459
458 OutputArea.prototype.clear_output = function (stdout, stderr, other) {
460 OutputArea.prototype.clear_output = function (stdout, stderr, other) {
459 var that = this;
461 var that = this;
460 if (this.clear_out_timeout != null){
462 if (this.clear_out_timeout != null){
461 // fire previous pending clear *immediately*
463 // fire previous pending clear *immediately*
462 clearTimeout(this.clear_out_timeout);
464 clearTimeout(this.clear_out_timeout);
463 this.clear_out_timeout = null;
465 this.clear_out_timeout = null;
464 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
466 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
465 }
467 }
466 // store flags for flushing the timeout
468 // store flags for flushing the timeout
467 this._clear_stdout = stdout;
469 this._clear_stdout = stdout;
468 this._clear_stderr = stderr;
470 this._clear_stderr = stderr;
469 this._clear_other = other;
471 this._clear_other = other;
470 this.clear_out_timeout = setTimeout(function() {
472 this.clear_out_timeout = setTimeout(function() {
471 // really clear timeout only after a short delay
473 // really clear timeout only after a short delay
472 // this reduces flicker in 'clear_output; print' cases
474 // this reduces flicker in 'clear_output; print' cases
473 that.clear_out_timeout = null;
475 that.clear_out_timeout = null;
474 that._clear_stdout = that._clear_stderr = that._clear_other = null;
476 that._clear_stdout = that._clear_stderr = that._clear_other = null;
475 that.clear_output_callback(stdout, stderr, other);
477 that.clear_output_callback(stdout, stderr, other);
476 }, 500
478 }, 500
477 );
479 );
478 };
480 };
479
481
480
482
481 OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) {
483 OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) {
482 var output_div = this.element;
484 var output_div = this.element;
483
485
484 if (stdout && stderr && other){
486 if (stdout && stderr && other){
485 // clear all, no need for logic
487 // clear all, no need for logic
486 output_div.html("");
488 output_div.html("");
487 this.outputs = [];
489 this.outputs = [];
488 this.unscroll_area();
490 this.unscroll_area();
489 return;
491 return;
490 }
492 }
491 // remove html output
493 // remove html output
492 // each output_subarea that has an identifying class is in an output_area
494 // each output_subarea that has an identifying class is in an output_area
493 // which is the element to be removed.
495 // which is the element to be removed.
494 if (stdout) {
496 if (stdout) {
495 output_div.find("div.output_stdout").parent().remove();
497 output_div.find("div.output_stdout").parent().remove();
496 }
498 }
497 if (stderr) {
499 if (stderr) {
498 output_div.find("div.output_stderr").parent().remove();
500 output_div.find("div.output_stderr").parent().remove();
499 }
501 }
500 if (other) {
502 if (other) {
501 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
503 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
502 }
504 }
503 this.unscroll_area();
505 this.unscroll_area();
504
506
505 // remove cleared outputs from JSON list:
507 // remove cleared outputs from JSON list:
506 for (var i = this.outputs.length - 1; i >= 0; i--) {
508 for (var i = this.outputs.length - 1; i >= 0; i--) {
507 var out = this.outputs[i];
509 var out = this.outputs[i];
508 var output_type = out.output_type;
510 var output_type = out.output_type;
509 if (output_type == "display_data" && other) {
511 if (output_type == "display_data" && other) {
510 this.outputs.splice(i,1);
512 this.outputs.splice(i,1);
511 } else if (output_type == "stream") {
513 } else if (output_type == "stream") {
512 if (stdout && out.stream == "stdout") {
514 if (stdout && out.stream == "stdout") {
513 this.outputs.splice(i,1);
515 this.outputs.splice(i,1);
514 } else if (stderr && out.stream == "stderr") {
516 } else if (stderr && out.stream == "stderr") {
515 this.outputs.splice(i,1);
517 this.outputs.splice(i,1);
516 }
518 }
517 }
519 }
518 }
520 }
519 };
521 };
520
522
521
523
522 OutputArea.prototype.flush_clear_timeout = function() {
524 OutputArea.prototype.flush_clear_timeout = function() {
523 var output_div = this.element;
525 var output_div = this.element;
524 if (this.clear_out_timeout){
526 if (this.clear_out_timeout){
525 clearTimeout(this.clear_out_timeout);
527 clearTimeout(this.clear_out_timeout);
526 this.clear_out_timeout = null;
528 this.clear_out_timeout = null;
527 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
529 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
528 };
530 };
529 }
531 }
530
532
531
533
532 // JSON serialization
534 // JSON serialization
533
535
534 OutputArea.prototype.fromJSON = function (outputs) {
536 OutputArea.prototype.fromJSON = function (outputs) {
535 var len = outputs.length;
537 var len = outputs.length;
536 for (var i=0; i<len; i++) {
538 for (var i=0; i<len; i++) {
537 // append with dynamic=false.
539 // append with dynamic=false.
538 this.append_output(outputs[i], false);
540 this.append_output(outputs[i], false);
539 };
541 };
540 };
542 };
541
543
542
544
543 OutputArea.prototype.toJSON = function () {
545 OutputArea.prototype.toJSON = function () {
544 var outputs = [];
546 var outputs = [];
545 var len = this.outputs.length;
547 var len = this.outputs.length;
546 for (var i=0; i<len; i++) {
548 for (var i=0; i<len; i++) {
547 outputs[i] = this.outputs[i];
549 outputs[i] = this.outputs[i];
548 };
550 };
549 return outputs;
551 return outputs;
550 };
552 };
551
553
552
554
553 IPython.OutputArea = OutputArea;
555 IPython.OutputArea = OutputArea;
554
556
555 return IPython;
557 return IPython;
556
558
557 }(IPython));
559 }(IPython));
@@ -1,72 +1,73 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // QuickHelp button
9 // QuickHelp button
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var QuickHelp = function (selector) {
14 var QuickHelp = function (selector) {
15 };
15 };
16
16
17 QuickHelp.prototype.show_keyboard_shortcuts = function () {
17 QuickHelp.prototype.show_keyboard_shortcuts = function () {
18 // toggles display of keyboard shortcut dialog
18 // toggles display of keyboard shortcut dialog
19 var that = this;
19 var that = this;
20 if ( this.shortcut_dialog ){
20 if ( this.shortcut_dialog ){
21 // if dialog is already shown, close it
21 // if dialog is already shown, close it
22 this.shortcut_dialog.dialog("close");
22 this.shortcut_dialog.dialog("close");
23 this.shortcut_dialog = null;
23 this.shortcut_dialog = null;
24 return;
24 return;
25 }
25 }
26 var dialog = $('<div/>');
26 var dialog = $('<div/>');
27 this.shortcut_dialog = dialog;
27 this.shortcut_dialog = dialog;
28 var shortcuts = [
28 var shortcuts = [
29 {key: 'Shift-Enter', help: 'run cell'},
29 {key: 'Shift-Enter', help: 'run cell'},
30 {key: 'Ctrl-Enter', help: 'run cell in-place'},
30 {key: 'Ctrl-Enter', help: 'run cell in-place'},
31 {key: 'Alt-Enter', help: 'run cell, insert below'},
31 {key: 'Alt-Enter', help: 'run cell, insert below'},
32 {key: 'Ctrl-m x', help: 'cut cell'},
32 {key: 'Ctrl-m x', help: 'cut cell'},
33 {key: 'Ctrl-m c', help: 'copy cell'},
33 {key: 'Ctrl-m c', help: 'copy cell'},
34 {key: 'Ctrl-m v', help: 'paste cell'},
34 {key: 'Ctrl-m v', help: 'paste cell'},
35 {key: 'Ctrl-m d', help: 'delete cell'},
35 {key: 'Ctrl-m d', help: 'delete cell'},
36 {key: 'Ctrl-m z', help: 'undo last cell deletion'},
36 {key: 'Ctrl-m a', help: 'insert cell above'},
37 {key: 'Ctrl-m a', help: 'insert cell above'},
37 {key: 'Ctrl-m b', help: 'insert cell below'},
38 {key: 'Ctrl-m b', help: 'insert cell below'},
38 {key: 'Ctrl-m o', help: 'toggle output'},
39 {key: 'Ctrl-m o', help: 'toggle output'},
39 {key: 'Ctrl-m O', help: 'toggle output scroll'},
40 {key: 'Ctrl-m O', help: 'toggle output scroll'},
40 {key: 'Ctrl-m l', help: 'toggle line numbers'},
41 {key: 'Ctrl-m l', help: 'toggle line numbers'},
41 {key: 'Ctrl-m s', help: 'save notebook'},
42 {key: 'Ctrl-m s', help: 'save notebook'},
42 {key: 'Ctrl-m j', help: 'move cell down'},
43 {key: 'Ctrl-m j', help: 'move cell down'},
43 {key: 'Ctrl-m k', help: 'move cell up'},
44 {key: 'Ctrl-m k', help: 'move cell up'},
44 {key: 'Ctrl-m y', help: 'code cell'},
45 {key: 'Ctrl-m y', help: 'code cell'},
45 {key: 'Ctrl-m m', help: 'markdown cell'},
46 {key: 'Ctrl-m m', help: 'markdown cell'},
46 {key: 'Ctrl-m t', help: 'raw cell'},
47 {key: 'Ctrl-m t', help: 'raw cell'},
47 {key: 'Ctrl-m 1-6', help: 'heading 1-6 cell'},
48 {key: 'Ctrl-m 1-6', help: 'heading 1-6 cell'},
48 {key: 'Ctrl-m p', help: 'select previous'},
49 {key: 'Ctrl-m p', help: 'select previous'},
49 {key: 'Ctrl-m n', help: 'select next'},
50 {key: 'Ctrl-m n', help: 'select next'},
50 {key: 'Ctrl-m i', help: 'interrupt kernel'},
51 {key: 'Ctrl-m i', help: 'interrupt kernel'},
51 {key: 'Ctrl-m .', help: 'restart kernel'},
52 {key: 'Ctrl-m .', help: 'restart kernel'},
52 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
53 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
53 ];
54 ];
54 for (var i=0; i<shortcuts.length; i++) {
55 for (var i=0; i<shortcuts.length; i++) {
55 dialog.append($('<div>').
56 dialog.append($('<div>').
56 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
57 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
57 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
58 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
58 );
59 );
59 };
60 };
60 dialog.bind('dialogclose', function(event) {
61 dialog.bind('dialogclose', function(event) {
61 // dialog has been closed, allow it to be drawn again.
62 // dialog has been closed, allow it to be drawn again.
62 that.shortcut_dialog = null;
63 that.shortcut_dialog = null;
63 });
64 });
64 dialog.dialog({title: 'Keyboard shortcuts', closeText: ''});
65 dialog.dialog({title: 'Keyboard shortcuts', closeText: ''});
65 };
66 };
66
67
67 // Set module variables
68 // Set module variables
68 IPython.QuickHelp = QuickHelp;
69 IPython.QuickHelp = QuickHelp;
69
70
70 return IPython;
71 return IPython;
71
72
72 }(IPython));
73 }(IPython));
@@ -1,421 +1,417 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2012 The IPython Development Team
2 // Copyright (C) 2008-2012 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // TextCell
9 // TextCell
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 // TextCell base class
14 // TextCell base class
15 var key = IPython.utils.keycodes;
15 var key = IPython.utils.keycodes;
16
16
17 var TextCell = function () {
17 var TextCell = function () {
18 this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed';
18 this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed';
19 IPython.Cell.apply(this, arguments);
19 IPython.Cell.apply(this, arguments);
20 this.rendered = false;
20 this.rendered = false;
21 this.cell_type = this.cell_type || 'text';
21 this.cell_type = this.cell_type || 'text';
22 };
22 };
23
23
24
24
25 TextCell.prototype = new IPython.Cell();
25 TextCell.prototype = new IPython.Cell();
26
26
27
27
28 TextCell.prototype.create_element = function () {
28 TextCell.prototype.create_element = function () {
29 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
29 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
30 cell.attr('tabindex','2');
30 cell.attr('tabindex','2');
31 var input_area = $('<div/>').addClass('text_cell_input border-box-sizing');
31 var input_area = $('<div/>').addClass('text_cell_input border-box-sizing');
32 this.code_mirror = CodeMirror(input_area.get(0), {
32 this.code_mirror = CodeMirror(input_area.get(0), {
33 indentUnit : 4,
33 indentUnit : 4,
34 mode: this.code_mirror_mode,
34 mode: this.code_mirror_mode,
35 theme: 'default',
35 theme: 'default',
36 value: this.placeholder,
36 value: this.placeholder,
37 readOnly: this.read_only,
37 readOnly: this.read_only,
38 lineWrapping : true,
38 lineWrapping : true,
39 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
39 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
40 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
40 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
41 });
41 });
42 // The tabindex=-1 makes this div focusable.
42 // The tabindex=-1 makes this div focusable.
43 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
43 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
44 addClass('rendered_html').attr('tabindex','-1');
44 addClass('rendered_html').attr('tabindex','-1');
45 cell.append(input_area).append(render_area);
45 cell.append(input_area).append(render_area);
46 this.element = cell;
46 this.element = cell;
47 };
47 };
48
48
49
49
50 TextCell.prototype.bind_events = function () {
50 TextCell.prototype.bind_events = function () {
51 IPython.Cell.prototype.bind_events.apply(this);
51 IPython.Cell.prototype.bind_events.apply(this);
52 var that = this;
52 var that = this;
53 this.element.keydown(function (event) {
53 this.element.keydown(function (event) {
54 if (event.which === 13 && !event.shiftKey) {
54 if (event.which === 13 && !event.shiftKey) {
55 if (that.rendered) {
55 if (that.rendered) {
56 that.edit();
56 that.edit();
57 return false;
57 return false;
58 };
58 };
59 };
59 };
60 });
60 });
61 this.element.dblclick(function () {
61 this.element.dblclick(function () {
62 that.edit();
62 that.edit();
63 });
63 });
64 };
64 };
65
65
66
66
67 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
67 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
68 // This method gets called in CodeMirror's onKeyDown/onKeyPress
68 // This method gets called in CodeMirror's onKeyDown/onKeyPress
69 // handlers and is used to provide custom key handling. Its return
69 // handlers and is used to provide custom key handling. Its return
70 // value is used to determine if CodeMirror should ignore the event:
70 // value is used to determine if CodeMirror should ignore the event:
71 // true = ignore, false = don't ignore.
71 // true = ignore, false = don't ignore.
72
72
73 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
73 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
74 // Always ignore shift-enter in CodeMirror as we handle it.
74 // Always ignore shift-enter in CodeMirror as we handle it.
75 return true;
75 return true;
76 }
76 }
77 return false;
77 return false;
78 };
78 };
79
79
80
80
81 TextCell.prototype.select = function () {
81 TextCell.prototype.select = function () {
82 IPython.Cell.prototype.select.apply(this);
82 IPython.Cell.prototype.select.apply(this);
83 var output = this.element.find("div.text_cell_render");
83 var output = this.element.find("div.text_cell_render");
84 output.trigger('focus');
84 output.trigger('focus');
85 };
85 };
86
86
87
87
88 TextCell.prototype.unselect = function() {
88 TextCell.prototype.unselect = function() {
89 // render on selection of another cell
89 // render on selection of another cell
90 this.render();
90 this.render();
91 IPython.Cell.prototype.unselect.apply(this);
91 IPython.Cell.prototype.unselect.apply(this);
92 };
92 };
93
93
94
94
95 TextCell.prototype.edit = function () {
95 TextCell.prototype.edit = function () {
96 if ( this.read_only ) return;
96 if ( this.read_only ) return;
97 if (this.rendered === true) {
97 if (this.rendered === true) {
98 var text_cell = this.element;
98 var text_cell = this.element;
99 var output = text_cell.find("div.text_cell_render");
99 var output = text_cell.find("div.text_cell_render");
100 output.hide();
100 output.hide();
101 text_cell.find('div.text_cell_input').show();
101 text_cell.find('div.text_cell_input').show();
102 this.code_mirror.refresh();
102 this.code_mirror.refresh();
103 this.code_mirror.focus();
103 this.code_mirror.focus();
104 // We used to need an additional refresh() after the focus, but
104 // We used to need an additional refresh() after the focus, but
105 // it appears that this has been fixed in CM. This bug would show
105 // it appears that this has been fixed in CM. This bug would show
106 // up on FF when a newly loaded markdown cell was edited.
106 // up on FF when a newly loaded markdown cell was edited.
107 this.rendered = false;
107 this.rendered = false;
108 if (this.get_text() === this.placeholder) {
108 if (this.get_text() === this.placeholder) {
109 this.set_text('');
109 this.set_text('');
110 this.refresh();
110 this.refresh();
111 }
111 }
112 }
112 }
113 };
113 };
114
114
115
115
116 // Subclasses must define render.
116 // Subclasses must define render.
117 TextCell.prototype.render = function () {};
117 TextCell.prototype.render = function () {};
118
118
119
119
120 TextCell.prototype.get_text = function() {
120 TextCell.prototype.get_text = function() {
121 return this.code_mirror.getValue();
121 return this.code_mirror.getValue();
122 };
122 };
123
123
124
124
125 TextCell.prototype.set_text = function(text) {
125 TextCell.prototype.set_text = function(text) {
126 this.code_mirror.setValue(text);
126 this.code_mirror.setValue(text);
127 this.code_mirror.refresh();
127 this.code_mirror.refresh();
128 };
128 };
129
129
130
130
131 TextCell.prototype.get_rendered = function() {
131 TextCell.prototype.get_rendered = function() {
132 return this.element.find('div.text_cell_render').html();
132 return this.element.find('div.text_cell_render').html();
133 };
133 };
134
134
135
135
136 TextCell.prototype.set_rendered = function(text) {
136 TextCell.prototype.set_rendered = function(text) {
137 this.element.find('div.text_cell_render').html(text);
137 this.element.find('div.text_cell_render').html(text);
138 };
138 };
139
139
140
140
141 TextCell.prototype.at_top = function () {
141 TextCell.prototype.at_top = function () {
142 if (this.rendered) {
142 if (this.rendered) {
143 return true;
143 return true;
144 } else {
144 } else {
145 return false;
145 return false;
146 }
146 }
147 };
147 };
148
148
149
149
150 TextCell.prototype.at_bottom = function () {
150 TextCell.prototype.at_bottom = function () {
151 if (this.rendered) {
151 if (this.rendered) {
152 return true;
152 return true;
153 } else {
153 } else {
154 return false;
154 return false;
155 }
155 }
156 };
156 };
157
157
158
158
159 TextCell.prototype.fromJSON = function (data) {
159 TextCell.prototype.fromJSON = function (data) {
160 IPython.Cell.prototype.fromJSON.apply(this, arguments);
160 IPython.Cell.prototype.fromJSON.apply(this, arguments);
161 if (data.cell_type === this.cell_type) {
161 if (data.cell_type === this.cell_type) {
162 if (data.source !== undefined) {
162 if (data.source !== undefined) {
163 this.set_text(data.source);
163 this.set_text(data.source);
164 // make this value the starting point, so that we can only undo
164 // make this value the starting point, so that we can only undo
165 // to this state, instead of a blank cell
165 // to this state, instead of a blank cell
166 this.code_mirror.clearHistory();
166 this.code_mirror.clearHistory();
167 this.set_rendered(data.rendered || '');
167 this.set_rendered(data.rendered || '');
168 this.rendered = false;
168 this.rendered = false;
169 this.render();
169 this.render();
170 }
170 }
171 }
171 }
172 };
172 };
173
173
174
174
175 TextCell.prototype.toJSON = function () {
175 TextCell.prototype.toJSON = function () {
176 var data = IPython.Cell.prototype.toJSON.apply(this);
176 var data = IPython.Cell.prototype.toJSON.apply(this);
177 data.cell_type = this.cell_type;
177 data.cell_type = this.cell_type;
178 data.source = this.get_text();
178 data.source = this.get_text();
179 return data;
179 return data;
180 };
180 };
181
181
182
182
183 // HTMLCell
183 // HTMLCell
184
184
185 var HTMLCell = function () {
185 var HTMLCell = function () {
186 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$";
186 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$";
187 IPython.TextCell.apply(this, arguments);
187 IPython.TextCell.apply(this, arguments);
188 this.cell_type = 'html';
188 this.cell_type = 'html';
189 };
189 };
190
190
191
191
192 HTMLCell.prototype = new TextCell();
192 HTMLCell.prototype = new TextCell();
193
193
194
194
195 HTMLCell.prototype.render = function () {
195 HTMLCell.prototype.render = function () {
196 if (this.rendered === false) {
196 if (this.rendered === false) {
197 var text = this.get_text();
197 var text = this.get_text();
198 if (text === "") { text = this.placeholder; }
198 if (text === "") { text = this.placeholder; }
199 this.set_rendered(text);
199 this.set_rendered(text);
200 this.typeset();
200 this.typeset();
201 this.element.find('div.text_cell_input').hide();
201 this.element.find('div.text_cell_input').hide();
202 this.element.find("div.text_cell_render").show();
202 this.element.find("div.text_cell_render").show();
203 this.rendered = true;
203 this.rendered = true;
204 }
204 }
205 };
205 };
206
206
207
207
208 // MarkdownCell
208 // MarkdownCell
209
209
210 var MarkdownCell = function () {
210 var MarkdownCell = function () {
211 this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$";
211 this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$";
212 IPython.TextCell.apply(this, arguments);
212 IPython.TextCell.apply(this, arguments);
213 this.cell_type = 'markdown';
213 this.cell_type = 'markdown';
214 };
214 };
215
215
216
216
217 MarkdownCell.prototype = new TextCell();
217 MarkdownCell.prototype = new TextCell();
218
218
219
219
220 MarkdownCell.prototype.render = function () {
220 MarkdownCell.prototype.render = function () {
221 if (this.rendered === false) {
221 if (this.rendered === false) {
222 var text = this.get_text();
222 var text = this.get_text();
223 if (text === "") { text = this.placeholder; }
223 if (text === "") { text = this.placeholder; }
224
225 text = IPython.mathjaxutils.remove_math(text)
224 text = IPython.mathjaxutils.remove_math(text)
226 var html = IPython.markdown_converter.makeHtml(text);
225 var html = IPython.markdown_converter.makeHtml(text);
227 html = IPython.mathjaxutils.replace_math(html)
226 html = IPython.mathjaxutils.replace_math(html)
228
229 try {
227 try {
230 this.set_rendered(html);
228 this.set_rendered(html);
231 } catch (e) {
229 } catch (e) {
232 console.log("Error running Javascript in Markdown:");
230 console.log("Error running Javascript in Markdown:");
233 console.log(e);
231 console.log(e);
234 this.set_rendered($("<div/>").addClass("js-error").html(
232 this.set_rendered($("<div/>").addClass("js-error").html(
235 "Error rendering Markdown!<br/>" + e.toString())
233 "Error rendering Markdown!<br/>" + e.toString())
236 );
234 );
237 }
235 }
238 this.typeset()
239 this.element.find('div.text_cell_input').hide();
236 this.element.find('div.text_cell_input').hide();
240 this.element.find("div.text_cell_render").show();
237 this.element.find("div.text_cell_render").show();
241 var code_snippets = this.element.find("pre > code");
238 var code_snippets = this.element.find("pre > code");
242 code_snippets.replaceWith(function () {
239 code_snippets.replaceWith(function () {
243 var code = $(this).html();
240 var code = $(this).html();
244 /* Substitute br for newlines and &nbsp; for spaces
241 /* Substitute br for newlines and &nbsp; for spaces
245 before highlighting, since prettify doesn't
242 before highlighting, since prettify doesn't
246 preserve those on all browsers */
243 preserve those on all browsers */
247 code = code.replace(/(\r\n|\n|\r)/gm, "<br/>");
244 code = code.replace(/(\r\n|\n|\r)/gm, "<br/>");
248 code = code.replace(/ /gm, '&nbsp;');
245 code = code.replace(/ /gm, '&nbsp;');
249 code = prettyPrintOne(code);
246 code = prettyPrintOne(code);
250
247
251 return '<code class="prettyprint">' + code + '</code>';
248 return '<code class="prettyprint">' + code + '</code>';
252 });
249 });
253
250 this.typeset()
254 IPython.mathjaxutils.queue_render()
255 this.rendered = true;
251 this.rendered = true;
256 }
252 }
257 };
253 };
258
254
259
255
260 // RawCell
256 // RawCell
261
257
262 var RawCell = function () {
258 var RawCell = function () {
263 this.placeholder = "Type plain text and LaTeX: $\\alpha^2$";
259 this.placeholder = "Type plain text and LaTeX: $\\alpha^2$";
264 this.code_mirror_mode = 'rst';
260 this.code_mirror_mode = 'rst';
265 IPython.TextCell.apply(this, arguments);
261 IPython.TextCell.apply(this, arguments);
266 this.cell_type = 'raw';
262 this.cell_type = 'raw';
267 var that = this
263 var that = this
268
264
269 this.element.focusout(
265 this.element.focusout(
270 function() { that.auto_highlight(); }
266 function() { that.auto_highlight(); }
271 );
267 );
272 };
268 };
273
269
274
270
275 RawCell.prototype = new TextCell();
271 RawCell.prototype = new TextCell();
276
272
277 RawCell.prototype.auto_highlight = function () {
273 RawCell.prototype.auto_highlight = function () {
278 this._auto_highlight(IPython.config.raw_cell_highlight);
274 this._auto_highlight(IPython.config.raw_cell_highlight);
279 };
275 };
280
276
281 RawCell.prototype.render = function () {
277 RawCell.prototype.render = function () {
282 this.rendered = true;
278 this.rendered = true;
283 this.edit();
279 this.edit();
284 };
280 };
285
281
286
282
287 RawCell.prototype.handle_codemirror_keyevent = function (editor, event) {
283 RawCell.prototype.handle_codemirror_keyevent = function (editor, event) {
288 // This method gets called in CodeMirror's onKeyDown/onKeyPress
284 // This method gets called in CodeMirror's onKeyDown/onKeyPress
289 // handlers and is used to provide custom key handling. Its return
285 // handlers and is used to provide custom key handling. Its return
290 // value is used to determine if CodeMirror should ignore the event:
286 // value is used to determine if CodeMirror should ignore the event:
291 // true = ignore, false = don't ignore.
287 // true = ignore, false = don't ignore.
292
288
293 var that = this;
289 var that = this;
294 if (event.which === key.UPARROW && event.type === 'keydown') {
290 if (event.which === key.UPARROW && event.type === 'keydown') {
295 // If we are not at the top, let CM handle the up arrow and
291 // If we are not at the top, let CM handle the up arrow and
296 // prevent the global keydown handler from handling it.
292 // prevent the global keydown handler from handling it.
297 if (!that.at_top()) {
293 if (!that.at_top()) {
298 event.stop();
294 event.stop();
299 return false;
295 return false;
300 } else {
296 } else {
301 return true;
297 return true;
302 };
298 };
303 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
299 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
304 // If we are not at the bottom, let CM handle the down arrow and
300 // If we are not at the bottom, let CM handle the down arrow and
305 // prevent the global keydown handler from handling it.
301 // prevent the global keydown handler from handling it.
306 if (!that.at_bottom()) {
302 if (!that.at_bottom()) {
307 event.stop();
303 event.stop();
308 return false;
304 return false;
309 } else {
305 } else {
310 return true;
306 return true;
311 };
307 };
312 };
308 };
313 return false;
309 return false;
314 };
310 };
315
311
316
312
317 RawCell.prototype.select = function () {
313 RawCell.prototype.select = function () {
318 IPython.Cell.prototype.select.apply(this);
314 IPython.Cell.prototype.select.apply(this);
319 this.code_mirror.refresh();
315 this.code_mirror.refresh();
320 this.code_mirror.focus();
316 this.code_mirror.focus();
321 };
317 };
322
318
323
319
324 RawCell.prototype.at_top = function () {
320 RawCell.prototype.at_top = function () {
325 var cursor = this.code_mirror.getCursor();
321 var cursor = this.code_mirror.getCursor();
326 if (cursor.line === 0 && cursor.ch === 0) {
322 if (cursor.line === 0 && cursor.ch === 0) {
327 return true;
323 return true;
328 } else {
324 } else {
329 return false;
325 return false;
330 }
326 }
331 };
327 };
332
328
333
329
334 RawCell.prototype.at_bottom = function () {
330 RawCell.prototype.at_bottom = function () {
335 var cursor = this.code_mirror.getCursor();
331 var cursor = this.code_mirror.getCursor();
336 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
332 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
337 return true;
333 return true;
338 } else {
334 } else {
339 return false;
335 return false;
340 }
336 }
341 };
337 };
342
338
343
339
344 // HTMLCell
340 // HTMLCell
345
341
346 var HeadingCell = function () {
342 var HeadingCell = function () {
347 this.placeholder = "Type Heading Here";
343 this.placeholder = "Type Heading Here";
348 IPython.TextCell.apply(this, arguments);
344 IPython.TextCell.apply(this, arguments);
349 this.cell_type = 'heading';
345 this.cell_type = 'heading';
350 this.level = 1;
346 this.level = 1;
351 };
347 };
352
348
353
349
354 HeadingCell.prototype = new TextCell();
350 HeadingCell.prototype = new TextCell();
355
351
356
352
357 HeadingCell.prototype.fromJSON = function (data) {
353 HeadingCell.prototype.fromJSON = function (data) {
358 if (data.level != undefined){
354 if (data.level != undefined){
359 this.level = data.level;
355 this.level = data.level;
360 }
356 }
361 IPython.TextCell.prototype.fromJSON.apply(this, arguments);
357 IPython.TextCell.prototype.fromJSON.apply(this, arguments);
362 };
358 };
363
359
364
360
365 HeadingCell.prototype.toJSON = function () {
361 HeadingCell.prototype.toJSON = function () {
366 var data = IPython.TextCell.prototype.toJSON.apply(this);
362 var data = IPython.TextCell.prototype.toJSON.apply(this);
367 data.level = this.get_level();
363 data.level = this.get_level();
368 return data;
364 return data;
369 };
365 };
370
366
371
367
372 HeadingCell.prototype.set_level = function (level) {
368 HeadingCell.prototype.set_level = function (level) {
373 this.level = level;
369 this.level = level;
374 if (this.rendered) {
370 if (this.rendered) {
375 this.rendered = false;
371 this.rendered = false;
376 this.render();
372 this.render();
377 };
373 };
378 };
374 };
379
375
380
376
381 HeadingCell.prototype.get_level = function () {
377 HeadingCell.prototype.get_level = function () {
382 return this.level;
378 return this.level;
383 };
379 };
384
380
385
381
386 HeadingCell.prototype.set_rendered = function (text) {
382 HeadingCell.prototype.set_rendered = function (text) {
387 var r = this.element.find("div.text_cell_render");
383 var r = this.element.find("div.text_cell_render");
388 r.empty();
384 r.empty();
389 r.append($('<h'+this.level+'/>').html(text));
385 r.append($('<h'+this.level+'/>').html(text));
390 };
386 };
391
387
392
388
393 HeadingCell.prototype.get_rendered = function () {
389 HeadingCell.prototype.get_rendered = function () {
394 var r = this.element.find("div.text_cell_render");
390 var r = this.element.find("div.text_cell_render");
395 return r.children().first().html();
391 return r.children().first().html();
396 };
392 };
397
393
398
394
399 HeadingCell.prototype.render = function () {
395 HeadingCell.prototype.render = function () {
400 if (this.rendered === false) {
396 if (this.rendered === false) {
401 var text = this.get_text();
397 var text = this.get_text();
402 if (text === "") { text = this.placeholder; }
398 if (text === "") { text = this.placeholder; }
403 this.set_rendered(text);
399 this.set_rendered(text);
404 this.typeset();
400 this.typeset();
405 this.element.find('div.text_cell_input').hide();
401 this.element.find('div.text_cell_input').hide();
406 this.element.find("div.text_cell_render").show();
402 this.element.find("div.text_cell_render").show();
407 this.rendered = true;
403 this.rendered = true;
408 };
404 };
409 };
405 };
410
406
411 IPython.TextCell = TextCell;
407 IPython.TextCell = TextCell;
412 IPython.HTMLCell = HTMLCell;
408 IPython.HTMLCell = HTMLCell;
413 IPython.MarkdownCell = MarkdownCell;
409 IPython.MarkdownCell = MarkdownCell;
414 IPython.RawCell = RawCell;
410 IPython.RawCell = RawCell;
415 IPython.HeadingCell = HeadingCell;
411 IPython.HeadingCell = HeadingCell;
416
412
417
413
418 return IPython;
414 return IPython;
419
415
420 }(IPython));
416 }(IPython));
421
417
@@ -1,267 +1,288 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2012 The IPython Development Team
2 // Copyright (C) 2008-2012 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Utilities
9 // Utilities
10 //============================================================================
10 //============================================================================
11
11
12 IPython.namespace('IPython.utils');
12 IPython.namespace('IPython.utils');
13
13
14 IPython.utils = (function (IPython) {
14 IPython.utils = (function (IPython) {
15
15
16 //============================================================================
16 //============================================================================
17 // Cross-browser RegEx Split
17 // Cross-browser RegEx Split
18 //============================================================================
18 //============================================================================
19
19
20 // This code has been MODIFIED from the code licensed below to not replace the
20 // This code has been MODIFIED from the code licensed below to not replace the
21 // default browser split. The license is reproduced here.
21 // default browser split. The license is reproduced here.
22
22
23 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
23 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
24 /*!
24 /*!
25 * Cross-Browser Split 1.1.1
25 * Cross-Browser Split 1.1.1
26 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
26 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
27 * Available under the MIT License
27 * Available under the MIT License
28 * ECMAScript compliant, uniform cross-browser split method
28 * ECMAScript compliant, uniform cross-browser split method
29 */
29 */
30
30
31 /**
31 /**
32 * Splits a string into an array of strings using a regex or string
32 * Splits a string into an array of strings using a regex or string
33 * separator. Matches of the separator are not included in the result array.
33 * separator. Matches of the separator are not included in the result array.
34 * However, if `separator` is a regex that contains capturing groups,
34 * However, if `separator` is a regex that contains capturing groups,
35 * backreferences are spliced into the result each time `separator` is
35 * backreferences are spliced into the result each time `separator` is
36 * matched. Fixes browser bugs compared to the native
36 * matched. Fixes browser bugs compared to the native
37 * `String.prototype.split` and can be used reliably cross-browser.
37 * `String.prototype.split` and can be used reliably cross-browser.
38 * @param {String} str String to split.
38 * @param {String} str String to split.
39 * @param {RegExp|String} separator Regex or string to use for separating
39 * @param {RegExp|String} separator Regex or string to use for separating
40 * the string.
40 * the string.
41 * @param {Number} [limit] Maximum number of items to include in the result
41 * @param {Number} [limit] Maximum number of items to include in the result
42 * array.
42 * array.
43 * @returns {Array} Array of substrings.
43 * @returns {Array} Array of substrings.
44 * @example
44 * @example
45 *
45 *
46 * // Basic use
46 * // Basic use
47 * regex_split('a b c d', ' ');
47 * regex_split('a b c d', ' ');
48 * // -> ['a', 'b', 'c', 'd']
48 * // -> ['a', 'b', 'c', 'd']
49 *
49 *
50 * // With limit
50 * // With limit
51 * regex_split('a b c d', ' ', 2);
51 * regex_split('a b c d', ' ', 2);
52 * // -> ['a', 'b']
52 * // -> ['a', 'b']
53 *
53 *
54 * // Backreferences in result array
54 * // Backreferences in result array
55 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
55 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
56 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
56 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
57 */
57 */
58 var regex_split = function (str, separator, limit) {
58 var regex_split = function (str, separator, limit) {
59 // If `separator` is not a regex, use `split`
59 // If `separator` is not a regex, use `split`
60 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
60 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
61 return split.call(str, separator, limit);
61 return split.call(str, separator, limit);
62 }
62 }
63 var output = [],
63 var output = [],
64 flags = (separator.ignoreCase ? "i" : "") +
64 flags = (separator.ignoreCase ? "i" : "") +
65 (separator.multiline ? "m" : "") +
65 (separator.multiline ? "m" : "") +
66 (separator.extended ? "x" : "") + // Proposed for ES6
66 (separator.extended ? "x" : "") + // Proposed for ES6
67 (separator.sticky ? "y" : ""), // Firefox 3+
67 (separator.sticky ? "y" : ""), // Firefox 3+
68 lastLastIndex = 0,
68 lastLastIndex = 0,
69 // Make `global` and avoid `lastIndex` issues by working with a copy
69 // Make `global` and avoid `lastIndex` issues by working with a copy
70 separator = new RegExp(separator.source, flags + "g"),
70 separator = new RegExp(separator.source, flags + "g"),
71 separator2, match, lastIndex, lastLength;
71 separator2, match, lastIndex, lastLength;
72 str += ""; // Type-convert
72 str += ""; // Type-convert
73
73
74 compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined"
74 compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined"
75 if (!compliantExecNpcg) {
75 if (!compliantExecNpcg) {
76 // Doesn't need flags gy, but they don't hurt
76 // Doesn't need flags gy, but they don't hurt
77 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
77 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
78 }
78 }
79 /* Values for `limit`, per the spec:
79 /* Values for `limit`, per the spec:
80 * If undefined: 4294967295 // Math.pow(2, 32) - 1
80 * If undefined: 4294967295 // Math.pow(2, 32) - 1
81 * If 0, Infinity, or NaN: 0
81 * If 0, Infinity, or NaN: 0
82 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
82 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
83 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
83 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
84 * If other: Type-convert, then use the above rules
84 * If other: Type-convert, then use the above rules
85 */
85 */
86 limit = typeof(limit) === "undefined" ?
86 limit = typeof(limit) === "undefined" ?
87 -1 >>> 0 : // Math.pow(2, 32) - 1
87 -1 >>> 0 : // Math.pow(2, 32) - 1
88 limit >>> 0; // ToUint32(limit)
88 limit >>> 0; // ToUint32(limit)
89 while (match = separator.exec(str)) {
89 while (match = separator.exec(str)) {
90 // `separator.lastIndex` is not reliable cross-browser
90 // `separator.lastIndex` is not reliable cross-browser
91 lastIndex = match.index + match[0].length;
91 lastIndex = match.index + match[0].length;
92 if (lastIndex > lastLastIndex) {
92 if (lastIndex > lastLastIndex) {
93 output.push(str.slice(lastLastIndex, match.index));
93 output.push(str.slice(lastLastIndex, match.index));
94 // Fix browsers whose `exec` methods don't consistently return `undefined` for
94 // Fix browsers whose `exec` methods don't consistently return `undefined` for
95 // nonparticipating capturing groups
95 // nonparticipating capturing groups
96 if (!compliantExecNpcg && match.length > 1) {
96 if (!compliantExecNpcg && match.length > 1) {
97 match[0].replace(separator2, function () {
97 match[0].replace(separator2, function () {
98 for (var i = 1; i < arguments.length - 2; i++) {
98 for (var i = 1; i < arguments.length - 2; i++) {
99 if (typeof(arguments[i]) === "undefined") {
99 if (typeof(arguments[i]) === "undefined") {
100 match[i] = undefined;
100 match[i] = undefined;
101 }
101 }
102 }
102 }
103 });
103 });
104 }
104 }
105 if (match.length > 1 && match.index < str.length) {
105 if (match.length > 1 && match.index < str.length) {
106 Array.prototype.push.apply(output, match.slice(1));
106 Array.prototype.push.apply(output, match.slice(1));
107 }
107 }
108 lastLength = match[0].length;
108 lastLength = match[0].length;
109 lastLastIndex = lastIndex;
109 lastLastIndex = lastIndex;
110 if (output.length >= limit) {
110 if (output.length >= limit) {
111 break;
111 break;
112 }
112 }
113 }
113 }
114 if (separator.lastIndex === match.index) {
114 if (separator.lastIndex === match.index) {
115 separator.lastIndex++; // Avoid an infinite loop
115 separator.lastIndex++; // Avoid an infinite loop
116 }
116 }
117 }
117 }
118 if (lastLastIndex === str.length) {
118 if (lastLastIndex === str.length) {
119 if (lastLength || !separator.test("")) {
119 if (lastLength || !separator.test("")) {
120 output.push("");
120 output.push("");
121 }
121 }
122 } else {
122 } else {
123 output.push(str.slice(lastLastIndex));
123 output.push(str.slice(lastLastIndex));
124 }
124 }
125 return output.length > limit ? output.slice(0, limit) : output;
125 return output.length > limit ? output.slice(0, limit) : output;
126 };
126 };
127
127
128 //============================================================================
128 //============================================================================
129 // End contributed Cross-browser RegEx Split
129 // End contributed Cross-browser RegEx Split
130 //============================================================================
130 //============================================================================
131
131
132
132
133 var uuid = function () {
133 var uuid = function () {
134 // http://www.ietf.org/rfc/rfc4122.txt
134 // http://www.ietf.org/rfc/rfc4122.txt
135 var s = [];
135 var s = [];
136 var hexDigits = "0123456789ABCDEF";
136 var hexDigits = "0123456789ABCDEF";
137 for (var i = 0; i < 32; i++) {
137 for (var i = 0; i < 32; i++) {
138 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
138 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
139 }
139 }
140 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
140 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
141 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
141 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
142
142
143 var uuid = s.join("");
143 var uuid = s.join("");
144 return uuid;
144 return uuid;
145 };
145 };
146
146
147
147
148 //Fix raw text to parse correctly in crazy XML
148 //Fix raw text to parse correctly in crazy XML
149 function xmlencode(string) {
149 function xmlencode(string) {
150 return string.replace(/\&/g,'&'+'amp;')
150 return string.replace(/\&/g,'&'+'amp;')
151 .replace(/</g,'&'+'lt;')
151 .replace(/</g,'&'+'lt;')
152 .replace(/>/g,'&'+'gt;')
152 .replace(/>/g,'&'+'gt;')
153 .replace(/\'/g,'&'+'apos;')
153 .replace(/\'/g,'&'+'apos;')
154 .replace(/\"/g,'&'+'quot;')
154 .replace(/\"/g,'&'+'quot;')
155 .replace(/`/g,'&'+'#96;');
155 .replace(/`/g,'&'+'#96;');
156 }
156 }
157
157
158
158
159 //Map from terminal commands to CSS classes
159 //Map from terminal commands to CSS classes
160 ansi_colormap = {
160 ansi_colormap = {
161 "30":"ansiblack", "31":"ansired",
161 "30":"ansiblack", "31":"ansired",
162 "32":"ansigreen", "33":"ansiyellow",
162 "32":"ansigreen", "33":"ansiyellow",
163 "34":"ansiblue", "35":"ansipurple","36":"ansicyan",
163 "34":"ansiblue", "35":"ansipurple","36":"ansicyan",
164 "37":"ansigrey", "01":"ansibold"
164 "37":"ansigrey", "01":"ansibold"
165 };
165 };
166
166
167 // Transform ANSI color escape codes into HTML <span> tags with css
167 // Transform ANSI color escape codes into HTML <span> tags with css
168 // classes listed in the above ansi_colormap object. The actual color used
168 // classes listed in the above ansi_colormap object. The actual color used
169 // are set in the css file.
169 // are set in the css file.
170 function fixConsole(txt) {
170 function fixConsole(txt) {
171 txt = xmlencode(txt);
171 txt = xmlencode(txt);
172 var re = /\033\[([\dA-Fa-f;]*?)m/;
172 var re = /\033\[([\dA-Fa-f;]*?)m/;
173 var opened = false;
173 var opened = false;
174 var cmds = [];
174 var cmds = [];
175 var opener = "";
175 var opener = "";
176 var closer = "";
176 var closer = "";
177 while (re.test(txt)) {
177 while (re.test(txt)) {
178 var cmds = txt.match(re)[1].split(";");
178 var cmds = txt.match(re)[1].split(";");
179 closer = opened?"</span>":"";
179 closer = opened?"</span>":"";
180 opened = cmds.length > 1 || cmds[0] != 0;
180 opened = cmds.length > 1 || cmds[0] != 0;
181 var rep = [];
181 var rep = [];
182 for (var i in cmds)
182 for (var i in cmds)
183 if (typeof(ansi_colormap[cmds[i]]) != "undefined")
183 if (typeof(ansi_colormap[cmds[i]]) != "undefined")
184 rep.push(ansi_colormap[cmds[i]]);
184 rep.push(ansi_colormap[cmds[i]]);
185 opener = rep.length > 0?"<span class=\""+rep.join(" ")+"\">":"";
185 opener = rep.length > 0?"<span class=\""+rep.join(" ")+"\">":"";
186 txt = txt.replace(re, closer + opener);
186 txt = txt.replace(re, closer + opener);
187 }
187 }
188 if (opened) txt += "</span>";
188 if (opened) txt += "</span>";
189 return txt;
189 return txt;
190 }
190 }
191
191
192 // Remove chunks that should be overridden by the effect of
192 // Remove chunks that should be overridden by the effect of
193 // carriage return characters
193 // carriage return characters
194 function fixCarriageReturn(txt) {
194 function fixCarriageReturn(txt) {
195 tmp = txt;
195 tmp = txt;
196 do {
196 do {
197 txt = tmp;
197 txt = tmp;
198 tmp = txt.replace(/^.*\r(?!\n)/gm, '');
198 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
199 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
199 } while (tmp.length < txt.length);
200 } while (tmp.length < txt.length);
200 return txt;
201 return txt;
201 }
202 }
202
203
204 // Locate URLs in plain text and wrap them in spaces so that they can be
205 // better picked out by autoLinkUrls even after the text has been
206 // converted to HTML
207 function wrapUrls(txt) {
208 // Note this regexp is a modified version of one from
209 // Markdown.Converter For now it only supports http(s) and ftp URLs,
210 // but could easily support others (though file:// should maybe be
211 // avoided)
212 var url_re = /(^|\W)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi;
213 return txt.replace(url_re, "$1 $2$3 $4");
214 }
215
216 // Locate a URL with spaces around it and convert that to a anchor tag
217 function autoLinkUrls(txt) {
218 return txt.replace(/ ((https?|ftp):[^'">\s]+) /gi,
219 "<a target=\"_blank\" href=\"$1\">$1</a>");
220 }
221
203 grow = function(element) {
222 grow = function(element) {
204 // Grow the cell by hand. This is used upon reloading from JSON, when the
223 // Grow the cell by hand. This is used upon reloading from JSON, when the
205 // autogrow handler is not called.
224 // autogrow handler is not called.
206 var dom = element.get(0);
225 var dom = element.get(0);
207 var lines_count = 0;
226 var lines_count = 0;
208 // modified split rule from
227 // modified split rule from
209 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
228 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
210 var lines = dom.value.split(/\r|\r\n|\n/);
229 var lines = dom.value.split(/\r|\r\n|\n/);
211 lines_count = lines.length;
230 lines_count = lines.length;
212 if (lines_count >= 1) {
231 if (lines_count >= 1) {
213 dom.rows = lines_count;
232 dom.rows = lines_count;
214 } else {
233 } else {
215 dom.rows = 1;
234 dom.rows = 1;
216 }
235 }
217 };
236 };
218
237
219 // some keycodes that seem to be platform/browser independant
238 // some keycodes that seem to be platform/browser independant
220 var keycodes ={
239 var keycodes ={
221 BACKSPACE: 8,
240 BACKSPACE: 8,
222 TAB : 9,
241 TAB : 9,
223 ENTER : 13,
242 ENTER : 13,
224 SHIFT : 16,
243 SHIFT : 16,
225 CTRL : 17,
244 CTRL : 17,
226 CONTROL : 17,
245 CONTROL : 17,
227 ALT : 18,
246 ALT : 18,
228 ESC : 27,
247 ESC : 27,
229 SPACE : 32,
248 SPACE : 32,
230 PGUP : 33,
249 PGUP : 33,
231 PGDOWN : 34,
250 PGDOWN : 34,
232 LEFT_ARROW: 37,
251 LEFT_ARROW: 37,
233 LEFTARROW: 37,
252 LEFTARROW: 37,
234 LEFT : 37,
253 LEFT : 37,
235 UP_ARROW : 38,
254 UP_ARROW : 38,
236 UPARROW : 38,
255 UPARROW : 38,
237 UP : 38,
256 UP : 38,
238 RIGHT_ARROW:39,
257 RIGHT_ARROW:39,
239 RIGHTARROW:39,
258 RIGHTARROW:39,
240 RIGHT : 39,
259 RIGHT : 39,
241 DOWN_ARROW: 40,
260 DOWN_ARROW: 40,
242 DOWNARROW: 40,
261 DOWNARROW: 40,
243 DOWN : 40,
262 DOWN : 40,
244 };
263 };
245
264
246
265
247 points_to_pixels = function (points) {
266 points_to_pixels = function (points) {
248 // A reasonably good way of converting between points and pixels.
267 // A reasonably good way of converting between points and pixels.
249 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
268 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
250 $(body).append(test);
269 $(body).append(test);
251 var pixel_per_point = test.width()/10000;
270 var pixel_per_point = test.width()/10000;
252 test.remove();
271 test.remove();
253 return Math.floor(points*pixel_per_point);
272 return Math.floor(points*pixel_per_point);
254 }
273 }
255
274
256
275
257 return {
276 return {
258 regex_split : regex_split,
277 regex_split : regex_split,
259 uuid : uuid,
278 uuid : uuid,
260 fixConsole : fixConsole,
279 fixConsole : fixConsole,
261 keycodes : keycodes,
280 keycodes : keycodes,
262 grow : grow,
281 grow : grow,
263 fixCarriageReturn : fixCarriageReturn,
282 fixCarriageReturn : fixCarriageReturn,
283 wrapUrls : wrapUrls,
284 autoLinkUrls : autoLinkUrls,
264 points_to_pixels : points_to_pixels
285 points_to_pixels : points_to_pixels
265 };
286 };
266
287
267 }(IPython));
288 }(IPython));
@@ -1,224 +1,226 b''
1 {% extends page.html %}
1 {% extends page.html %}
2 {% block stylesheet %}
2 {% block stylesheet %}
3
3
4 {% if mathjax_url %}
4 {% if mathjax_url %}
5 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
5 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
6 {% end %}
6 {% end %}
7 <script type="text/javascript">
7 <script type="text/javascript">
8 // MathJax disabled, set as null to distingish from *missing* MathJax,
8 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 // where it will be undefined, and should prompt a dialog later.
9 // where it will be undefined, and should prompt a dialog later.
10 window.mathjax_url = "{{mathjax_url}}";
10 window.mathjax_url = "{{mathjax_url}}";
11 </script>
11 </script>
12
12
13 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
13 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
14 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
14 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
15
15
16 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
16 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
17
17
18 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
18 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("css/tooltip.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("css/tooltip.css") }}" type="text/css" />
20 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
20 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
21
21
22 <link rel="stylesheet" href="{{ static_url("css/printnotebook.css") }}" type="text/css" media="print"/>
22 <link rel="stylesheet" href="{{ static_url("css/printnotebook.css") }}" type="text/css" media="print"/>
23
23
24 {% end %}
24 {% end %}
25
25
26
26
27 {% block params %}
27 {% block params %}
28
28
29 data-project={{project}}
29 data-project={{project}}
30 data-base-project-url={{base_project_url}}
30 data-base-project-url={{base_project_url}}
31 data-base-kernel-url={{base_kernel_url}}
31 data-base-kernel-url={{base_kernel_url}}
32 data-read-only={{read_only and not logged_in}}
32 data-read-only={{read_only and not logged_in}}
33 data-notebook-id={{notebook_id}}
33 data-notebook-id={{notebook_id}}
34
34
35 {% end %}
35 {% end %}
36
36
37
37
38 {% block header %}
38 {% block header %}
39
39
40 <span id="save_widget">
40 <span id="save_widget">
41 <span id="notebook_name"></span>
41 <span id="notebook_name"></span>
42 <span id="save_status"></span>
42 <span id="save_status"></span>
43 </span>
43 </span>
44
44
45 {% end %}
45 {% end %}
46
46
47
47
48 {% block site %}
48 {% block site %}
49
49
50 <div id="menubar_container">
50 <div id="menubar_container">
51 <div id="menubar">
51 <div id="menubar">
52 <ul id="menus">
52 <ul id="menus">
53 <li><a href="#">File</a>
53 <li><a href="#">File</a>
54 <ul>
54 <ul>
55 <li id="new_notebook"><a href="#">New</a></li>
55 <li id="new_notebook"><a href="#">New</a></li>
56 <li id="open_notebook"><a href="#">Open...</a></li>
56 <li id="open_notebook"><a href="#">Open...</a></li>
57 <hr/>
57 <hr/>
58 <li id="copy_notebook"><a href="#">Make a Copy...</a></li>
58 <li id="copy_notebook"><a href="#">Make a Copy...</a></li>
59 <li id="rename_notebook"><a href="#">Rename...</a></li>
59 <li id="rename_notebook"><a href="#">Rename...</a></li>
60 <li id="save_notebook"><a href="#">Save</a></li>
60 <li id="save_notebook"><a href="#">Save</a></li>
61 <hr/>
61 <hr/>
62 <li><a href="#">Download as</a>
62 <li><a href="#">Download as</a>
63 <ul>
63 <ul>
64 <li id="download_ipynb"><a href="#">IPython (.ipynb)</a></li>
64 <li id="download_ipynb"><a href="#">IPython (.ipynb)</a></li>
65 <li id="download_py"><a href="#">Python (.py)</a></li>
65 <li id="download_py"><a href="#">Python (.py)</a></li>
66 </ul>
66 </ul>
67 </li>
67 </li>
68 <hr/>
68 <hr/>
69 <li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>
69 <li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>
70 <hr/>
70 <hr/>
71 <li id="kill_and_exit"><a href="#" >Close and halt</a></li>
71 <li id="kill_and_exit"><a href="#" >Close and halt</a></li>
72 </ul>
72 </ul>
73 </li>
73 </li>
74 <li><a href="#">Edit</a>
74 <li><a href="#">Edit</a>
75 <ul>
75 <ul>
76 <li id="cut_cell"><a href="#">Cut Cell</a></li>
76 <li id="cut_cell"><a href="#">Cut Cell</a></li>
77 <li id="copy_cell"><a href="#">Copy Cell</a></li>
77 <li id="copy_cell"><a href="#">Copy Cell</a></li>
78 <li id="paste_cell" class="ui-state-disabled"><a href="#">Paste Cell</a></li>
79 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li>
78 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li>
80 <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Cell Below</a></li>
79 <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Cell Below</a></li>
80 <li id="paste_cell_replace" class="ui-state-disabled"><a href="#">Paste Cell &amp; Replace</a></li>
81 <li id="delete_cell"><a href="#">Delete</a></li>
81 <li id="delete_cell"><a href="#">Delete</a></li>
82 <hr/>
82 <hr/>
83 <li id="split_cell"><a href="#">Split Cell</a></li>
83 <li id="split_cell"><a href="#">Split Cell</a></li>
84 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
84 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
85 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
85 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
86 <hr/>
86 <hr/>
87 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
87 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
88 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
88 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
89 <hr/>
89 <hr/>
90 <li id="select_previous"><a href="#">Select Previous Cell</a></li>
90 <li id="select_previous"><a href="#">Select Previous Cell</a></li>
91 <li id="select_next"><a href="#">Select Next Cell</a></li>
91 <li id="select_next"><a href="#">Select Next Cell</a></li>
92 </ul>
92 </ul>
93 </li>
93 </li>
94 <li><a href="#">View</a>
94 <li><a href="#">View</a>
95 <ul>
95 <ul>
96 <li id="toggle_header"><a href="#">Toggle Header</a></li>
96 <li id="toggle_header"><a href="#">Toggle Header</a></li>
97 <li id="toggle_toolbar"><a href="#">Toggle Toolbar</a></li>
97 <li id="toggle_toolbar"><a href="#">Toggle Toolbar</a></li>
98 </ul>
98 </ul>
99 </li>
99 </li>
100 <li><a href="#">Insert</a>
100 <li><a href="#">Insert</a>
101 <ul>
101 <ul>
102 <li id="insert_cell_above"><a href="#">Insert Cell Above</a></li>
102 <li id="insert_cell_above"><a href="#">Insert Cell Above</a></li>
103 <li id="insert_cell_below"><a href="#">Insert Cell Below</a></li>
103 <li id="insert_cell_below"><a href="#">Insert Cell Below</a></li>
104 </ul>
104 </ul>
105 </li>
105 </li>
106 <li><a href="#">Cell</a>
106 <li><a href="#">Cell</a>
107 <ul>
107 <ul>
108 <li id="run_cell"><a href="#">Run</a></li>
108 <li id="run_cell"><a href="#">Run</a></li>
109 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
109 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
110 <li id="run_all_cells"><a href="#">Run All</a></li>
110 <li id="run_all_cells"><a href="#">Run All</a></li>
111 <li id="run_all_cells_above"><a href="#">Run All Above</a></li>
112 <li id="run_all_cells_below"><a href="#">Run All Below</a></li>
111 <hr/>
113 <hr/>
112 <li id="to_code"><a href="#">Code</a></li>
114 <li id="to_code"><a href="#">Code</a></li>
113 <li id="to_markdown"><a href="#">Markdown </a></li>
115 <li id="to_markdown"><a href="#">Markdown </a></li>
114 <li id="to_raw"><a href="#">Raw Text</a></li>
116 <li id="to_raw"><a href="#">Raw Text</a></li>
115 <li id="to_heading1"><a href="#">Heading 1</a></li>
117 <li id="to_heading1"><a href="#">Heading 1</a></li>
116 <li id="to_heading2"><a href="#">Heading 2</a></li>
118 <li id="to_heading2"><a href="#">Heading 2</a></li>
117 <li id="to_heading3"><a href="#">Heading 3</a></li>
119 <li id="to_heading3"><a href="#">Heading 3</a></li>
118 <li id="to_heading4"><a href="#">Heading 4</a></li>
120 <li id="to_heading4"><a href="#">Heading 4</a></li>
119 <li id="to_heading5"><a href="#">Heading 5</a></li>
121 <li id="to_heading5"><a href="#">Heading 5</a></li>
120 <li id="to_heading6"><a href="#">Heading 6</a></li>
122 <li id="to_heading6"><a href="#">Heading 6</a></li>
121 <hr/>
123 <hr/>
122 <li id="toggle_output"><a href="#">Toggle Current Output</a></li>
124 <li id="toggle_output"><a href="#">Toggle Current Output</a></li>
123 <li id="all_outputs"><a href="#">All Output</a>
125 <li id="all_outputs"><a href="#">All Output</a>
124 <ul>
126 <ul>
125 <li id="expand_all_output"><a href="#">Expand</a></li>
127 <li id="expand_all_output"><a href="#">Expand</a></li>
126 <li id="scroll_all_output"><a href="#">Scroll Long</a></li>
128 <li id="scroll_all_output"><a href="#">Scroll Long</a></li>
127 <li id="collapse_all_output"><a href="#">Collapse</a></li>
129 <li id="collapse_all_output"><a href="#">Collapse</a></li>
128 <li id="clear_all_output"><a href="#">Clear</a></li>
130 <li id="clear_all_output"><a href="#">Clear</a></li>
129 </ul>
131 </ul>
130 </li>
132 </li>
131 </ul>
133 </ul>
132 </li>
134 </li>
133 <li><a href="#">Kernel</a>
135 <li><a href="#">Kernel</a>
134 <ul>
136 <ul>
135 <li id="int_kernel"><a href="#">Interrupt</a></li>
137 <li id="int_kernel"><a href="#">Interrupt</a></li>
136 <li id="restart_kernel"><a href="#">Restart</a></li>
138 <li id="restart_kernel"><a href="#">Restart</a></li>
137 </ul>
139 </ul>
138 </li>
140 </li>
139 <li><a href="#">Help</a>
141 <li><a href="#">Help</a>
140 <ul>
142 <ul>
141 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
143 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
142 <li><a href="http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html" target="_blank">Notebook Help</a></li>
144 <li><a href="http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html" target="_blank">Notebook Help</a></li>
143 <li id="keyboard_shortcuts"><a href="#">Keyboard Shortcuts</a></li>
145 <li id="keyboard_shortcuts"><a href="#">Keyboard Shortcuts</a></li>
144 <hr/>
146 <hr/>
145 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
147 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
146 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
148 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
147 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
149 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
148 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
150 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
149 <li><a href="http://matplotlib.sourceforge.net/" target="_blank">Matplotlib</a></li>
151 <li><a href="http://matplotlib.sourceforge.net/" target="_blank">Matplotlib</a></li>
150 </ul>
152 </ul>
151 </li>
153 </li>
152 </ul>
154 </ul>
153
155
154 </div>
156 </div>
155 <div id="notification_area">
157 <div id="notification_area">
156 </div>
158 </div>
157 </div>
159 </div>
158
160
159
161
160 <div id="maintoolbar"></div>
162 <div id="maintoolbar"></div>
161
163
162 <div id="main_app">
164 <div id="main_app">
163
165
164 <div id="notebook_panel">
166 <div id="notebook_panel">
165 <div id="notebook"></div>
167 <div id="notebook"></div>
166 <div id="pager_splitter"></div>
168 <div id="pager_splitter"></div>
167 <div id="pager_container">
169 <div id="pager_container">
168 <div id='pager_button_area'>
170 <div id='pager_button_area'>
169 </div>
171 </div>
170 <div id="pager"></div>
172 <div id="pager"></div>
171 </div>
173 </div>
172 </div>
174 </div>
173
175
174 </div>
176 </div>
175 <div id='tooltip' class='ipython_tooltip ui-corner-all' style='display:none'></div>
177 <div id='tooltip' class='ipython_tooltip ui-corner-all' style='display:none'></div>
176
178
177
179
178 {% end %}
180 {% end %}
179
181
180
182
181 {% block script %}
183 {% block script %}
182
184
183 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
185 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
184 <script src="{{ static_url("codemirror/lib/util/loadmode.js") }}" charset="utf-8"></script>
186 <script src="{{ static_url("codemirror/lib/util/loadmode.js") }}" charset="utf-8"></script>
185 <script src="{{ static_url("codemirror/lib/util/multiplex.js") }}" charset="utf-8"></script>
187 <script src="{{ static_url("codemirror/lib/util/multiplex.js") }}" charset="utf-8"></script>
186 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
188 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
187 <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
189 <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
188 <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
190 <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
189 <script src="{{ static_url("codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
191 <script src="{{ static_url("codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
190 <script src="{{ static_url("codemirror/mode/css/css.js") }}" charset="utf-8"></script>
192 <script src="{{ static_url("codemirror/mode/css/css.js") }}" charset="utf-8"></script>
191 <script src="{{ static_url("codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
193 <script src="{{ static_url("codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
192 <script src="{{ static_url("codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
194 <script src="{{ static_url("codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
193
195
194 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
196 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
195
197
196 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
198 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
197 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
199 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
198
200
199 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
201 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
200 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
202 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
201 <script src="{{ static_url("js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
203 <script src="{{ static_url("js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
202 <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
204 <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
203 <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
205 <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
204 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
206 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
205 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
207 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
206 <script src="{{ static_url("js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
208 <script src="{{ static_url("js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
207 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
209 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
208 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
210 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
209 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
211 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
210 <script src="{{ static_url("js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
212 <script src="{{ static_url("js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
211 <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
213 <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
212 <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
214 <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
213 <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
215 <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
214 <script src="{{ static_url("js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
216 <script src="{{ static_url("js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
215 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
217 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
216 <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
218 <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
217 <script src="{{ static_url("js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
219 <script src="{{ static_url("js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
218 <script src="{{ static_url("js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
220 <script src="{{ static_url("js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
219 <script src="{{ static_url("js/config.js") }}" type="text/javascript" charset="utf-8"></script>
221 <script src="{{ static_url("js/config.js") }}" type="text/javascript" charset="utf-8"></script>
220 <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
222 <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
221
223
222 <script src="{{ static_url("js/contexthint.js") }}" charset="utf-8"></script>
224 <script src="{{ static_url("js/contexthint.js") }}" charset="utf-8"></script>
223
225
224 {% end %}
226 {% end %}
@@ -1,81 +1,81 b''
1 {% extends page.html %}
1 {% extends page.html %}
2
2
3 {% block stylesheet %}
3 {% block stylesheet %}
4
4
5 {% if mathjax_url %}
5 {% if mathjax_url %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 {% end %}
7 {% end %}
8 <script type="text/javascript">
8 <script type="text/javascript">
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 // where it will be undefined, and should prompt a dialog later.
10 // where it will be undefined, and should prompt a dialog later.
11 window.mathjax_url = "{{mathjax_url}}";
11 window.mathjax_url = "{{mathjax_url}}";
12 </script>
12 </script>
13
13
14 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
14 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
15 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
15 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
16
16
17 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
17 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
18
18
19 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
20 <link rel="stylesheet" href="{{ static_url("css/printnotebook.css") }}" type="text/css" />
20 <link rel="stylesheet" href="{{ static_url("css/printnotebook.css") }}" type="text/css" />
21 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
21 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
22
22
23 {% end %}
23 {% end %}
24
24
25
25
26 {% block params %}
26 {% block params %}
27
27
28 data-project={{project}}
28 data-project={{project}}
29 data-base-project-url={{base_project_url}}
29 data-base-project-url={{base_project_url}}
30 data-base-kernel-url={{base_kernel_url}}
30 data-base-kernel-url={{base_kernel_url}}
31 data-read-only={{read_only and not logged_in}}
31 data-read-only={{read_only and not logged_in}}
32 data-notebook-id={{notebook_id}}
32 data-notebook-id={{notebook_id}}
33
33
34 {% end %}
34 {% end %}
35
35
36
36
37 {% block header %}
37 {% block header %}
38 {% end %}
38 {% end %}
39
39
40
40
41 {% block site %}
41 {% block site %}
42
42
43 <div id="main_app">
43 <div id="main_app">
44
44
45 <div id="notebook_panel">
45 <div id="notebook_panel">
46 <div id="notebook"></div>
46 <div id="notebook"></div>
47 </div>
47 </div>
48
48
49 </div>
49 </div>
50
50
51 {% end %}
51 {% end %}
52
52
53
53
54 {% block script %}
54 {% block script %}
55
55
56 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
56 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
57 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
57 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
58 <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
58 <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
59 <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
59 <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
60 <script src="{{ static_url("codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
60 <script src="{{ static_url("codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
61 <script src="{{ static_url("codemirror/mode/css/css.js") }}" charset="utf-8"></script>
61 <script src="{{ static_url("codemirror/mode/css/css.js") }}" charset="utf-8"></script>
62 <script src="{{ static_url("codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
62 <script src="{{ static_url("codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
63 <script src="{{ static_url("codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
63 <script src="{{ static_url("codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
64
64
65 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
65 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
66
66
67 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
67 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
68 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
68 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
69
69
70 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
70 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
71 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
71 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
72 <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
72 <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
73 <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
73 <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
74 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
74 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
75 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
75 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
76 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
76 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
77 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
77 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
78 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
78 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
79 <script src="{{ static_url("js/printnotebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
79 <script src="{{ static_url("js/printnotebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
80
80
81 {% end %}
81 {% end %}
@@ -1,1959 +1,1999 b''
1 """ An abstract base class for console-type widgets.
1 """ An abstract base class for console-type widgets.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 # Standard library imports
7 # Standard library imports
8 import os.path
8 import os.path
9 import re
9 import re
10 import sys
10 import sys
11 from textwrap import dedent
11 from textwrap import dedent
12 from unicodedata import category
12 from unicodedata import category
13 import webbrowser
13 import webbrowser
14
14
15 # System library imports
15 # System library imports
16 from IPython.external.qt import QtCore, QtGui
16 from IPython.external.qt import QtCore, QtGui
17
17
18 # Local imports
18 # Local imports
19 from IPython.config.configurable import LoggingConfigurable
19 from IPython.config.configurable import LoggingConfigurable
20 from IPython.core.inputsplitter import ESC_SEQUENCES
20 from IPython.core.inputsplitter import ESC_SEQUENCES
21 from IPython.frontend.qt.rich_text import HtmlExporter
21 from IPython.frontend.qt.rich_text import HtmlExporter
22 from IPython.frontend.qt.util import MetaQObjectHasTraits, get_font
22 from IPython.frontend.qt.util import MetaQObjectHasTraits, get_font
23 from IPython.utils.text import columnize
23 from IPython.utils.text import columnize
24 from IPython.utils.traitlets import Bool, Enum, Integer, Unicode
24 from IPython.utils.traitlets import Bool, Enum, Integer, Unicode
25 from ansi_code_processor import QtAnsiCodeProcessor
25 from ansi_code_processor import QtAnsiCodeProcessor
26 from completion_widget import CompletionWidget
26 from completion_widget import CompletionWidget
27 from completion_html import CompletionHtml
27 from completion_html import CompletionHtml
28 from completion_plain import CompletionPlain
28 from completion_plain import CompletionPlain
29 from kill_ring import QtKillRing
29 from kill_ring import QtKillRing
30
30
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Functions
33 # Functions
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 ESCAPE_CHARS = ''.join(ESC_SEQUENCES)
36 ESCAPE_CHARS = ''.join(ESC_SEQUENCES)
37 ESCAPE_RE = re.compile("^["+ESCAPE_CHARS+"]+")
37 ESCAPE_RE = re.compile("^["+ESCAPE_CHARS+"]+")
38
38
39 def commonprefix(items):
39 def commonprefix(items):
40 """Get common prefix for completions
40 """Get common prefix for completions
41
41
42 Return the longest common prefix of a list of strings, but with special
42 Return the longest common prefix of a list of strings, but with special
43 treatment of escape characters that might precede commands in IPython,
43 treatment of escape characters that might precede commands in IPython,
44 such as %magic functions. Used in tab completion.
44 such as %magic functions. Used in tab completion.
45
45
46 For a more general function, see os.path.commonprefix
46 For a more general function, see os.path.commonprefix
47 """
47 """
48 # the last item will always have the least leading % symbol
48 # the last item will always have the least leading % symbol
49 # min / max are first/last in alphabetical order
49 # min / max are first/last in alphabetical order
50 first_match = ESCAPE_RE.match(min(items))
50 first_match = ESCAPE_RE.match(min(items))
51 last_match = ESCAPE_RE.match(max(items))
51 last_match = ESCAPE_RE.match(max(items))
52 # common suffix is (common prefix of reversed items) reversed
52 # common suffix is (common prefix of reversed items) reversed
53 if first_match and last_match:
53 if first_match and last_match:
54 prefix = os.path.commonprefix((first_match.group(0)[::-1], last_match.group(0)[::-1]))[::-1]
54 prefix = os.path.commonprefix((first_match.group(0)[::-1], last_match.group(0)[::-1]))[::-1]
55 else:
55 else:
56 prefix = ''
56 prefix = ''
57
57
58 items = [s.lstrip(ESCAPE_CHARS) for s in items]
58 items = [s.lstrip(ESCAPE_CHARS) for s in items]
59 return prefix+os.path.commonprefix(items)
59 return prefix+os.path.commonprefix(items)
60
60
61 def is_letter_or_number(char):
61 def is_letter_or_number(char):
62 """ Returns whether the specified unicode character is a letter or a number.
62 """ Returns whether the specified unicode character is a letter or a number.
63 """
63 """
64 cat = category(char)
64 cat = category(char)
65 return cat.startswith('L') or cat.startswith('N')
65 return cat.startswith('L') or cat.startswith('N')
66
66
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68 # Classes
68 # Classes
69 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
70
70
71 class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):
71 class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):
72 """ An abstract base class for console-type widgets. This class has
72 """ An abstract base class for console-type widgets. This class has
73 functionality for:
73 functionality for:
74
74
75 * Maintaining a prompt and editing region
75 * Maintaining a prompt and editing region
76 * Providing the traditional Unix-style console keyboard shortcuts
76 * Providing the traditional Unix-style console keyboard shortcuts
77 * Performing tab completion
77 * Performing tab completion
78 * Paging text
78 * Paging text
79 * Handling ANSI escape codes
79 * Handling ANSI escape codes
80
80
81 ConsoleWidget also provides a number of utility methods that will be
81 ConsoleWidget also provides a number of utility methods that will be
82 convenient to implementors of a console-style widget.
82 convenient to implementors of a console-style widget.
83 """
83 """
84 __metaclass__ = MetaQObjectHasTraits
84 __metaclass__ = MetaQObjectHasTraits
85
85
86 #------ Configuration ------------------------------------------------------
86 #------ Configuration ------------------------------------------------------
87
87
88 ansi_codes = Bool(True, config=True,
88 ansi_codes = Bool(True, config=True,
89 help="Whether to process ANSI escape codes."
89 help="Whether to process ANSI escape codes."
90 )
90 )
91 buffer_size = Integer(500, config=True,
91 buffer_size = Integer(500, config=True,
92 help="""
92 help="""
93 The maximum number of lines of text before truncation. Specifying a
93 The maximum number of lines of text before truncation. Specifying a
94 non-positive number disables text truncation (not recommended).
94 non-positive number disables text truncation (not recommended).
95 """
95 """
96 )
96 )
97 gui_completion = Enum(['plain', 'droplist', 'ncurses'], config=True,
97 gui_completion = Enum(['plain', 'droplist', 'ncurses'], config=True,
98 default_value = 'ncurses',
98 default_value = 'ncurses',
99 help="""
99 help="""
100 The type of completer to use. Valid values are:
100 The type of completer to use. Valid values are:
101
101
102 'plain' : Show the availlable completion as a text list
102 'plain' : Show the availlable completion as a text list
103 Below the editting area.
103 Below the editting area.
104 'droplist': Show the completion in a drop down list navigable
104 'droplist': Show the completion in a drop down list navigable
105 by the arrow keys, and from which you can select
105 by the arrow keys, and from which you can select
106 completion by pressing Return.
106 completion by pressing Return.
107 'ncurses' : Show the completion as a text list which is navigable by
107 'ncurses' : Show the completion as a text list which is navigable by
108 `tab` and arrow keys.
108 `tab` and arrow keys.
109 """
109 """
110 )
110 )
111 # NOTE: this value can only be specified during initialization.
111 # NOTE: this value can only be specified during initialization.
112 kind = Enum(['plain', 'rich'], default_value='plain', config=True,
112 kind = Enum(['plain', 'rich'], default_value='plain', config=True,
113 help="""
113 help="""
114 The type of underlying text widget to use. Valid values are 'plain',
114 The type of underlying text widget to use. Valid values are 'plain',
115 which specifies a QPlainTextEdit, and 'rich', which specifies a
115 which specifies a QPlainTextEdit, and 'rich', which specifies a
116 QTextEdit.
116 QTextEdit.
117 """
117 """
118 )
118 )
119 # NOTE: this value can only be specified during initialization.
119 # NOTE: this value can only be specified during initialization.
120 paging = Enum(['inside', 'hsplit', 'vsplit', 'custom', 'none'],
120 paging = Enum(['inside', 'hsplit', 'vsplit', 'custom', 'none'],
121 default_value='inside', config=True,
121 default_value='inside', config=True,
122 help="""
122 help="""
123 The type of paging to use. Valid values are:
123 The type of paging to use. Valid values are:
124
124
125 'inside' : The widget pages like a traditional terminal.
125 'inside' : The widget pages like a traditional terminal.
126 'hsplit' : When paging is requested, the widget is split
126 'hsplit' : When paging is requested, the widget is split
127 horizontally. The top pane contains the console, and the
127 horizontally. The top pane contains the console, and the
128 bottom pane contains the paged text.
128 bottom pane contains the paged text.
129 'vsplit' : Similar to 'hsplit', except that a vertical splitter
129 'vsplit' : Similar to 'hsplit', except that a vertical splitter
130 used.
130 used.
131 'custom' : No action is taken by the widget beyond emitting a
131 'custom' : No action is taken by the widget beyond emitting a
132 'custom_page_requested(str)' signal.
132 'custom_page_requested(str)' signal.
133 'none' : The text is written directly to the console.
133 'none' : The text is written directly to the console.
134 """)
134 """)
135
135
136 font_family = Unicode(config=True,
136 font_family = Unicode(config=True,
137 help="""The font family to use for the console.
137 help="""The font family to use for the console.
138 On OSX this defaults to Monaco, on Windows the default is
138 On OSX this defaults to Monaco, on Windows the default is
139 Consolas with fallback of Courier, and on other platforms
139 Consolas with fallback of Courier, and on other platforms
140 the default is Monospace.
140 the default is Monospace.
141 """)
141 """)
142 def _font_family_default(self):
142 def _font_family_default(self):
143 if sys.platform == 'win32':
143 if sys.platform == 'win32':
144 # Consolas ships with Vista/Win7, fallback to Courier if needed
144 # Consolas ships with Vista/Win7, fallback to Courier if needed
145 return 'Consolas'
145 return 'Consolas'
146 elif sys.platform == 'darwin':
146 elif sys.platform == 'darwin':
147 # OSX always has Monaco, no need for a fallback
147 # OSX always has Monaco, no need for a fallback
148 return 'Monaco'
148 return 'Monaco'
149 else:
149 else:
150 # Monospace should always exist, no need for a fallback
150 # Monospace should always exist, no need for a fallback
151 return 'Monospace'
151 return 'Monospace'
152
152
153 font_size = Integer(config=True,
153 font_size = Integer(config=True,
154 help="""The font size. If unconfigured, Qt will be entrusted
154 help="""The font size. If unconfigured, Qt will be entrusted
155 with the size of the font.
155 with the size of the font.
156 """)
156 """)
157
157
158 # Whether to override ShortcutEvents for the keybindings defined by this
158 # Whether to override ShortcutEvents for the keybindings defined by this
159 # widget (Ctrl+n, Ctrl+a, etc). Enable this if you want this widget to take
159 # widget (Ctrl+n, Ctrl+a, etc). Enable this if you want this widget to take
160 # priority (when it has focus) over, e.g., window-level menu shortcuts.
160 # priority (when it has focus) over, e.g., window-level menu shortcuts.
161 override_shortcuts = Bool(False)
161 override_shortcuts = Bool(False)
162
162
163 # ------ Custom Qt Widgets -------------------------------------------------
163 # ------ Custom Qt Widgets -------------------------------------------------
164
164
165 # For other projects to easily override the Qt widgets used by the console
165 # For other projects to easily override the Qt widgets used by the console
166 # (e.g. Spyder)
166 # (e.g. Spyder)
167 custom_control = None
167 custom_control = None
168 custom_page_control = None
168 custom_page_control = None
169
169
170 #------ Signals ------------------------------------------------------------
170 #------ Signals ------------------------------------------------------------
171
171
172 # Signals that indicate ConsoleWidget state.
172 # Signals that indicate ConsoleWidget state.
173 copy_available = QtCore.Signal(bool)
173 copy_available = QtCore.Signal(bool)
174 redo_available = QtCore.Signal(bool)
174 redo_available = QtCore.Signal(bool)
175 undo_available = QtCore.Signal(bool)
175 undo_available = QtCore.Signal(bool)
176
176
177 # Signal emitted when paging is needed and the paging style has been
177 # Signal emitted when paging is needed and the paging style has been
178 # specified as 'custom'.
178 # specified as 'custom'.
179 custom_page_requested = QtCore.Signal(object)
179 custom_page_requested = QtCore.Signal(object)
180
180
181 # Signal emitted when the font is changed.
181 # Signal emitted when the font is changed.
182 font_changed = QtCore.Signal(QtGui.QFont)
182 font_changed = QtCore.Signal(QtGui.QFont)
183
183
184 #------ Protected class variables ------------------------------------------
184 #------ Protected class variables ------------------------------------------
185
185
186 # control handles
186 # control handles
187 _control = None
187 _control = None
188 _page_control = None
188 _page_control = None
189 _splitter = None
189 _splitter = None
190
190
191 # When the control key is down, these keys are mapped.
191 # When the control key is down, these keys are mapped.
192 _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left,
192 _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left,
193 QtCore.Qt.Key_F : QtCore.Qt.Key_Right,
193 QtCore.Qt.Key_F : QtCore.Qt.Key_Right,
194 QtCore.Qt.Key_A : QtCore.Qt.Key_Home,
194 QtCore.Qt.Key_A : QtCore.Qt.Key_Home,
195 QtCore.Qt.Key_P : QtCore.Qt.Key_Up,
195 QtCore.Qt.Key_P : QtCore.Qt.Key_Up,
196 QtCore.Qt.Key_N : QtCore.Qt.Key_Down,
196 QtCore.Qt.Key_N : QtCore.Qt.Key_Down,
197 QtCore.Qt.Key_H : QtCore.Qt.Key_Backspace, }
197 QtCore.Qt.Key_H : QtCore.Qt.Key_Backspace, }
198 if not sys.platform == 'darwin':
198 if not sys.platform == 'darwin':
199 # On OS X, Ctrl-E already does the right thing, whereas End moves the
199 # On OS X, Ctrl-E already does the right thing, whereas End moves the
200 # cursor to the bottom of the buffer.
200 # cursor to the bottom of the buffer.
201 _ctrl_down_remap[QtCore.Qt.Key_E] = QtCore.Qt.Key_End
201 _ctrl_down_remap[QtCore.Qt.Key_E] = QtCore.Qt.Key_End
202
202
203 # The shortcuts defined by this widget. We need to keep track of these to
203 # The shortcuts defined by this widget. We need to keep track of these to
204 # support 'override_shortcuts' above.
204 # support 'override_shortcuts' above.
205 _shortcuts = set(_ctrl_down_remap.keys() +
205 _shortcuts = set(_ctrl_down_remap.keys() +
206 [ QtCore.Qt.Key_C, QtCore.Qt.Key_G, QtCore.Qt.Key_O,
206 [ QtCore.Qt.Key_C, QtCore.Qt.Key_G, QtCore.Qt.Key_O,
207 QtCore.Qt.Key_V ])
207 QtCore.Qt.Key_V ])
208
208
209 _temp_buffer_filled = False
209 _temp_buffer_filled = False
210
210
211 #---------------------------------------------------------------------------
211 #---------------------------------------------------------------------------
212 # 'QObject' interface
212 # 'QObject' interface
213 #---------------------------------------------------------------------------
213 #---------------------------------------------------------------------------
214
214
215 def __init__(self, parent=None, **kw):
215 def __init__(self, parent=None, **kw):
216 """ Create a ConsoleWidget.
216 """ Create a ConsoleWidget.
217
217
218 Parameters:
218 Parameters:
219 -----------
219 -----------
220 parent : QWidget, optional [default None]
220 parent : QWidget, optional [default None]
221 The parent for this widget.
221 The parent for this widget.
222 """
222 """
223 QtGui.QWidget.__init__(self, parent)
223 QtGui.QWidget.__init__(self, parent)
224 LoggingConfigurable.__init__(self, **kw)
224 LoggingConfigurable.__init__(self, **kw)
225
225
226 # While scrolling the pager on Mac OS X, it tears badly. The
226 # While scrolling the pager on Mac OS X, it tears badly. The
227 # NativeGesture is platform and perhaps build-specific hence
227 # NativeGesture is platform and perhaps build-specific hence
228 # we take adequate precautions here.
228 # we take adequate precautions here.
229 self._pager_scroll_events = [QtCore.QEvent.Wheel]
229 self._pager_scroll_events = [QtCore.QEvent.Wheel]
230 if hasattr(QtCore.QEvent, 'NativeGesture'):
230 if hasattr(QtCore.QEvent, 'NativeGesture'):
231 self._pager_scroll_events.append(QtCore.QEvent.NativeGesture)
231 self._pager_scroll_events.append(QtCore.QEvent.NativeGesture)
232
232
233 # Create the layout and underlying text widget.
233 # Create the layout and underlying text widget.
234 layout = QtGui.QStackedLayout(self)
234 layout = QtGui.QStackedLayout(self)
235 layout.setContentsMargins(0, 0, 0, 0)
235 layout.setContentsMargins(0, 0, 0, 0)
236 self._control = self._create_control()
236 self._control = self._create_control()
237 if self.paging in ('hsplit', 'vsplit'):
237 if self.paging in ('hsplit', 'vsplit'):
238 self._splitter = QtGui.QSplitter()
238 self._splitter = QtGui.QSplitter()
239 if self.paging == 'hsplit':
239 if self.paging == 'hsplit':
240 self._splitter.setOrientation(QtCore.Qt.Horizontal)
240 self._splitter.setOrientation(QtCore.Qt.Horizontal)
241 else:
241 else:
242 self._splitter.setOrientation(QtCore.Qt.Vertical)
242 self._splitter.setOrientation(QtCore.Qt.Vertical)
243 self._splitter.addWidget(self._control)
243 self._splitter.addWidget(self._control)
244 layout.addWidget(self._splitter)
244 layout.addWidget(self._splitter)
245 else:
245 else:
246 layout.addWidget(self._control)
246 layout.addWidget(self._control)
247
247
248 # Create the paging widget, if necessary.
248 # Create the paging widget, if necessary.
249 if self.paging in ('inside', 'hsplit', 'vsplit'):
249 if self.paging in ('inside', 'hsplit', 'vsplit'):
250 self._page_control = self._create_page_control()
250 self._page_control = self._create_page_control()
251 if self._splitter:
251 if self._splitter:
252 self._page_control.hide()
252 self._page_control.hide()
253 self._splitter.addWidget(self._page_control)
253 self._splitter.addWidget(self._page_control)
254 else:
254 else:
255 layout.addWidget(self._page_control)
255 layout.addWidget(self._page_control)
256
256
257 # Initialize protected variables. Some variables contain useful state
257 # Initialize protected variables. Some variables contain useful state
258 # information for subclasses; they should be considered read-only.
258 # information for subclasses; they should be considered read-only.
259 self._append_before_prompt_pos = 0
259 self._append_before_prompt_pos = 0
260 self._ansi_processor = QtAnsiCodeProcessor()
260 self._ansi_processor = QtAnsiCodeProcessor()
261 if self.gui_completion == 'ncurses':
261 if self.gui_completion == 'ncurses':
262 self._completion_widget = CompletionHtml(self)
262 self._completion_widget = CompletionHtml(self)
263 elif self.gui_completion == 'droplist':
263 elif self.gui_completion == 'droplist':
264 self._completion_widget = CompletionWidget(self)
264 self._completion_widget = CompletionWidget(self)
265 elif self.gui_completion == 'plain':
265 elif self.gui_completion == 'plain':
266 self._completion_widget = CompletionPlain(self)
266 self._completion_widget = CompletionPlain(self)
267
267
268 self._continuation_prompt = '> '
268 self._continuation_prompt = '> '
269 self._continuation_prompt_html = None
269 self._continuation_prompt_html = None
270 self._executing = False
270 self._executing = False
271 self._filter_drag = False
272 self._filter_resize = False
271 self._filter_resize = False
273 self._html_exporter = HtmlExporter(self._control)
272 self._html_exporter = HtmlExporter(self._control)
274 self._input_buffer_executing = ''
273 self._input_buffer_executing = ''
275 self._input_buffer_pending = ''
274 self._input_buffer_pending = ''
276 self._kill_ring = QtKillRing(self._control)
275 self._kill_ring = QtKillRing(self._control)
277 self._prompt = ''
276 self._prompt = ''
278 self._prompt_html = None
277 self._prompt_html = None
279 self._prompt_pos = 0
278 self._prompt_pos = 0
280 self._prompt_sep = ''
279 self._prompt_sep = ''
281 self._reading = False
280 self._reading = False
282 self._reading_callback = None
281 self._reading_callback = None
283 self._tab_width = 8
282 self._tab_width = 8
284
283
285 # Set a monospaced font.
284 # Set a monospaced font.
286 self.reset_font()
285 self.reset_font()
287
286
288 # Configure actions.
287 # Configure actions.
289 action = QtGui.QAction('Print', None)
288 action = QtGui.QAction('Print', None)
290 action.setEnabled(True)
289 action.setEnabled(True)
291 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
290 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
292 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
291 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
293 # Only override the default if there is a collision.
292 # Only override the default if there is a collision.
294 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
293 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
295 printkey = "Ctrl+Shift+P"
294 printkey = "Ctrl+Shift+P"
296 action.setShortcut(printkey)
295 action.setShortcut(printkey)
297 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
296 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
298 action.triggered.connect(self.print_)
297 action.triggered.connect(self.print_)
299 self.addAction(action)
298 self.addAction(action)
300 self.print_action = action
299 self.print_action = action
301
300
302 action = QtGui.QAction('Save as HTML/XML', None)
301 action = QtGui.QAction('Save as HTML/XML', None)
303 action.setShortcut(QtGui.QKeySequence.Save)
302 action.setShortcut(QtGui.QKeySequence.Save)
304 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
303 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
305 action.triggered.connect(self.export_html)
304 action.triggered.connect(self.export_html)
306 self.addAction(action)
305 self.addAction(action)
307 self.export_action = action
306 self.export_action = action
308
307
309 action = QtGui.QAction('Select All', None)
308 action = QtGui.QAction('Select All', None)
310 action.setEnabled(True)
309 action.setEnabled(True)
311 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
310 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
312 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
311 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
313 # Only override the default if there is a collision.
312 # Only override the default if there is a collision.
314 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
313 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
315 selectall = "Ctrl+Shift+A"
314 selectall = "Ctrl+Shift+A"
316 action.setShortcut(selectall)
315 action.setShortcut(selectall)
317 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
316 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
318 action.triggered.connect(self.select_all)
317 action.triggered.connect(self.select_all)
319 self.addAction(action)
318 self.addAction(action)
320 self.select_all_action = action
319 self.select_all_action = action
321
320
322 self.increase_font_size = QtGui.QAction("Bigger Font",
321 self.increase_font_size = QtGui.QAction("Bigger Font",
323 self,
322 self,
324 shortcut=QtGui.QKeySequence.ZoomIn,
323 shortcut=QtGui.QKeySequence.ZoomIn,
325 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
324 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
326 statusTip="Increase the font size by one point",
325 statusTip="Increase the font size by one point",
327 triggered=self._increase_font_size)
326 triggered=self._increase_font_size)
328 self.addAction(self.increase_font_size)
327 self.addAction(self.increase_font_size)
329
328
330 self.decrease_font_size = QtGui.QAction("Smaller Font",
329 self.decrease_font_size = QtGui.QAction("Smaller Font",
331 self,
330 self,
332 shortcut=QtGui.QKeySequence.ZoomOut,
331 shortcut=QtGui.QKeySequence.ZoomOut,
333 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
332 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
334 statusTip="Decrease the font size by one point",
333 statusTip="Decrease the font size by one point",
335 triggered=self._decrease_font_size)
334 triggered=self._decrease_font_size)
336 self.addAction(self.decrease_font_size)
335 self.addAction(self.decrease_font_size)
337
336
338 self.reset_font_size = QtGui.QAction("Normal Font",
337 self.reset_font_size = QtGui.QAction("Normal Font",
339 self,
338 self,
340 shortcut="Ctrl+0",
339 shortcut="Ctrl+0",
341 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
340 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
342 statusTip="Restore the Normal font size",
341 statusTip="Restore the Normal font size",
343 triggered=self.reset_font)
342 triggered=self.reset_font)
344 self.addAction(self.reset_font_size)
343 self.addAction(self.reset_font_size)
345
344
345 # Accept drag and drop events here. Drops were already turned off
346 # in self._control when that widget was created.
347 self.setAcceptDrops(True)
346
348
349 #---------------------------------------------------------------------------
350 # Drag and drop support
351 #---------------------------------------------------------------------------
352
353 def dragEnterEvent(self, e):
354 if e.mimeData().hasUrls():
355 # The link action should indicate to that the drop will insert
356 # the file anme.
357 e.setDropAction(QtCore.Qt.LinkAction)
358 e.accept()
359 elif e.mimeData().hasText():
360 # By changing the action to copy we don't need to worry about
361 # the user accidentally moving text around in the widget.
362 e.setDropAction(QtCore.Qt.CopyAction)
363 e.accept()
364
365 def dragMoveEvent(self, e):
366 if e.mimeData().hasUrls():
367 pass
368 elif e.mimeData().hasText():
369 cursor = self._control.cursorForPosition(e.pos())
370 if self._in_buffer(cursor.position()):
371 e.setDropAction(QtCore.Qt.CopyAction)
372 self._control.setTextCursor(cursor)
373 else:
374 e.setDropAction(QtCore.Qt.IgnoreAction)
375 e.accept()
376
377 def dropEvent(self, e):
378 if e.mimeData().hasUrls():
379 self._keep_cursor_in_buffer()
380 cursor = self._control.textCursor()
381 filenames = [url.toLocalFile() for url in e.mimeData().urls()]
382 text = ', '.join("'" + f.replace("'", "'\"'\"'") + "'"
383 for f in filenames)
384 self._insert_plain_text_into_buffer(cursor, text)
385 elif e.mimeData().hasText():
386 cursor = self._control.cursorForPosition(e.pos())
387 if self._in_buffer(cursor.position()):
388 text = e.mimeData().text()
389 self._insert_plain_text_into_buffer(cursor, text)
347
390
348 def eventFilter(self, obj, event):
391 def eventFilter(self, obj, event):
349 """ Reimplemented to ensure a console-like behavior in the underlying
392 """ Reimplemented to ensure a console-like behavior in the underlying
350 text widgets.
393 text widgets.
351 """
394 """
352 etype = event.type()
395 etype = event.type()
353 if etype == QtCore.QEvent.KeyPress:
396 if etype == QtCore.QEvent.KeyPress:
354
397
355 # Re-map keys for all filtered widgets.
398 # Re-map keys for all filtered widgets.
356 key = event.key()
399 key = event.key()
357 if self._control_key_down(event.modifiers()) and \
400 if self._control_key_down(event.modifiers()) and \
358 key in self._ctrl_down_remap:
401 key in self._ctrl_down_remap:
359 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
402 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
360 self._ctrl_down_remap[key],
403 self._ctrl_down_remap[key],
361 QtCore.Qt.NoModifier)
404 QtCore.Qt.NoModifier)
362 QtGui.qApp.sendEvent(obj, new_event)
405 QtGui.qApp.sendEvent(obj, new_event)
363 return True
406 return True
364
407
365 elif obj == self._control:
408 elif obj == self._control:
366 return self._event_filter_console_keypress(event)
409 return self._event_filter_console_keypress(event)
367
410
368 elif obj == self._page_control:
411 elif obj == self._page_control:
369 return self._event_filter_page_keypress(event)
412 return self._event_filter_page_keypress(event)
370
413
371 # Make middle-click paste safe.
414 # Make middle-click paste safe.
372 elif etype == QtCore.QEvent.MouseButtonRelease and \
415 elif etype == QtCore.QEvent.MouseButtonRelease and \
373 event.button() == QtCore.Qt.MidButton and \
416 event.button() == QtCore.Qt.MidButton and \
374 obj == self._control.viewport():
417 obj == self._control.viewport():
375 cursor = self._control.cursorForPosition(event.pos())
418 cursor = self._control.cursorForPosition(event.pos())
376 self._control.setTextCursor(cursor)
419 self._control.setTextCursor(cursor)
377 self.paste(QtGui.QClipboard.Selection)
420 self.paste(QtGui.QClipboard.Selection)
378 return True
421 return True
379
422
380 # Manually adjust the scrollbars *after* a resize event is dispatched.
423 # Manually adjust the scrollbars *after* a resize event is dispatched.
381 elif etype == QtCore.QEvent.Resize and not self._filter_resize:
424 elif etype == QtCore.QEvent.Resize and not self._filter_resize:
382 self._filter_resize = True
425 self._filter_resize = True
383 QtGui.qApp.sendEvent(obj, event)
426 QtGui.qApp.sendEvent(obj, event)
384 self._adjust_scrollbars()
427 self._adjust_scrollbars()
385 self._filter_resize = False
428 self._filter_resize = False
386 return True
429 return True
387
430
388 # Override shortcuts for all filtered widgets.
431 # Override shortcuts for all filtered widgets.
389 elif etype == QtCore.QEvent.ShortcutOverride and \
432 elif etype == QtCore.QEvent.ShortcutOverride and \
390 self.override_shortcuts and \
433 self.override_shortcuts and \
391 self._control_key_down(event.modifiers()) and \
434 self._control_key_down(event.modifiers()) and \
392 event.key() in self._shortcuts:
435 event.key() in self._shortcuts:
393 event.accept()
436 event.accept()
394
437
395 # Ensure that drags are safe. The problem is that the drag starting
396 # logic, which determines whether the drag is a Copy or Move, is locked
397 # down in QTextControl. If the widget is editable, which it must be if
398 # we're not executing, the drag will be a Move. The following hack
399 # prevents QTextControl from deleting the text by clearing the selection
400 # when a drag leave event originating from this widget is dispatched.
401 # The fact that we have to clear the user's selection is unfortunate,
402 # but the alternative--trying to prevent Qt from using its hardwired
403 # drag logic and writing our own--is worse.
404 elif etype == QtCore.QEvent.DragEnter and \
405 obj == self._control.viewport() and \
406 event.source() == self._control.viewport():
407 self._filter_drag = True
408 elif etype == QtCore.QEvent.DragLeave and \
409 obj == self._control.viewport() and \
410 self._filter_drag:
411 cursor = self._control.textCursor()
412 cursor.clearSelection()
413 self._control.setTextCursor(cursor)
414 self._filter_drag = False
415
416 # Ensure that drops are safe.
417 elif etype == QtCore.QEvent.Drop and obj == self._control.viewport():
418 cursor = self._control.cursorForPosition(event.pos())
419 if self._in_buffer(cursor.position()):
420 text = event.mimeData().text()
421 self._insert_plain_text_into_buffer(cursor, text)
422
423 # Qt is expecting to get something here--drag and drop occurs in its
424 # own event loop. Send a DragLeave event to end it.
425 QtGui.qApp.sendEvent(obj, QtGui.QDragLeaveEvent())
426 return True
427
428 # Handle scrolling of the vsplit pager. This hack attempts to solve
438 # Handle scrolling of the vsplit pager. This hack attempts to solve
429 # problems with tearing of the help text inside the pager window. This
439 # problems with tearing of the help text inside the pager window. This
430 # happens only on Mac OS X with both PySide and PyQt. This fix isn't
440 # happens only on Mac OS X with both PySide and PyQt. This fix isn't
431 # perfect but makes the pager more usable.
441 # perfect but makes the pager more usable.
432 elif etype in self._pager_scroll_events and \
442 elif etype in self._pager_scroll_events and \
433 obj == self._page_control:
443 obj == self._page_control:
434 self._page_control.repaint()
444 self._page_control.repaint()
435 return True
445 return True
436
446
437 elif etype == QtCore.QEvent.MouseMove:
447 elif etype == QtCore.QEvent.MouseMove:
438 anchor = self._control.anchorAt(event.pos())
448 anchor = self._control.anchorAt(event.pos())
439 QtGui.QToolTip.showText(event.globalPos(), anchor)
449 QtGui.QToolTip.showText(event.globalPos(), anchor)
440
450
441 return super(ConsoleWidget, self).eventFilter(obj, event)
451 return super(ConsoleWidget, self).eventFilter(obj, event)
442
452
443 #---------------------------------------------------------------------------
453 #---------------------------------------------------------------------------
444 # 'QWidget' interface
454 # 'QWidget' interface
445 #---------------------------------------------------------------------------
455 #---------------------------------------------------------------------------
446
456
447 def sizeHint(self):
457 def sizeHint(self):
448 """ Reimplemented to suggest a size that is 80 characters wide and
458 """ Reimplemented to suggest a size that is 80 characters wide and
449 25 lines high.
459 25 lines high.
450 """
460 """
451 font_metrics = QtGui.QFontMetrics(self.font)
461 font_metrics = QtGui.QFontMetrics(self.font)
452 margin = (self._control.frameWidth() +
462 margin = (self._control.frameWidth() +
453 self._control.document().documentMargin()) * 2
463 self._control.document().documentMargin()) * 2
454 style = self.style()
464 style = self.style()
455 splitwidth = style.pixelMetric(QtGui.QStyle.PM_SplitterWidth)
465 splitwidth = style.pixelMetric(QtGui.QStyle.PM_SplitterWidth)
456
466
457 # Note 1: Despite my best efforts to take the various margins into
467 # Note 1: Despite my best efforts to take the various margins into
458 # account, the width is still coming out a bit too small, so we include
468 # account, the width is still coming out a bit too small, so we include
459 # a fudge factor of one character here.
469 # a fudge factor of one character here.
460 # Note 2: QFontMetrics.maxWidth is not used here or anywhere else due
470 # Note 2: QFontMetrics.maxWidth is not used here or anywhere else due
461 # to a Qt bug on certain Mac OS systems where it returns 0.
471 # to a Qt bug on certain Mac OS systems where it returns 0.
462 width = font_metrics.width(' ') * 81 + margin
472 width = font_metrics.width(' ') * 81 + margin
463 width += style.pixelMetric(QtGui.QStyle.PM_ScrollBarExtent)
473 width += style.pixelMetric(QtGui.QStyle.PM_ScrollBarExtent)
464 if self.paging == 'hsplit':
474 if self.paging == 'hsplit':
465 width = width * 2 + splitwidth
475 width = width * 2 + splitwidth
466
476
467 height = font_metrics.height() * 25 + margin
477 height = font_metrics.height() * 25 + margin
468 if self.paging == 'vsplit':
478 if self.paging == 'vsplit':
469 height = height * 2 + splitwidth
479 height = height * 2 + splitwidth
470
480
471 return QtCore.QSize(width, height)
481 return QtCore.QSize(width, height)
472
482
473 #---------------------------------------------------------------------------
483 #---------------------------------------------------------------------------
474 # 'ConsoleWidget' public interface
484 # 'ConsoleWidget' public interface
475 #---------------------------------------------------------------------------
485 #---------------------------------------------------------------------------
476
486
477 def can_copy(self):
487 def can_copy(self):
478 """ Returns whether text can be copied to the clipboard.
488 """ Returns whether text can be copied to the clipboard.
479 """
489 """
480 return self._control.textCursor().hasSelection()
490 return self._control.textCursor().hasSelection()
481
491
482 def can_cut(self):
492 def can_cut(self):
483 """ Returns whether text can be cut to the clipboard.
493 """ Returns whether text can be cut to the clipboard.
484 """
494 """
485 cursor = self._control.textCursor()
495 cursor = self._control.textCursor()
486 return (cursor.hasSelection() and
496 return (cursor.hasSelection() and
487 self._in_buffer(cursor.anchor()) and
497 self._in_buffer(cursor.anchor()) and
488 self._in_buffer(cursor.position()))
498 self._in_buffer(cursor.position()))
489
499
490 def can_paste(self):
500 def can_paste(self):
491 """ Returns whether text can be pasted from the clipboard.
501 """ Returns whether text can be pasted from the clipboard.
492 """
502 """
493 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
503 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
494 return bool(QtGui.QApplication.clipboard().text())
504 return bool(QtGui.QApplication.clipboard().text())
495 return False
505 return False
496
506
497 def clear(self, keep_input=True):
507 def clear(self, keep_input=True):
498 """ Clear the console.
508 """ Clear the console.
499
509
500 Parameters:
510 Parameters:
501 -----------
511 -----------
502 keep_input : bool, optional (default True)
512 keep_input : bool, optional (default True)
503 If set, restores the old input buffer if a new prompt is written.
513 If set, restores the old input buffer if a new prompt is written.
504 """
514 """
505 if self._executing:
515 if self._executing:
506 self._control.clear()
516 self._control.clear()
507 else:
517 else:
508 if keep_input:
518 if keep_input:
509 input_buffer = self.input_buffer
519 input_buffer = self.input_buffer
510 self._control.clear()
520 self._control.clear()
511 self._show_prompt()
521 self._show_prompt()
512 if keep_input:
522 if keep_input:
513 self.input_buffer = input_buffer
523 self.input_buffer = input_buffer
514
524
515 def copy(self):
525 def copy(self):
516 """ Copy the currently selected text to the clipboard.
526 """ Copy the currently selected text to the clipboard.
517 """
527 """
518 self.layout().currentWidget().copy()
528 self.layout().currentWidget().copy()
519
529
520 def copy_anchor(self, anchor):
530 def copy_anchor(self, anchor):
521 """ Copy anchor text to the clipboard
531 """ Copy anchor text to the clipboard
522 """
532 """
523 QtGui.QApplication.clipboard().setText(anchor)
533 QtGui.QApplication.clipboard().setText(anchor)
524
534
525 def cut(self):
535 def cut(self):
526 """ Copy the currently selected text to the clipboard and delete it
536 """ Copy the currently selected text to the clipboard and delete it
527 if it's inside the input buffer.
537 if it's inside the input buffer.
528 """
538 """
529 self.copy()
539 self.copy()
530 if self.can_cut():
540 if self.can_cut():
531 self._control.textCursor().removeSelectedText()
541 self._control.textCursor().removeSelectedText()
532
542
533 def execute(self, source=None, hidden=False, interactive=False):
543 def execute(self, source=None, hidden=False, interactive=False):
534 """ Executes source or the input buffer, possibly prompting for more
544 """ Executes source or the input buffer, possibly prompting for more
535 input.
545 input.
536
546
537 Parameters:
547 Parameters:
538 -----------
548 -----------
539 source : str, optional
549 source : str, optional
540
550
541 The source to execute. If not specified, the input buffer will be
551 The source to execute. If not specified, the input buffer will be
542 used. If specified and 'hidden' is False, the input buffer will be
552 used. If specified and 'hidden' is False, the input buffer will be
543 replaced with the source before execution.
553 replaced with the source before execution.
544
554
545 hidden : bool, optional (default False)
555 hidden : bool, optional (default False)
546
556
547 If set, no output will be shown and the prompt will not be modified.
557 If set, no output will be shown and the prompt will not be modified.
548 In other words, it will be completely invisible to the user that
558 In other words, it will be completely invisible to the user that
549 an execution has occurred.
559 an execution has occurred.
550
560
551 interactive : bool, optional (default False)
561 interactive : bool, optional (default False)
552
562
553 Whether the console is to treat the source as having been manually
563 Whether the console is to treat the source as having been manually
554 entered by the user. The effect of this parameter depends on the
564 entered by the user. The effect of this parameter depends on the
555 subclass implementation.
565 subclass implementation.
556
566
557 Raises:
567 Raises:
558 -------
568 -------
559 RuntimeError
569 RuntimeError
560 If incomplete input is given and 'hidden' is True. In this case,
570 If incomplete input is given and 'hidden' is True. In this case,
561 it is not possible to prompt for more input.
571 it is not possible to prompt for more input.
562
572
563 Returns:
573 Returns:
564 --------
574 --------
565 A boolean indicating whether the source was executed.
575 A boolean indicating whether the source was executed.
566 """
576 """
567 # WARNING: The order in which things happen here is very particular, in
577 # WARNING: The order in which things happen here is very particular, in
568 # large part because our syntax highlighting is fragile. If you change
578 # large part because our syntax highlighting is fragile. If you change
569 # something, test carefully!
579 # something, test carefully!
570
580
571 # Decide what to execute.
581 # Decide what to execute.
572 if source is None:
582 if source is None:
573 source = self.input_buffer
583 source = self.input_buffer
574 if not hidden:
584 if not hidden:
575 # A newline is appended later, but it should be considered part
585 # A newline is appended later, but it should be considered part
576 # of the input buffer.
586 # of the input buffer.
577 source += '\n'
587 source += '\n'
578 elif not hidden:
588 elif not hidden:
579 self.input_buffer = source
589 self.input_buffer = source
580
590
581 # Execute the source or show a continuation prompt if it is incomplete.
591 # Execute the source or show a continuation prompt if it is incomplete.
582 complete = self._is_complete(source, interactive)
592 complete = self._is_complete(source, interactive)
583 if hidden:
593 if hidden:
584 if complete:
594 if complete:
585 self._execute(source, hidden)
595 self._execute(source, hidden)
586 else:
596 else:
587 error = 'Incomplete noninteractive input: "%s"'
597 error = 'Incomplete noninteractive input: "%s"'
588 raise RuntimeError(error % source)
598 raise RuntimeError(error % source)
589 else:
599 else:
590 if complete:
600 if complete:
591 self._append_plain_text('\n')
601 self._append_plain_text('\n')
592 self._input_buffer_executing = self.input_buffer
602 self._input_buffer_executing = self.input_buffer
593 self._executing = True
603 self._executing = True
594 self._prompt_finished()
604 self._prompt_finished()
595
605
596 # The maximum block count is only in effect during execution.
606 # The maximum block count is only in effect during execution.
597 # This ensures that _prompt_pos does not become invalid due to
607 # This ensures that _prompt_pos does not become invalid due to
598 # text truncation.
608 # text truncation.
599 self._control.document().setMaximumBlockCount(self.buffer_size)
609 self._control.document().setMaximumBlockCount(self.buffer_size)
600
610
601 # Setting a positive maximum block count will automatically
611 # Setting a positive maximum block count will automatically
602 # disable the undo/redo history, but just to be safe:
612 # disable the undo/redo history, but just to be safe:
603 self._control.setUndoRedoEnabled(False)
613 self._control.setUndoRedoEnabled(False)
604
614
605 # Perform actual execution.
615 # Perform actual execution.
606 self._execute(source, hidden)
616 self._execute(source, hidden)
607
617
608 else:
618 else:
609 # Do this inside an edit block so continuation prompts are
619 # Do this inside an edit block so continuation prompts are
610 # removed seamlessly via undo/redo.
620 # removed seamlessly via undo/redo.
611 cursor = self._get_end_cursor()
621 cursor = self._get_end_cursor()
612 cursor.beginEditBlock()
622 cursor.beginEditBlock()
613 cursor.insertText('\n')
623 cursor.insertText('\n')
614 self._insert_continuation_prompt(cursor)
624 self._insert_continuation_prompt(cursor)
615 cursor.endEditBlock()
625 cursor.endEditBlock()
616
626
617 # Do not do this inside the edit block. It works as expected
627 # Do not do this inside the edit block. It works as expected
618 # when using a QPlainTextEdit control, but does not have an
628 # when using a QPlainTextEdit control, but does not have an
619 # effect when using a QTextEdit. I believe this is a Qt bug.
629 # effect when using a QTextEdit. I believe this is a Qt bug.
620 self._control.moveCursor(QtGui.QTextCursor.End)
630 self._control.moveCursor(QtGui.QTextCursor.End)
621
631
622 return complete
632 return complete
623
633
624 def export_html(self):
634 def export_html(self):
625 """ Shows a dialog to export HTML/XML in various formats.
635 """ Shows a dialog to export HTML/XML in various formats.
626 """
636 """
627 self._html_exporter.export()
637 self._html_exporter.export()
628
638
629 def _get_input_buffer(self, force=False):
639 def _get_input_buffer(self, force=False):
630 """ The text that the user has entered entered at the current prompt.
640 """ The text that the user has entered entered at the current prompt.
631
641
632 If the console is currently executing, the text that is executing will
642 If the console is currently executing, the text that is executing will
633 always be returned.
643 always be returned.
634 """
644 """
635 # If we're executing, the input buffer may not even exist anymore due to
645 # If we're executing, the input buffer may not even exist anymore due to
636 # the limit imposed by 'buffer_size'. Therefore, we store it.
646 # the limit imposed by 'buffer_size'. Therefore, we store it.
637 if self._executing and not force:
647 if self._executing and not force:
638 return self._input_buffer_executing
648 return self._input_buffer_executing
639
649
640 cursor = self._get_end_cursor()
650 cursor = self._get_end_cursor()
641 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
651 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
642 input_buffer = cursor.selection().toPlainText()
652 input_buffer = cursor.selection().toPlainText()
643
653
644 # Strip out continuation prompts.
654 # Strip out continuation prompts.
645 return input_buffer.replace('\n' + self._continuation_prompt, '\n')
655 return input_buffer.replace('\n' + self._continuation_prompt, '\n')
646
656
647 def _set_input_buffer(self, string):
657 def _set_input_buffer(self, string):
648 """ Sets the text in the input buffer.
658 """ Sets the text in the input buffer.
649
659
650 If the console is currently executing, this call has no *immediate*
660 If the console is currently executing, this call has no *immediate*
651 effect. When the execution is finished, the input buffer will be updated
661 effect. When the execution is finished, the input buffer will be updated
652 appropriately.
662 appropriately.
653 """
663 """
654 # If we're executing, store the text for later.
664 # If we're executing, store the text for later.
655 if self._executing:
665 if self._executing:
656 self._input_buffer_pending = string
666 self._input_buffer_pending = string
657 return
667 return
658
668
659 # Remove old text.
669 # Remove old text.
660 cursor = self._get_end_cursor()
670 cursor = self._get_end_cursor()
661 cursor.beginEditBlock()
671 cursor.beginEditBlock()
662 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
672 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
663 cursor.removeSelectedText()
673 cursor.removeSelectedText()
664
674
665 # Insert new text with continuation prompts.
675 # Insert new text with continuation prompts.
666 self._insert_plain_text_into_buffer(self._get_prompt_cursor(), string)
676 self._insert_plain_text_into_buffer(self._get_prompt_cursor(), string)
667 cursor.endEditBlock()
677 cursor.endEditBlock()
668 self._control.moveCursor(QtGui.QTextCursor.End)
678 self._control.moveCursor(QtGui.QTextCursor.End)
669
679
670 input_buffer = property(_get_input_buffer, _set_input_buffer)
680 input_buffer = property(_get_input_buffer, _set_input_buffer)
671
681
672 def _get_font(self):
682 def _get_font(self):
673 """ The base font being used by the ConsoleWidget.
683 """ The base font being used by the ConsoleWidget.
674 """
684 """
675 return self._control.document().defaultFont()
685 return self._control.document().defaultFont()
676
686
677 def _set_font(self, font):
687 def _set_font(self, font):
678 """ Sets the base font for the ConsoleWidget to the specified QFont.
688 """ Sets the base font for the ConsoleWidget to the specified QFont.
679 """
689 """
680 font_metrics = QtGui.QFontMetrics(font)
690 font_metrics = QtGui.QFontMetrics(font)
681 self._control.setTabStopWidth(self.tab_width * font_metrics.width(' '))
691 self._control.setTabStopWidth(self.tab_width * font_metrics.width(' '))
682
692
683 self._completion_widget.setFont(font)
693 self._completion_widget.setFont(font)
684 self._control.document().setDefaultFont(font)
694 self._control.document().setDefaultFont(font)
685 if self._page_control:
695 if self._page_control:
686 self._page_control.document().setDefaultFont(font)
696 self._page_control.document().setDefaultFont(font)
687
697
688 self.font_changed.emit(font)
698 self.font_changed.emit(font)
689
699
690 font = property(_get_font, _set_font)
700 font = property(_get_font, _set_font)
691
701
692 def open_anchor(self, anchor):
702 def open_anchor(self, anchor):
693 """ Open selected anchor in the default webbrowser
703 """ Open selected anchor in the default webbrowser
694 """
704 """
695 webbrowser.open( anchor )
705 webbrowser.open( anchor )
696
706
697 def paste(self, mode=QtGui.QClipboard.Clipboard):
707 def paste(self, mode=QtGui.QClipboard.Clipboard):
698 """ Paste the contents of the clipboard into the input region.
708 """ Paste the contents of the clipboard into the input region.
699
709
700 Parameters:
710 Parameters:
701 -----------
711 -----------
702 mode : QClipboard::Mode, optional [default QClipboard::Clipboard]
712 mode : QClipboard::Mode, optional [default QClipboard::Clipboard]
703
713
704 Controls which part of the system clipboard is used. This can be
714 Controls which part of the system clipboard is used. This can be
705 used to access the selection clipboard in X11 and the Find buffer
715 used to access the selection clipboard in X11 and the Find buffer
706 in Mac OS. By default, the regular clipboard is used.
716 in Mac OS. By default, the regular clipboard is used.
707 """
717 """
708 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
718 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
709 # Make sure the paste is safe.
719 # Make sure the paste is safe.
710 self._keep_cursor_in_buffer()
720 self._keep_cursor_in_buffer()
711 cursor = self._control.textCursor()
721 cursor = self._control.textCursor()
712
722
713 # Remove any trailing newline, which confuses the GUI and forces the
723 # Remove any trailing newline, which confuses the GUI and forces the
714 # user to backspace.
724 # user to backspace.
715 text = QtGui.QApplication.clipboard().text(mode).rstrip()
725 text = QtGui.QApplication.clipboard().text(mode).rstrip()
716 self._insert_plain_text_into_buffer(cursor, dedent(text))
726 self._insert_plain_text_into_buffer(cursor, dedent(text))
717
727
718 def print_(self, printer = None):
728 def print_(self, printer = None):
719 """ Print the contents of the ConsoleWidget to the specified QPrinter.
729 """ Print the contents of the ConsoleWidget to the specified QPrinter.
720 """
730 """
721 if (not printer):
731 if (not printer):
722 printer = QtGui.QPrinter()
732 printer = QtGui.QPrinter()
723 if(QtGui.QPrintDialog(printer).exec_() != QtGui.QDialog.Accepted):
733 if(QtGui.QPrintDialog(printer).exec_() != QtGui.QDialog.Accepted):
724 return
734 return
725 self._control.print_(printer)
735 self._control.print_(printer)
726
736
727 def prompt_to_top(self):
737 def prompt_to_top(self):
728 """ Moves the prompt to the top of the viewport.
738 """ Moves the prompt to the top of the viewport.
729 """
739 """
730 if not self._executing:
740 if not self._executing:
731 prompt_cursor = self._get_prompt_cursor()
741 prompt_cursor = self._get_prompt_cursor()
732 if self._get_cursor().blockNumber() < prompt_cursor.blockNumber():
742 if self._get_cursor().blockNumber() < prompt_cursor.blockNumber():
733 self._set_cursor(prompt_cursor)
743 self._set_cursor(prompt_cursor)
734 self._set_top_cursor(prompt_cursor)
744 self._set_top_cursor(prompt_cursor)
735
745
736 def redo(self):
746 def redo(self):
737 """ Redo the last operation. If there is no operation to redo, nothing
747 """ Redo the last operation. If there is no operation to redo, nothing
738 happens.
748 happens.
739 """
749 """
740 self._control.redo()
750 self._control.redo()
741
751
742 def reset_font(self):
752 def reset_font(self):
743 """ Sets the font to the default fixed-width font for this platform.
753 """ Sets the font to the default fixed-width font for this platform.
744 """
754 """
745 if sys.platform == 'win32':
755 if sys.platform == 'win32':
746 # Consolas ships with Vista/Win7, fallback to Courier if needed
756 # Consolas ships with Vista/Win7, fallback to Courier if needed
747 fallback = 'Courier'
757 fallback = 'Courier'
748 elif sys.platform == 'darwin':
758 elif sys.platform == 'darwin':
749 # OSX always has Monaco
759 # OSX always has Monaco
750 fallback = 'Monaco'
760 fallback = 'Monaco'
751 else:
761 else:
752 # Monospace should always exist
762 # Monospace should always exist
753 fallback = 'Monospace'
763 fallback = 'Monospace'
754 font = get_font(self.font_family, fallback)
764 font = get_font(self.font_family, fallback)
755 if self.font_size:
765 if self.font_size:
756 font.setPointSize(self.font_size)
766 font.setPointSize(self.font_size)
757 else:
767 else:
758 font.setPointSize(QtGui.qApp.font().pointSize())
768 font.setPointSize(QtGui.qApp.font().pointSize())
759 font.setStyleHint(QtGui.QFont.TypeWriter)
769 font.setStyleHint(QtGui.QFont.TypeWriter)
760 self._set_font(font)
770 self._set_font(font)
761
771
762 def change_font_size(self, delta):
772 def change_font_size(self, delta):
763 """Change the font size by the specified amount (in points).
773 """Change the font size by the specified amount (in points).
764 """
774 """
765 font = self.font
775 font = self.font
766 size = max(font.pointSize() + delta, 1) # minimum 1 point
776 size = max(font.pointSize() + delta, 1) # minimum 1 point
767 font.setPointSize(size)
777 font.setPointSize(size)
768 self._set_font(font)
778 self._set_font(font)
769
779
770 def _increase_font_size(self):
780 def _increase_font_size(self):
771 self.change_font_size(1)
781 self.change_font_size(1)
772
782
773 def _decrease_font_size(self):
783 def _decrease_font_size(self):
774 self.change_font_size(-1)
784 self.change_font_size(-1)
775
785
776 def select_all(self):
786 def select_all(self):
777 """ Selects all the text in the buffer.
787 """ Selects all the text in the buffer.
778 """
788 """
779 self._control.selectAll()
789 self._control.selectAll()
780
790
781 def _get_tab_width(self):
791 def _get_tab_width(self):
782 """ The width (in terms of space characters) for tab characters.
792 """ The width (in terms of space characters) for tab characters.
783 """
793 """
784 return self._tab_width
794 return self._tab_width
785
795
786 def _set_tab_width(self, tab_width):
796 def _set_tab_width(self, tab_width):
787 """ Sets the width (in terms of space characters) for tab characters.
797 """ Sets the width (in terms of space characters) for tab characters.
788 """
798 """
789 font_metrics = QtGui.QFontMetrics(self.font)
799 font_metrics = QtGui.QFontMetrics(self.font)
790 self._control.setTabStopWidth(tab_width * font_metrics.width(' '))
800 self._control.setTabStopWidth(tab_width * font_metrics.width(' '))
791
801
792 self._tab_width = tab_width
802 self._tab_width = tab_width
793
803
794 tab_width = property(_get_tab_width, _set_tab_width)
804 tab_width = property(_get_tab_width, _set_tab_width)
795
805
796 def undo(self):
806 def undo(self):
797 """ Undo the last operation. If there is no operation to undo, nothing
807 """ Undo the last operation. If there is no operation to undo, nothing
798 happens.
808 happens.
799 """
809 """
800 self._control.undo()
810 self._control.undo()
801
811
802 #---------------------------------------------------------------------------
812 #---------------------------------------------------------------------------
803 # 'ConsoleWidget' abstract interface
813 # 'ConsoleWidget' abstract interface
804 #---------------------------------------------------------------------------
814 #---------------------------------------------------------------------------
805
815
806 def _is_complete(self, source, interactive):
816 def _is_complete(self, source, interactive):
807 """ Returns whether 'source' can be executed. When triggered by an
817 """ Returns whether 'source' can be executed. When triggered by an
808 Enter/Return key press, 'interactive' is True; otherwise, it is
818 Enter/Return key press, 'interactive' is True; otherwise, it is
809 False.
819 False.
810 """
820 """
811 raise NotImplementedError
821 raise NotImplementedError
812
822
813 def _execute(self, source, hidden):
823 def _execute(self, source, hidden):
814 """ Execute 'source'. If 'hidden', do not show any output.
824 """ Execute 'source'. If 'hidden', do not show any output.
815 """
825 """
816 raise NotImplementedError
826 raise NotImplementedError
817
827
818 def _prompt_started_hook(self):
828 def _prompt_started_hook(self):
819 """ Called immediately after a new prompt is displayed.
829 """ Called immediately after a new prompt is displayed.
820 """
830 """
821 pass
831 pass
822
832
823 def _prompt_finished_hook(self):
833 def _prompt_finished_hook(self):
824 """ Called immediately after a prompt is finished, i.e. when some input
834 """ Called immediately after a prompt is finished, i.e. when some input
825 will be processed and a new prompt displayed.
835 will be processed and a new prompt displayed.
826 """
836 """
827 pass
837 pass
828
838
829 def _up_pressed(self, shift_modifier):
839 def _up_pressed(self, shift_modifier):
830 """ Called when the up key is pressed. Returns whether to continue
840 """ Called when the up key is pressed. Returns whether to continue
831 processing the event.
841 processing the event.
832 """
842 """
833 return True
843 return True
834
844
835 def _down_pressed(self, shift_modifier):
845 def _down_pressed(self, shift_modifier):
836 """ Called when the down key is pressed. Returns whether to continue
846 """ Called when the down key is pressed. Returns whether to continue
837 processing the event.
847 processing the event.
838 """
848 """
839 return True
849 return True
840
850
841 def _tab_pressed(self):
851 def _tab_pressed(self):
842 """ Called when the tab key is pressed. Returns whether to continue
852 """ Called when the tab key is pressed. Returns whether to continue
843 processing the event.
853 processing the event.
844 """
854 """
845 return False
855 return False
846
856
847 #--------------------------------------------------------------------------
857 #--------------------------------------------------------------------------
848 # 'ConsoleWidget' protected interface
858 # 'ConsoleWidget' protected interface
849 #--------------------------------------------------------------------------
859 #--------------------------------------------------------------------------
850
860
851 def _append_custom(self, insert, input, before_prompt=False):
861 def _append_custom(self, insert, input, before_prompt=False):
852 """ A low-level method for appending content to the end of the buffer.
862 """ A low-level method for appending content to the end of the buffer.
853
863
854 If 'before_prompt' is enabled, the content will be inserted before the
864 If 'before_prompt' is enabled, the content will be inserted before the
855 current prompt, if there is one.
865 current prompt, if there is one.
856 """
866 """
857 # Determine where to insert the content.
867 # Determine where to insert the content.
858 cursor = self._control.textCursor()
868 cursor = self._control.textCursor()
859 if before_prompt and (self._reading or not self._executing):
869 if before_prompt and (self._reading or not self._executing):
860 cursor.setPosition(self._append_before_prompt_pos)
870 cursor.setPosition(self._append_before_prompt_pos)
861 else:
871 else:
862 cursor.movePosition(QtGui.QTextCursor.End)
872 cursor.movePosition(QtGui.QTextCursor.End)
863 start_pos = cursor.position()
873 start_pos = cursor.position()
864
874
865 # Perform the insertion.
875 # Perform the insertion.
866 result = insert(cursor, input)
876 result = insert(cursor, input)
867
877
868 # Adjust the prompt position if we have inserted before it. This is safe
878 # Adjust the prompt position if we have inserted before it. This is safe
869 # because buffer truncation is disabled when not executing.
879 # because buffer truncation is disabled when not executing.
870 if before_prompt and not self._executing:
880 if before_prompt and not self._executing:
871 diff = cursor.position() - start_pos
881 diff = cursor.position() - start_pos
872 self._append_before_prompt_pos += diff
882 self._append_before_prompt_pos += diff
873 self._prompt_pos += diff
883 self._prompt_pos += diff
874
884
875 return result
885 return result
876
886
877 def _append_block(self, block_format=None, before_prompt=False):
887 def _append_block(self, block_format=None, before_prompt=False):
878 """ Appends an new QTextBlock to the end of the console buffer.
888 """ Appends an new QTextBlock to the end of the console buffer.
879 """
889 """
880 self._append_custom(self._insert_block, block_format, before_prompt)
890 self._append_custom(self._insert_block, block_format, before_prompt)
881
891
882 def _append_html(self, html, before_prompt=False):
892 def _append_html(self, html, before_prompt=False):
883 """ Appends HTML at the end of the console buffer.
893 """ Appends HTML at the end of the console buffer.
884 """
894 """
885 self._append_custom(self._insert_html, html, before_prompt)
895 self._append_custom(self._insert_html, html, before_prompt)
886
896
887 def _append_html_fetching_plain_text(self, html, before_prompt=False):
897 def _append_html_fetching_plain_text(self, html, before_prompt=False):
888 """ Appends HTML, then returns the plain text version of it.
898 """ Appends HTML, then returns the plain text version of it.
889 """
899 """
890 return self._append_custom(self._insert_html_fetching_plain_text,
900 return self._append_custom(self._insert_html_fetching_plain_text,
891 html, before_prompt)
901 html, before_prompt)
892
902
893 def _append_plain_text(self, text, before_prompt=False):
903 def _append_plain_text(self, text, before_prompt=False):
894 """ Appends plain text, processing ANSI codes if enabled.
904 """ Appends plain text, processing ANSI codes if enabled.
895 """
905 """
896 self._append_custom(self._insert_plain_text, text, before_prompt)
906 self._append_custom(self._insert_plain_text, text, before_prompt)
897
907
898 def _cancel_completion(self):
908 def _cancel_completion(self):
899 """ If text completion is progress, cancel it.
909 """ If text completion is progress, cancel it.
900 """
910 """
901 self._completion_widget.cancel_completion()
911 self._completion_widget.cancel_completion()
902
912
903 def _clear_temporary_buffer(self):
913 def _clear_temporary_buffer(self):
904 """ Clears the "temporary text" buffer, i.e. all the text following
914 """ Clears the "temporary text" buffer, i.e. all the text following
905 the prompt region.
915 the prompt region.
906 """
916 """
907 # Select and remove all text below the input buffer.
917 # Select and remove all text below the input buffer.
908 cursor = self._get_prompt_cursor()
918 cursor = self._get_prompt_cursor()
909 prompt = self._continuation_prompt.lstrip()
919 prompt = self._continuation_prompt.lstrip()
910 if(self._temp_buffer_filled):
920 if(self._temp_buffer_filled):
911 self._temp_buffer_filled = False
921 self._temp_buffer_filled = False
912 while cursor.movePosition(QtGui.QTextCursor.NextBlock):
922 while cursor.movePosition(QtGui.QTextCursor.NextBlock):
913 temp_cursor = QtGui.QTextCursor(cursor)
923 temp_cursor = QtGui.QTextCursor(cursor)
914 temp_cursor.select(QtGui.QTextCursor.BlockUnderCursor)
924 temp_cursor.select(QtGui.QTextCursor.BlockUnderCursor)
915 text = temp_cursor.selection().toPlainText().lstrip()
925 text = temp_cursor.selection().toPlainText().lstrip()
916 if not text.startswith(prompt):
926 if not text.startswith(prompt):
917 break
927 break
918 else:
928 else:
919 # We've reached the end of the input buffer and no text follows.
929 # We've reached the end of the input buffer and no text follows.
920 return
930 return
921 cursor.movePosition(QtGui.QTextCursor.Left) # Grab the newline.
931 cursor.movePosition(QtGui.QTextCursor.Left) # Grab the newline.
922 cursor.movePosition(QtGui.QTextCursor.End,
932 cursor.movePosition(QtGui.QTextCursor.End,
923 QtGui.QTextCursor.KeepAnchor)
933 QtGui.QTextCursor.KeepAnchor)
924 cursor.removeSelectedText()
934 cursor.removeSelectedText()
925
935
926 # After doing this, we have no choice but to clear the undo/redo
936 # After doing this, we have no choice but to clear the undo/redo
927 # history. Otherwise, the text is not "temporary" at all, because it
937 # history. Otherwise, the text is not "temporary" at all, because it
928 # can be recalled with undo/redo. Unfortunately, Qt does not expose
938 # can be recalled with undo/redo. Unfortunately, Qt does not expose
929 # fine-grained control to the undo/redo system.
939 # fine-grained control to the undo/redo system.
930 if self._control.isUndoRedoEnabled():
940 if self._control.isUndoRedoEnabled():
931 self._control.setUndoRedoEnabled(False)
941 self._control.setUndoRedoEnabled(False)
932 self._control.setUndoRedoEnabled(True)
942 self._control.setUndoRedoEnabled(True)
933
943
934 def _complete_with_items(self, cursor, items):
944 def _complete_with_items(self, cursor, items):
935 """ Performs completion with 'items' at the specified cursor location.
945 """ Performs completion with 'items' at the specified cursor location.
936 """
946 """
937 self._cancel_completion()
947 self._cancel_completion()
938
948
939 if len(items) == 1:
949 if len(items) == 1:
940 cursor.setPosition(self._control.textCursor().position(),
950 cursor.setPosition(self._control.textCursor().position(),
941 QtGui.QTextCursor.KeepAnchor)
951 QtGui.QTextCursor.KeepAnchor)
942 cursor.insertText(items[0])
952 cursor.insertText(items[0])
943
953
944 elif len(items) > 1:
954 elif len(items) > 1:
945 current_pos = self._control.textCursor().position()
955 current_pos = self._control.textCursor().position()
946 prefix = commonprefix(items)
956 prefix = commonprefix(items)
947 if prefix:
957 if prefix:
948 cursor.setPosition(current_pos, QtGui.QTextCursor.KeepAnchor)
958 cursor.setPosition(current_pos, QtGui.QTextCursor.KeepAnchor)
949 cursor.insertText(prefix)
959 cursor.insertText(prefix)
950 current_pos = cursor.position()
960 current_pos = cursor.position()
951
961
952 cursor.movePosition(QtGui.QTextCursor.Left, n=len(prefix))
962 cursor.movePosition(QtGui.QTextCursor.Left, n=len(prefix))
953 self._completion_widget.show_items(cursor, items)
963 self._completion_widget.show_items(cursor, items)
954
964
955
965
956 def _fill_temporary_buffer(self, cursor, text, html=False):
966 def _fill_temporary_buffer(self, cursor, text, html=False):
957 """fill the area below the active editting zone with text"""
967 """fill the area below the active editting zone with text"""
958
968
959 current_pos = self._control.textCursor().position()
969 current_pos = self._control.textCursor().position()
960
970
961 cursor.beginEditBlock()
971 cursor.beginEditBlock()
962 self._append_plain_text('\n')
972 self._append_plain_text('\n')
963 self._page(text, html=html)
973 self._page(text, html=html)
964 cursor.endEditBlock()
974 cursor.endEditBlock()
965
975
966 cursor.setPosition(current_pos)
976 cursor.setPosition(current_pos)
967 self._control.moveCursor(QtGui.QTextCursor.End)
977 self._control.moveCursor(QtGui.QTextCursor.End)
968 self._control.setTextCursor(cursor)
978 self._control.setTextCursor(cursor)
969
979
970 self._temp_buffer_filled = True
980 self._temp_buffer_filled = True
971
981
972
982
973 def _context_menu_make(self, pos):
983 def _context_menu_make(self, pos):
974 """ Creates a context menu for the given QPoint (in widget coordinates).
984 """ Creates a context menu for the given QPoint (in widget coordinates).
975 """
985 """
976 menu = QtGui.QMenu(self)
986 menu = QtGui.QMenu(self)
977
987
978 self.cut_action = menu.addAction('Cut', self.cut)
988 self.cut_action = menu.addAction('Cut', self.cut)
979 self.cut_action.setEnabled(self.can_cut())
989 self.cut_action.setEnabled(self.can_cut())
980 self.cut_action.setShortcut(QtGui.QKeySequence.Cut)
990 self.cut_action.setShortcut(QtGui.QKeySequence.Cut)
981
991
982 self.copy_action = menu.addAction('Copy', self.copy)
992 self.copy_action = menu.addAction('Copy', self.copy)
983 self.copy_action.setEnabled(self.can_copy())
993 self.copy_action.setEnabled(self.can_copy())
984 self.copy_action.setShortcut(QtGui.QKeySequence.Copy)
994 self.copy_action.setShortcut(QtGui.QKeySequence.Copy)
985
995
986 self.paste_action = menu.addAction('Paste', self.paste)
996 self.paste_action = menu.addAction('Paste', self.paste)
987 self.paste_action.setEnabled(self.can_paste())
997 self.paste_action.setEnabled(self.can_paste())
988 self.paste_action.setShortcut(QtGui.QKeySequence.Paste)
998 self.paste_action.setShortcut(QtGui.QKeySequence.Paste)
989
999
990 anchor = self._control.anchorAt(pos)
1000 anchor = self._control.anchorAt(pos)
991 if anchor:
1001 if anchor:
992 menu.addSeparator()
1002 menu.addSeparator()
993 self.copy_link_action = menu.addAction(
1003 self.copy_link_action = menu.addAction(
994 'Copy Link Address', lambda: self.copy_anchor(anchor=anchor))
1004 'Copy Link Address', lambda: self.copy_anchor(anchor=anchor))
995 self.open_link_action = menu.addAction(
1005 self.open_link_action = menu.addAction(
996 'Open Link', lambda: self.open_anchor(anchor=anchor))
1006 'Open Link', lambda: self.open_anchor(anchor=anchor))
997
1007
998 menu.addSeparator()
1008 menu.addSeparator()
999 menu.addAction(self.select_all_action)
1009 menu.addAction(self.select_all_action)
1000
1010
1001 menu.addSeparator()
1011 menu.addSeparator()
1002 menu.addAction(self.export_action)
1012 menu.addAction(self.export_action)
1003 menu.addAction(self.print_action)
1013 menu.addAction(self.print_action)
1004
1014
1005 return menu
1015 return menu
1006
1016
1007 def _control_key_down(self, modifiers, include_command=False):
1017 def _control_key_down(self, modifiers, include_command=False):
1008 """ Given a KeyboardModifiers flags object, return whether the Control
1018 """ Given a KeyboardModifiers flags object, return whether the Control
1009 key is down.
1019 key is down.
1010
1020
1011 Parameters:
1021 Parameters:
1012 -----------
1022 -----------
1013 include_command : bool, optional (default True)
1023 include_command : bool, optional (default True)
1014 Whether to treat the Command key as a (mutually exclusive) synonym
1024 Whether to treat the Command key as a (mutually exclusive) synonym
1015 for Control when in Mac OS.
1025 for Control when in Mac OS.
1016 """
1026 """
1017 # Note that on Mac OS, ControlModifier corresponds to the Command key
1027 # Note that on Mac OS, ControlModifier corresponds to the Command key
1018 # while MetaModifier corresponds to the Control key.
1028 # while MetaModifier corresponds to the Control key.
1019 if sys.platform == 'darwin':
1029 if sys.platform == 'darwin':
1020 down = include_command and (modifiers & QtCore.Qt.ControlModifier)
1030 down = include_command and (modifiers & QtCore.Qt.ControlModifier)
1021 return bool(down) ^ bool(modifiers & QtCore.Qt.MetaModifier)
1031 return bool(down) ^ bool(modifiers & QtCore.Qt.MetaModifier)
1022 else:
1032 else:
1023 return bool(modifiers & QtCore.Qt.ControlModifier)
1033 return bool(modifiers & QtCore.Qt.ControlModifier)
1024
1034
1025 def _create_control(self):
1035 def _create_control(self):
1026 """ Creates and connects the underlying text widget.
1036 """ Creates and connects the underlying text widget.
1027 """
1037 """
1028 # Create the underlying control.
1038 # Create the underlying control.
1029 if self.custom_control:
1039 if self.custom_control:
1030 control = self.custom_control()
1040 control = self.custom_control()
1031 elif self.kind == 'plain':
1041 elif self.kind == 'plain':
1032 control = QtGui.QPlainTextEdit()
1042 control = QtGui.QPlainTextEdit()
1033 elif self.kind == 'rich':
1043 elif self.kind == 'rich':
1034 control = QtGui.QTextEdit()
1044 control = QtGui.QTextEdit()
1035 control.setAcceptRichText(False)
1045 control.setAcceptRichText(False)
1036 control.setMouseTracking(True)
1046 control.setMouseTracking(True)
1037
1047
1048 # Prevent the widget from handling drops, as we already provide
1049 # the logic in this class.
1050 control.setAcceptDrops(False)
1051
1038 # Install event filters. The filter on the viewport is needed for
1052 # Install event filters. The filter on the viewport is needed for
1039 # mouse events and drag events.
1053 # mouse events.
1040 control.installEventFilter(self)
1054 control.installEventFilter(self)
1041 control.viewport().installEventFilter(self)
1055 control.viewport().installEventFilter(self)
1042
1056
1043 # Connect signals.
1057 # Connect signals.
1044 control.customContextMenuRequested.connect(
1058 control.customContextMenuRequested.connect(
1045 self._custom_context_menu_requested)
1059 self._custom_context_menu_requested)
1046 control.copyAvailable.connect(self.copy_available)
1060 control.copyAvailable.connect(self.copy_available)
1047 control.redoAvailable.connect(self.redo_available)
1061 control.redoAvailable.connect(self.redo_available)
1048 control.undoAvailable.connect(self.undo_available)
1062 control.undoAvailable.connect(self.undo_available)
1049
1063
1050 # Hijack the document size change signal to prevent Qt from adjusting
1064 # Hijack the document size change signal to prevent Qt from adjusting
1051 # the viewport's scrollbar. We are relying on an implementation detail
1065 # the viewport's scrollbar. We are relying on an implementation detail
1052 # of Q(Plain)TextEdit here, which is potentially dangerous, but without
1066 # of Q(Plain)TextEdit here, which is potentially dangerous, but without
1053 # this functionality we cannot create a nice terminal interface.
1067 # this functionality we cannot create a nice terminal interface.
1054 layout = control.document().documentLayout()
1068 layout = control.document().documentLayout()
1055 layout.documentSizeChanged.disconnect()
1069 layout.documentSizeChanged.disconnect()
1056 layout.documentSizeChanged.connect(self._adjust_scrollbars)
1070 layout.documentSizeChanged.connect(self._adjust_scrollbars)
1057
1071
1058 # Configure the control.
1072 # Configure the control.
1059 control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1073 control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1060 control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
1074 control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
1061 control.setReadOnly(True)
1075 control.setReadOnly(True)
1062 control.setUndoRedoEnabled(False)
1076 control.setUndoRedoEnabled(False)
1063 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1077 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1064 return control
1078 return control
1065
1079
1066 def _create_page_control(self):
1080 def _create_page_control(self):
1067 """ Creates and connects the underlying paging widget.
1081 """ Creates and connects the underlying paging widget.
1068 """
1082 """
1069 if self.custom_page_control:
1083 if self.custom_page_control:
1070 control = self.custom_page_control()
1084 control = self.custom_page_control()
1071 elif self.kind == 'plain':
1085 elif self.kind == 'plain':
1072 control = QtGui.QPlainTextEdit()
1086 control = QtGui.QPlainTextEdit()
1073 elif self.kind == 'rich':
1087 elif self.kind == 'rich':
1074 control = QtGui.QTextEdit()
1088 control = QtGui.QTextEdit()
1075 control.installEventFilter(self)
1089 control.installEventFilter(self)
1076 viewport = control.viewport()
1090 viewport = control.viewport()
1077 viewport.installEventFilter(self)
1091 viewport.installEventFilter(self)
1078 control.setReadOnly(True)
1092 control.setReadOnly(True)
1079 control.setUndoRedoEnabled(False)
1093 control.setUndoRedoEnabled(False)
1080 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1094 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1081 return control
1095 return control
1082
1096
1083 def _event_filter_console_keypress(self, event):
1097 def _event_filter_console_keypress(self, event):
1084 """ Filter key events for the underlying text widget to create a
1098 """ Filter key events for the underlying text widget to create a
1085 console-like interface.
1099 console-like interface.
1086 """
1100 """
1087 intercepted = False
1101 intercepted = False
1088 cursor = self._control.textCursor()
1102 cursor = self._control.textCursor()
1089 position = cursor.position()
1103 position = cursor.position()
1090 key = event.key()
1104 key = event.key()
1091 ctrl_down = self._control_key_down(event.modifiers())
1105 ctrl_down = self._control_key_down(event.modifiers())
1092 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1106 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1093 shift_down = event.modifiers() & QtCore.Qt.ShiftModifier
1107 shift_down = event.modifiers() & QtCore.Qt.ShiftModifier
1094
1108
1095 #------ Special sequences ----------------------------------------------
1109 #------ Special sequences ----------------------------------------------
1096
1110
1097 if event.matches(QtGui.QKeySequence.Copy):
1111 if event.matches(QtGui.QKeySequence.Copy):
1098 self.copy()
1112 self.copy()
1099 intercepted = True
1113 intercepted = True
1100
1114
1101 elif event.matches(QtGui.QKeySequence.Cut):
1115 elif event.matches(QtGui.QKeySequence.Cut):
1102 self.cut()
1116 self.cut()
1103 intercepted = True
1117 intercepted = True
1104
1118
1105 elif event.matches(QtGui.QKeySequence.Paste):
1119 elif event.matches(QtGui.QKeySequence.Paste):
1106 self.paste()
1120 self.paste()
1107 intercepted = True
1121 intercepted = True
1108
1122
1109 #------ Special modifier logic -----------------------------------------
1123 #------ Special modifier logic -----------------------------------------
1110
1124
1111 elif key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
1125 elif key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
1112 intercepted = True
1126 intercepted = True
1113
1127
1114 # Special handling when tab completing in text mode.
1128 # Special handling when tab completing in text mode.
1115 self._cancel_completion()
1129 self._cancel_completion()
1116
1130
1117 if self._in_buffer(position):
1131 if self._in_buffer(position):
1118 # Special handling when a reading a line of raw input.
1132 # Special handling when a reading a line of raw input.
1119 if self._reading:
1133 if self._reading:
1120 self._append_plain_text('\n')
1134 self._append_plain_text('\n')
1121 self._reading = False
1135 self._reading = False
1122 if self._reading_callback:
1136 if self._reading_callback:
1123 self._reading_callback()
1137 self._reading_callback()
1124
1138
1125 # If the input buffer is a single line or there is only
1139 # If the input buffer is a single line or there is only
1126 # whitespace after the cursor, execute. Otherwise, split the
1140 # whitespace after the cursor, execute. Otherwise, split the
1127 # line with a continuation prompt.
1141 # line with a continuation prompt.
1128 elif not self._executing:
1142 elif not self._executing:
1129 cursor.movePosition(QtGui.QTextCursor.End,
1143 cursor.movePosition(QtGui.QTextCursor.End,
1130 QtGui.QTextCursor.KeepAnchor)
1144 QtGui.QTextCursor.KeepAnchor)
1131 at_end = len(cursor.selectedText().strip()) == 0
1145 at_end = len(cursor.selectedText().strip()) == 0
1132 single_line = (self._get_end_cursor().blockNumber() ==
1146 single_line = (self._get_end_cursor().blockNumber() ==
1133 self._get_prompt_cursor().blockNumber())
1147 self._get_prompt_cursor().blockNumber())
1134 if (at_end or shift_down or single_line) and not ctrl_down:
1148 if (at_end or shift_down or single_line) and not ctrl_down:
1135 self.execute(interactive = not shift_down)
1149 self.execute(interactive = not shift_down)
1136 else:
1150 else:
1137 # Do this inside an edit block for clean undo/redo.
1151 # Do this inside an edit block for clean undo/redo.
1138 cursor.beginEditBlock()
1152 cursor.beginEditBlock()
1139 cursor.setPosition(position)
1153 cursor.setPosition(position)
1140 cursor.insertText('\n')
1154 cursor.insertText('\n')
1141 self._insert_continuation_prompt(cursor)
1155 self._insert_continuation_prompt(cursor)
1142 cursor.endEditBlock()
1156 cursor.endEditBlock()
1143
1157
1144 # Ensure that the whole input buffer is visible.
1158 # Ensure that the whole input buffer is visible.
1145 # FIXME: This will not be usable if the input buffer is
1159 # FIXME: This will not be usable if the input buffer is
1146 # taller than the console widget.
1160 # taller than the console widget.
1147 self._control.moveCursor(QtGui.QTextCursor.End)
1161 self._control.moveCursor(QtGui.QTextCursor.End)
1148 self._control.setTextCursor(cursor)
1162 self._control.setTextCursor(cursor)
1149
1163
1150 #------ Control/Cmd modifier -------------------------------------------
1164 #------ Control/Cmd modifier -------------------------------------------
1151
1165
1152 elif ctrl_down:
1166 elif ctrl_down:
1153 if key == QtCore.Qt.Key_G:
1167 if key == QtCore.Qt.Key_G:
1154 self._keyboard_quit()
1168 self._keyboard_quit()
1155 intercepted = True
1169 intercepted = True
1156
1170
1157 elif key == QtCore.Qt.Key_K:
1171 elif key == QtCore.Qt.Key_K:
1158 if self._in_buffer(position):
1172 if self._in_buffer(position):
1159 cursor.clearSelection()
1173 cursor.clearSelection()
1160 cursor.movePosition(QtGui.QTextCursor.EndOfLine,
1174 cursor.movePosition(QtGui.QTextCursor.EndOfLine,
1161 QtGui.QTextCursor.KeepAnchor)
1175 QtGui.QTextCursor.KeepAnchor)
1162 if not cursor.hasSelection():
1176 if not cursor.hasSelection():
1163 # Line deletion (remove continuation prompt)
1177 # Line deletion (remove continuation prompt)
1164 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1178 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1165 QtGui.QTextCursor.KeepAnchor)
1179 QtGui.QTextCursor.KeepAnchor)
1166 cursor.movePosition(QtGui.QTextCursor.Right,
1180 cursor.movePosition(QtGui.QTextCursor.Right,
1167 QtGui.QTextCursor.KeepAnchor,
1181 QtGui.QTextCursor.KeepAnchor,
1168 len(self._continuation_prompt))
1182 len(self._continuation_prompt))
1169 self._kill_ring.kill_cursor(cursor)
1183 self._kill_ring.kill_cursor(cursor)
1170 self._set_cursor(cursor)
1184 self._set_cursor(cursor)
1171 intercepted = True
1185 intercepted = True
1172
1186
1173 elif key == QtCore.Qt.Key_L:
1187 elif key == QtCore.Qt.Key_L:
1174 self.prompt_to_top()
1188 self.prompt_to_top()
1175 intercepted = True
1189 intercepted = True
1176
1190
1177 elif key == QtCore.Qt.Key_O:
1191 elif key == QtCore.Qt.Key_O:
1178 if self._page_control and self._page_control.isVisible():
1192 if self._page_control and self._page_control.isVisible():
1179 self._page_control.setFocus()
1193 self._page_control.setFocus()
1180 intercepted = True
1194 intercepted = True
1181
1195
1182 elif key == QtCore.Qt.Key_U:
1196 elif key == QtCore.Qt.Key_U:
1183 if self._in_buffer(position):
1197 if self._in_buffer(position):
1184 cursor.clearSelection()
1198 cursor.clearSelection()
1185 start_line = cursor.blockNumber()
1199 start_line = cursor.blockNumber()
1186 if start_line == self._get_prompt_cursor().blockNumber():
1200 if start_line == self._get_prompt_cursor().blockNumber():
1187 offset = len(self._prompt)
1201 offset = len(self._prompt)
1188 else:
1202 else:
1189 offset = len(self._continuation_prompt)
1203 offset = len(self._continuation_prompt)
1190 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1204 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1191 QtGui.QTextCursor.KeepAnchor)
1205 QtGui.QTextCursor.KeepAnchor)
1192 cursor.movePosition(QtGui.QTextCursor.Right,
1206 cursor.movePosition(QtGui.QTextCursor.Right,
1193 QtGui.QTextCursor.KeepAnchor, offset)
1207 QtGui.QTextCursor.KeepAnchor, offset)
1194 self._kill_ring.kill_cursor(cursor)
1208 self._kill_ring.kill_cursor(cursor)
1195 self._set_cursor(cursor)
1209 self._set_cursor(cursor)
1196 intercepted = True
1210 intercepted = True
1197
1211
1198 elif key == QtCore.Qt.Key_Y:
1212 elif key == QtCore.Qt.Key_Y:
1199 self._keep_cursor_in_buffer()
1213 self._keep_cursor_in_buffer()
1200 self._kill_ring.yank()
1214 self._kill_ring.yank()
1201 intercepted = True
1215 intercepted = True
1202
1216
1203 elif key in (QtCore.Qt.Key_Backspace, QtCore.Qt.Key_Delete):
1217 elif key in (QtCore.Qt.Key_Backspace, QtCore.Qt.Key_Delete):
1204 if key == QtCore.Qt.Key_Backspace:
1218 if key == QtCore.Qt.Key_Backspace:
1205 cursor = self._get_word_start_cursor(position)
1219 cursor = self._get_word_start_cursor(position)
1206 else: # key == QtCore.Qt.Key_Delete
1220 else: # key == QtCore.Qt.Key_Delete
1207 cursor = self._get_word_end_cursor(position)
1221 cursor = self._get_word_end_cursor(position)
1208 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1222 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1209 self._kill_ring.kill_cursor(cursor)
1223 self._kill_ring.kill_cursor(cursor)
1210 intercepted = True
1224 intercepted = True
1211
1225
1212 elif key == QtCore.Qt.Key_D:
1226 elif key == QtCore.Qt.Key_D:
1213 if len(self.input_buffer) == 0:
1227 if len(self.input_buffer) == 0:
1214 self.exit_requested.emit(self)
1228 self.exit_requested.emit(self)
1215 else:
1229 else:
1216 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1230 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1217 QtCore.Qt.Key_Delete,
1231 QtCore.Qt.Key_Delete,
1218 QtCore.Qt.NoModifier)
1232 QtCore.Qt.NoModifier)
1219 QtGui.qApp.sendEvent(self._control, new_event)
1233 QtGui.qApp.sendEvent(self._control, new_event)
1220 intercepted = True
1234 intercepted = True
1221
1235
1222 #------ Alt modifier ---------------------------------------------------
1236 #------ Alt modifier ---------------------------------------------------
1223
1237
1224 elif alt_down:
1238 elif alt_down:
1225 if key == QtCore.Qt.Key_B:
1239 if key == QtCore.Qt.Key_B:
1226 self._set_cursor(self._get_word_start_cursor(position))
1240 self._set_cursor(self._get_word_start_cursor(position))
1227 intercepted = True
1241 intercepted = True
1228
1242
1229 elif key == QtCore.Qt.Key_F:
1243 elif key == QtCore.Qt.Key_F:
1230 self._set_cursor(self._get_word_end_cursor(position))
1244 self._set_cursor(self._get_word_end_cursor(position))
1231 intercepted = True
1245 intercepted = True
1232
1246
1233 elif key == QtCore.Qt.Key_Y:
1247 elif key == QtCore.Qt.Key_Y:
1234 self._kill_ring.rotate()
1248 self._kill_ring.rotate()
1235 intercepted = True
1249 intercepted = True
1236
1250
1237 elif key == QtCore.Qt.Key_Backspace:
1251 elif key == QtCore.Qt.Key_Backspace:
1238 cursor = self._get_word_start_cursor(position)
1252 cursor = self._get_word_start_cursor(position)
1239 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1253 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1240 self._kill_ring.kill_cursor(cursor)
1254 self._kill_ring.kill_cursor(cursor)
1241 intercepted = True
1255 intercepted = True
1242
1256
1243 elif key == QtCore.Qt.Key_D:
1257 elif key == QtCore.Qt.Key_D:
1244 cursor = self._get_word_end_cursor(position)
1258 cursor = self._get_word_end_cursor(position)
1245 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1259 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1246 self._kill_ring.kill_cursor(cursor)
1260 self._kill_ring.kill_cursor(cursor)
1247 intercepted = True
1261 intercepted = True
1248
1262
1249 elif key == QtCore.Qt.Key_Delete:
1263 elif key == QtCore.Qt.Key_Delete:
1250 intercepted = True
1264 intercepted = True
1251
1265
1252 elif key == QtCore.Qt.Key_Greater:
1266 elif key == QtCore.Qt.Key_Greater:
1253 self._control.moveCursor(QtGui.QTextCursor.End)
1267 self._control.moveCursor(QtGui.QTextCursor.End)
1254 intercepted = True
1268 intercepted = True
1255
1269
1256 elif key == QtCore.Qt.Key_Less:
1270 elif key == QtCore.Qt.Key_Less:
1257 self._control.setTextCursor(self._get_prompt_cursor())
1271 self._control.setTextCursor(self._get_prompt_cursor())
1258 intercepted = True
1272 intercepted = True
1259
1273
1260 #------ No modifiers ---------------------------------------------------
1274 #------ No modifiers ---------------------------------------------------
1261
1275
1262 else:
1276 else:
1263 if shift_down:
1277 if shift_down:
1264 anchormode = QtGui.QTextCursor.KeepAnchor
1278 anchormode = QtGui.QTextCursor.KeepAnchor
1265 else:
1279 else:
1266 anchormode = QtGui.QTextCursor.MoveAnchor
1280 anchormode = QtGui.QTextCursor.MoveAnchor
1267
1281
1268 if key == QtCore.Qt.Key_Escape:
1282 if key == QtCore.Qt.Key_Escape:
1269 self._keyboard_quit()
1283 self._keyboard_quit()
1270 intercepted = True
1284 intercepted = True
1271
1285
1272 elif key == QtCore.Qt.Key_Up:
1286 elif key == QtCore.Qt.Key_Up:
1273 if self._reading or not self._up_pressed(shift_down):
1287 if self._reading or not self._up_pressed(shift_down):
1274 intercepted = True
1288 intercepted = True
1275 else:
1289 else:
1276 prompt_line = self._get_prompt_cursor().blockNumber()
1290 prompt_line = self._get_prompt_cursor().blockNumber()
1277 intercepted = cursor.blockNumber() <= prompt_line
1291 intercepted = cursor.blockNumber() <= prompt_line
1278
1292
1279 elif key == QtCore.Qt.Key_Down:
1293 elif key == QtCore.Qt.Key_Down:
1280 if self._reading or not self._down_pressed(shift_down):
1294 if self._reading or not self._down_pressed(shift_down):
1281 intercepted = True
1295 intercepted = True
1282 else:
1296 else:
1283 end_line = self._get_end_cursor().blockNumber()
1297 end_line = self._get_end_cursor().blockNumber()
1284 intercepted = cursor.blockNumber() == end_line
1298 intercepted = cursor.blockNumber() == end_line
1285
1299
1286 elif key == QtCore.Qt.Key_Tab:
1300 elif key == QtCore.Qt.Key_Tab:
1287 if not self._reading:
1301 if not self._reading:
1288 if self._tab_pressed():
1302 if self._tab_pressed():
1289 # real tab-key, insert four spaces
1303 # real tab-key, insert four spaces
1290 cursor.insertText(' '*4)
1304 cursor.insertText(' '*4)
1291 intercepted = True
1305 intercepted = True
1292
1306
1293 elif key == QtCore.Qt.Key_Left:
1307 elif key == QtCore.Qt.Key_Left:
1294
1308
1295 # Move to the previous line
1309 # Move to the previous line
1296 line, col = cursor.blockNumber(), cursor.columnNumber()
1310 line, col = cursor.blockNumber(), cursor.columnNumber()
1297 if line > self._get_prompt_cursor().blockNumber() and \
1311 if line > self._get_prompt_cursor().blockNumber() and \
1298 col == len(self._continuation_prompt):
1312 col == len(self._continuation_prompt):
1299 self._control.moveCursor(QtGui.QTextCursor.PreviousBlock,
1313 self._control.moveCursor(QtGui.QTextCursor.PreviousBlock,
1300 mode=anchormode)
1314 mode=anchormode)
1301 self._control.moveCursor(QtGui.QTextCursor.EndOfBlock,
1315 self._control.moveCursor(QtGui.QTextCursor.EndOfBlock,
1302 mode=anchormode)
1316 mode=anchormode)
1303 intercepted = True
1317 intercepted = True
1304
1318
1305 # Regular left movement
1319 # Regular left movement
1306 else:
1320 else:
1307 intercepted = not self._in_buffer(position - 1)
1321 intercepted = not self._in_buffer(position - 1)
1308
1322
1309 elif key == QtCore.Qt.Key_Right:
1323 elif key == QtCore.Qt.Key_Right:
1310 original_block_number = cursor.blockNumber()
1324 original_block_number = cursor.blockNumber()
1311 cursor.movePosition(QtGui.QTextCursor.Right,
1325 cursor.movePosition(QtGui.QTextCursor.Right,
1312 mode=anchormode)
1326 mode=anchormode)
1313 if cursor.blockNumber() != original_block_number:
1327 if cursor.blockNumber() != original_block_number:
1314 cursor.movePosition(QtGui.QTextCursor.Right,
1328 cursor.movePosition(QtGui.QTextCursor.Right,
1315 n=len(self._continuation_prompt),
1329 n=len(self._continuation_prompt),
1316 mode=anchormode)
1330 mode=anchormode)
1317 self._set_cursor(cursor)
1331 self._set_cursor(cursor)
1318 intercepted = True
1332 intercepted = True
1319
1333
1320 elif key == QtCore.Qt.Key_Home:
1334 elif key == QtCore.Qt.Key_Home:
1321 start_line = cursor.blockNumber()
1335 start_line = cursor.blockNumber()
1322 if start_line == self._get_prompt_cursor().blockNumber():
1336 if start_line == self._get_prompt_cursor().blockNumber():
1323 start_pos = self._prompt_pos
1337 start_pos = self._prompt_pos
1324 else:
1338 else:
1325 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1339 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1326 QtGui.QTextCursor.KeepAnchor)
1340 QtGui.QTextCursor.KeepAnchor)
1327 start_pos = cursor.position()
1341 start_pos = cursor.position()
1328 start_pos += len(self._continuation_prompt)
1342 start_pos += len(self._continuation_prompt)
1329 cursor.setPosition(position)
1343 cursor.setPosition(position)
1330 if shift_down and self._in_buffer(position):
1344 if shift_down and self._in_buffer(position):
1331 cursor.setPosition(start_pos, QtGui.QTextCursor.KeepAnchor)
1345 cursor.setPosition(start_pos, QtGui.QTextCursor.KeepAnchor)
1332 else:
1346 else:
1333 cursor.setPosition(start_pos)
1347 cursor.setPosition(start_pos)
1334 self._set_cursor(cursor)
1348 self._set_cursor(cursor)
1335 intercepted = True
1349 intercepted = True
1336
1350
1337 elif key == QtCore.Qt.Key_Backspace:
1351 elif key == QtCore.Qt.Key_Backspace:
1338
1352
1339 # Line deletion (remove continuation prompt)
1353 # Line deletion (remove continuation prompt)
1340 line, col = cursor.blockNumber(), cursor.columnNumber()
1354 line, col = cursor.blockNumber(), cursor.columnNumber()
1341 if not self._reading and \
1355 if not self._reading and \
1342 col == len(self._continuation_prompt) and \
1356 col == len(self._continuation_prompt) and \
1343 line > self._get_prompt_cursor().blockNumber():
1357 line > self._get_prompt_cursor().blockNumber():
1344 cursor.beginEditBlock()
1358 cursor.beginEditBlock()
1345 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1359 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1346 QtGui.QTextCursor.KeepAnchor)
1360 QtGui.QTextCursor.KeepAnchor)
1347 cursor.removeSelectedText()
1361 cursor.removeSelectedText()
1348 cursor.deletePreviousChar()
1362 cursor.deletePreviousChar()
1349 cursor.endEditBlock()
1363 cursor.endEditBlock()
1350 intercepted = True
1364 intercepted = True
1351
1365
1352 # Regular backwards deletion
1366 # Regular backwards deletion
1353 else:
1367 else:
1354 anchor = cursor.anchor()
1368 anchor = cursor.anchor()
1355 if anchor == position:
1369 if anchor == position:
1356 intercepted = not self._in_buffer(position - 1)
1370 intercepted = not self._in_buffer(position - 1)
1357 else:
1371 else:
1358 intercepted = not self._in_buffer(min(anchor, position))
1372 intercepted = not self._in_buffer(min(anchor, position))
1359
1373
1360 elif key == QtCore.Qt.Key_Delete:
1374 elif key == QtCore.Qt.Key_Delete:
1361
1375
1362 # Line deletion (remove continuation prompt)
1376 # Line deletion (remove continuation prompt)
1363 if not self._reading and self._in_buffer(position) and \
1377 if not self._reading and self._in_buffer(position) and \
1364 cursor.atBlockEnd() and not cursor.hasSelection():
1378 cursor.atBlockEnd() and not cursor.hasSelection():
1365 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1379 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1366 QtGui.QTextCursor.KeepAnchor)
1380 QtGui.QTextCursor.KeepAnchor)
1367 cursor.movePosition(QtGui.QTextCursor.Right,
1381 cursor.movePosition(QtGui.QTextCursor.Right,
1368 QtGui.QTextCursor.KeepAnchor,
1382 QtGui.QTextCursor.KeepAnchor,
1369 len(self._continuation_prompt))
1383 len(self._continuation_prompt))
1370 cursor.removeSelectedText()
1384 cursor.removeSelectedText()
1371 intercepted = True
1385 intercepted = True
1372
1386
1373 # Regular forwards deletion:
1387 # Regular forwards deletion:
1374 else:
1388 else:
1375 anchor = cursor.anchor()
1389 anchor = cursor.anchor()
1376 intercepted = (not self._in_buffer(anchor) or
1390 intercepted = (not self._in_buffer(anchor) or
1377 not self._in_buffer(position))
1391 not self._in_buffer(position))
1378
1392
1379 # Don't move the cursor if Control/Cmd is pressed to allow copy-paste
1393 # Don't move the cursor if Control/Cmd is pressed to allow copy-paste
1380 # using the keyboard in any part of the buffer. Also, permit scrolling
1394 # using the keyboard in any part of the buffer. Also, permit scrolling
1381 # with Page Up/Down keys. Finally, if we're executing, don't move the
1395 # with Page Up/Down keys. Finally, if we're executing, don't move the
1382 # cursor (if even this made sense, we can't guarantee that the prompt
1396 # cursor (if even this made sense, we can't guarantee that the prompt
1383 # position is still valid due to text truncation).
1397 # position is still valid due to text truncation).
1384 if not (self._control_key_down(event.modifiers(), include_command=True)
1398 if not (self._control_key_down(event.modifiers(), include_command=True)
1385 or key in (QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown)
1399 or key in (QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown)
1386 or (self._executing and not self._reading)):
1400 or (self._executing and not self._reading)):
1387 self._keep_cursor_in_buffer()
1401 self._keep_cursor_in_buffer()
1388
1402
1389 return intercepted
1403 return intercepted
1390
1404
1391 def _event_filter_page_keypress(self, event):
1405 def _event_filter_page_keypress(self, event):
1392 """ Filter key events for the paging widget to create console-like
1406 """ Filter key events for the paging widget to create console-like
1393 interface.
1407 interface.
1394 """
1408 """
1395 key = event.key()
1409 key = event.key()
1396 ctrl_down = self._control_key_down(event.modifiers())
1410 ctrl_down = self._control_key_down(event.modifiers())
1397 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1411 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1398
1412
1399 if ctrl_down:
1413 if ctrl_down:
1400 if key == QtCore.Qt.Key_O:
1414 if key == QtCore.Qt.Key_O:
1401 self._control.setFocus()
1415 self._control.setFocus()
1402 intercept = True
1416 intercept = True
1403
1417
1404 elif alt_down:
1418 elif alt_down:
1405 if key == QtCore.Qt.Key_Greater:
1419 if key == QtCore.Qt.Key_Greater:
1406 self._page_control.moveCursor(QtGui.QTextCursor.End)
1420 self._page_control.moveCursor(QtGui.QTextCursor.End)
1407 intercepted = True
1421 intercepted = True
1408
1422
1409 elif key == QtCore.Qt.Key_Less:
1423 elif key == QtCore.Qt.Key_Less:
1410 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1424 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1411 intercepted = True
1425 intercepted = True
1412
1426
1413 elif key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
1427 elif key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
1414 if self._splitter:
1428 if self._splitter:
1415 self._page_control.hide()
1429 self._page_control.hide()
1416 self._control.setFocus()
1430 self._control.setFocus()
1417 else:
1431 else:
1418 self.layout().setCurrentWidget(self._control)
1432 self.layout().setCurrentWidget(self._control)
1419 return True
1433 return True
1420
1434
1421 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return,
1435 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return,
1422 QtCore.Qt.Key_Tab):
1436 QtCore.Qt.Key_Tab):
1423 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1437 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1424 QtCore.Qt.Key_PageDown,
1438 QtCore.Qt.Key_PageDown,
1425 QtCore.Qt.NoModifier)
1439 QtCore.Qt.NoModifier)
1426 QtGui.qApp.sendEvent(self._page_control, new_event)
1440 QtGui.qApp.sendEvent(self._page_control, new_event)
1427 return True
1441 return True
1428
1442
1429 elif key == QtCore.Qt.Key_Backspace:
1443 elif key == QtCore.Qt.Key_Backspace:
1430 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1444 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1431 QtCore.Qt.Key_PageUp,
1445 QtCore.Qt.Key_PageUp,
1432 QtCore.Qt.NoModifier)
1446 QtCore.Qt.NoModifier)
1433 QtGui.qApp.sendEvent(self._page_control, new_event)
1447 QtGui.qApp.sendEvent(self._page_control, new_event)
1434 return True
1448 return True
1435
1449
1436 return False
1450 return False
1437
1451
1438 def _format_as_columns(self, items, separator=' '):
1452 def _format_as_columns(self, items, separator=' '):
1439 """ Transform a list of strings into a single string with columns.
1453 """ Transform a list of strings into a single string with columns.
1440
1454
1441 Parameters
1455 Parameters
1442 ----------
1456 ----------
1443 items : sequence of strings
1457 items : sequence of strings
1444 The strings to process.
1458 The strings to process.
1445
1459
1446 separator : str, optional [default is two spaces]
1460 separator : str, optional [default is two spaces]
1447 The string that separates columns.
1461 The string that separates columns.
1448
1462
1449 Returns
1463 Returns
1450 -------
1464 -------
1451 The formatted string.
1465 The formatted string.
1452 """
1466 """
1453 # Calculate the number of characters available.
1467 # Calculate the number of characters available.
1454 width = self._control.viewport().width()
1468 width = self._control.viewport().width()
1455 char_width = QtGui.QFontMetrics(self.font).width(' ')
1469 char_width = QtGui.QFontMetrics(self.font).width(' ')
1456 displaywidth = max(10, (width / char_width) - 1)
1470 displaywidth = max(10, (width / char_width) - 1)
1457
1471
1458 return columnize(items, separator, displaywidth)
1472 return columnize(items, separator, displaywidth)
1459
1473
1460 def _get_block_plain_text(self, block):
1474 def _get_block_plain_text(self, block):
1461 """ Given a QTextBlock, return its unformatted text.
1475 """ Given a QTextBlock, return its unformatted text.
1462 """
1476 """
1463 cursor = QtGui.QTextCursor(block)
1477 cursor = QtGui.QTextCursor(block)
1464 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1478 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1465 cursor.movePosition(QtGui.QTextCursor.EndOfBlock,
1479 cursor.movePosition(QtGui.QTextCursor.EndOfBlock,
1466 QtGui.QTextCursor.KeepAnchor)
1480 QtGui.QTextCursor.KeepAnchor)
1467 return cursor.selection().toPlainText()
1481 return cursor.selection().toPlainText()
1468
1482
1469 def _get_cursor(self):
1483 def _get_cursor(self):
1470 """ Convenience method that returns a cursor for the current position.
1484 """ Convenience method that returns a cursor for the current position.
1471 """
1485 """
1472 return self._control.textCursor()
1486 return self._control.textCursor()
1473
1487
1474 def _get_end_cursor(self):
1488 def _get_end_cursor(self):
1475 """ Convenience method that returns a cursor for the last character.
1489 """ Convenience method that returns a cursor for the last character.
1476 """
1490 """
1477 cursor = self._control.textCursor()
1491 cursor = self._control.textCursor()
1478 cursor.movePosition(QtGui.QTextCursor.End)
1492 cursor.movePosition(QtGui.QTextCursor.End)
1479 return cursor
1493 return cursor
1480
1494
1481 def _get_input_buffer_cursor_column(self):
1495 def _get_input_buffer_cursor_column(self):
1482 """ Returns the column of the cursor in the input buffer, excluding the
1496 """ Returns the column of the cursor in the input buffer, excluding the
1483 contribution by the prompt, or -1 if there is no such column.
1497 contribution by the prompt, or -1 if there is no such column.
1484 """
1498 """
1485 prompt = self._get_input_buffer_cursor_prompt()
1499 prompt = self._get_input_buffer_cursor_prompt()
1486 if prompt is None:
1500 if prompt is None:
1487 return -1
1501 return -1
1488 else:
1502 else:
1489 cursor = self._control.textCursor()
1503 cursor = self._control.textCursor()
1490 return cursor.columnNumber() - len(prompt)
1504 return cursor.columnNumber() - len(prompt)
1491
1505
1492 def _get_input_buffer_cursor_line(self):
1506 def _get_input_buffer_cursor_line(self):
1493 """ Returns the text of the line of the input buffer that contains the
1507 """ Returns the text of the line of the input buffer that contains the
1494 cursor, or None if there is no such line.
1508 cursor, or None if there is no such line.
1495 """
1509 """
1496 prompt = self._get_input_buffer_cursor_prompt()
1510 prompt = self._get_input_buffer_cursor_prompt()
1497 if prompt is None:
1511 if prompt is None:
1498 return None
1512 return None
1499 else:
1513 else:
1500 cursor = self._control.textCursor()
1514 cursor = self._control.textCursor()
1501 text = self._get_block_plain_text(cursor.block())
1515 text = self._get_block_plain_text(cursor.block())
1502 return text[len(prompt):]
1516 return text[len(prompt):]
1503
1517
1504 def _get_input_buffer_cursor_prompt(self):
1518 def _get_input_buffer_cursor_prompt(self):
1505 """ Returns the (plain text) prompt for line of the input buffer that
1519 """ Returns the (plain text) prompt for line of the input buffer that
1506 contains the cursor, or None if there is no such line.
1520 contains the cursor, or None if there is no such line.
1507 """
1521 """
1508 if self._executing:
1522 if self._executing:
1509 return None
1523 return None
1510 cursor = self._control.textCursor()
1524 cursor = self._control.textCursor()
1511 if cursor.position() >= self._prompt_pos:
1525 if cursor.position() >= self._prompt_pos:
1512 if cursor.blockNumber() == self._get_prompt_cursor().blockNumber():
1526 if cursor.blockNumber() == self._get_prompt_cursor().blockNumber():
1513 return self._prompt
1527 return self._prompt
1514 else:
1528 else:
1515 return self._continuation_prompt
1529 return self._continuation_prompt
1516 else:
1530 else:
1517 return None
1531 return None
1518
1532
1519 def _get_prompt_cursor(self):
1533 def _get_prompt_cursor(self):
1520 """ Convenience method that returns a cursor for the prompt position.
1534 """ Convenience method that returns a cursor for the prompt position.
1521 """
1535 """
1522 cursor = self._control.textCursor()
1536 cursor = self._control.textCursor()
1523 cursor.setPosition(self._prompt_pos)
1537 cursor.setPosition(self._prompt_pos)
1524 return cursor
1538 return cursor
1525
1539
1526 def _get_selection_cursor(self, start, end):
1540 def _get_selection_cursor(self, start, end):
1527 """ Convenience method that returns a cursor with text selected between
1541 """ Convenience method that returns a cursor with text selected between
1528 the positions 'start' and 'end'.
1542 the positions 'start' and 'end'.
1529 """
1543 """
1530 cursor = self._control.textCursor()
1544 cursor = self._control.textCursor()
1531 cursor.setPosition(start)
1545 cursor.setPosition(start)
1532 cursor.setPosition(end, QtGui.QTextCursor.KeepAnchor)
1546 cursor.setPosition(end, QtGui.QTextCursor.KeepAnchor)
1533 return cursor
1547 return cursor
1534
1548
1535 def _get_word_start_cursor(self, position):
1549 def _get_word_start_cursor(self, position):
1536 """ Find the start of the word to the left the given position. If a
1550 """ Find the start of the word to the left the given position. If a
1537 sequence of non-word characters precedes the first word, skip over
1551 sequence of non-word characters precedes the first word, skip over
1538 them. (This emulates the behavior of bash, emacs, etc.)
1552 them. (This emulates the behavior of bash, emacs, etc.)
1539 """
1553 """
1540 document = self._control.document()
1554 document = self._control.document()
1541 position -= 1
1555 position -= 1
1542 while position >= self._prompt_pos and \
1556 while position >= self._prompt_pos and \
1543 not is_letter_or_number(document.characterAt(position)):
1557 not is_letter_or_number(document.characterAt(position)):
1544 position -= 1
1558 position -= 1
1545 while position >= self._prompt_pos and \
1559 while position >= self._prompt_pos and \
1546 is_letter_or_number(document.characterAt(position)):
1560 is_letter_or_number(document.characterAt(position)):
1547 position -= 1
1561 position -= 1
1548 cursor = self._control.textCursor()
1562 cursor = self._control.textCursor()
1549 cursor.setPosition(position + 1)
1563 cursor.setPosition(position + 1)
1550 return cursor
1564 return cursor
1551
1565
1552 def _get_word_end_cursor(self, position):
1566 def _get_word_end_cursor(self, position):
1553 """ Find the end of the word to the right the given position. If a
1567 """ Find the end of the word to the right the given position. If a
1554 sequence of non-word characters precedes the first word, skip over
1568 sequence of non-word characters precedes the first word, skip over
1555 them. (This emulates the behavior of bash, emacs, etc.)
1569 them. (This emulates the behavior of bash, emacs, etc.)
1556 """
1570 """
1557 document = self._control.document()
1571 document = self._control.document()
1558 end = self._get_end_cursor().position()
1572 end = self._get_end_cursor().position()
1559 while position < end and \
1573 while position < end and \
1560 not is_letter_or_number(document.characterAt(position)):
1574 not is_letter_or_number(document.characterAt(position)):
1561 position += 1
1575 position += 1
1562 while position < end and \
1576 while position < end and \
1563 is_letter_or_number(document.characterAt(position)):
1577 is_letter_or_number(document.characterAt(position)):
1564 position += 1
1578 position += 1
1565 cursor = self._control.textCursor()
1579 cursor = self._control.textCursor()
1566 cursor.setPosition(position)
1580 cursor.setPosition(position)
1567 return cursor
1581 return cursor
1568
1582
1569 def _insert_continuation_prompt(self, cursor):
1583 def _insert_continuation_prompt(self, cursor):
1570 """ Inserts new continuation prompt using the specified cursor.
1584 """ Inserts new continuation prompt using the specified cursor.
1571 """
1585 """
1572 if self._continuation_prompt_html is None:
1586 if self._continuation_prompt_html is None:
1573 self._insert_plain_text(cursor, self._continuation_prompt)
1587 self._insert_plain_text(cursor, self._continuation_prompt)
1574 else:
1588 else:
1575 self._continuation_prompt = self._insert_html_fetching_plain_text(
1589 self._continuation_prompt = self._insert_html_fetching_plain_text(
1576 cursor, self._continuation_prompt_html)
1590 cursor, self._continuation_prompt_html)
1577
1591
1578 def _insert_block(self, cursor, block_format=None):
1592 def _insert_block(self, cursor, block_format=None):
1579 """ Inserts an empty QTextBlock using the specified cursor.
1593 """ Inserts an empty QTextBlock using the specified cursor.
1580 """
1594 """
1581 if block_format is None:
1595 if block_format is None:
1582 block_format = QtGui.QTextBlockFormat()
1596 block_format = QtGui.QTextBlockFormat()
1583 cursor.insertBlock(block_format)
1597 cursor.insertBlock(block_format)
1584
1598
1585 def _insert_html(self, cursor, html):
1599 def _insert_html(self, cursor, html):
1586 """ Inserts HTML using the specified cursor in such a way that future
1600 """ Inserts HTML using the specified cursor in such a way that future
1587 formatting is unaffected.
1601 formatting is unaffected.
1588 """
1602 """
1589 cursor.beginEditBlock()
1603 cursor.beginEditBlock()
1590 cursor.insertHtml(html)
1604 cursor.insertHtml(html)
1591
1605
1592 # After inserting HTML, the text document "remembers" it's in "html
1606 # After inserting HTML, the text document "remembers" it's in "html
1593 # mode", which means that subsequent calls adding plain text will result
1607 # mode", which means that subsequent calls adding plain text will result
1594 # in unwanted formatting, lost tab characters, etc. The following code
1608 # in unwanted formatting, lost tab characters, etc. The following code
1595 # hacks around this behavior, which I consider to be a bug in Qt, by
1609 # hacks around this behavior, which I consider to be a bug in Qt, by
1596 # (crudely) resetting the document's style state.
1610 # (crudely) resetting the document's style state.
1597 cursor.movePosition(QtGui.QTextCursor.Left,
1611 cursor.movePosition(QtGui.QTextCursor.Left,
1598 QtGui.QTextCursor.KeepAnchor)
1612 QtGui.QTextCursor.KeepAnchor)
1599 if cursor.selection().toPlainText() == ' ':
1613 if cursor.selection().toPlainText() == ' ':
1600 cursor.removeSelectedText()
1614 cursor.removeSelectedText()
1601 else:
1615 else:
1602 cursor.movePosition(QtGui.QTextCursor.Right)
1616 cursor.movePosition(QtGui.QTextCursor.Right)
1603 cursor.insertText(' ', QtGui.QTextCharFormat())
1617 cursor.insertText(' ', QtGui.QTextCharFormat())
1604 cursor.endEditBlock()
1618 cursor.endEditBlock()
1605
1619
1606 def _insert_html_fetching_plain_text(self, cursor, html):
1620 def _insert_html_fetching_plain_text(self, cursor, html):
1607 """ Inserts HTML using the specified cursor, then returns its plain text
1621 """ Inserts HTML using the specified cursor, then returns its plain text
1608 version.
1622 version.
1609 """
1623 """
1610 cursor.beginEditBlock()
1624 cursor.beginEditBlock()
1611 cursor.removeSelectedText()
1625 cursor.removeSelectedText()
1612
1626
1613 start = cursor.position()
1627 start = cursor.position()
1614 self._insert_html(cursor, html)
1628 self._insert_html(cursor, html)
1615 end = cursor.position()
1629 end = cursor.position()
1616 cursor.setPosition(start, QtGui.QTextCursor.KeepAnchor)
1630 cursor.setPosition(start, QtGui.QTextCursor.KeepAnchor)
1617 text = cursor.selection().toPlainText()
1631 text = cursor.selection().toPlainText()
1618
1632
1619 cursor.setPosition(end)
1633 cursor.setPosition(end)
1620 cursor.endEditBlock()
1634 cursor.endEditBlock()
1621 return text
1635 return text
1622
1636
1623 def _insert_plain_text(self, cursor, text):
1637 def _insert_plain_text(self, cursor, text):
1624 """ Inserts plain text using the specified cursor, processing ANSI codes
1638 """ Inserts plain text using the specified cursor, processing ANSI codes
1625 if enabled.
1639 if enabled.
1626 """
1640 """
1627 cursor.beginEditBlock()
1641 cursor.beginEditBlock()
1628 if self.ansi_codes:
1642 if self.ansi_codes:
1629 for substring in self._ansi_processor.split_string(text):
1643 for substring in self._ansi_processor.split_string(text):
1630 for act in self._ansi_processor.actions:
1644 for act in self._ansi_processor.actions:
1631
1645
1632 # Unlike real terminal emulators, we don't distinguish
1646 # Unlike real terminal emulators, we don't distinguish
1633 # between the screen and the scrollback buffer. A screen
1647 # between the screen and the scrollback buffer. A screen
1634 # erase request clears everything.
1648 # erase request clears everything.
1635 if act.action == 'erase' and act.area == 'screen':
1649 if act.action == 'erase' and act.area == 'screen':
1636 cursor.select(QtGui.QTextCursor.Document)
1650 cursor.select(QtGui.QTextCursor.Document)
1637 cursor.removeSelectedText()
1651 cursor.removeSelectedText()
1638
1652
1639 # Simulate a form feed by scrolling just past the last line.
1653 # Simulate a form feed by scrolling just past the last line.
1640 elif act.action == 'scroll' and act.unit == 'page':
1654 elif act.action == 'scroll' and act.unit == 'page':
1641 cursor.insertText('\n')
1655 cursor.insertText('\n')
1642 cursor.endEditBlock()
1656 cursor.endEditBlock()
1643 self._set_top_cursor(cursor)
1657 self._set_top_cursor(cursor)
1644 cursor.joinPreviousEditBlock()
1658 cursor.joinPreviousEditBlock()
1645 cursor.deletePreviousChar()
1659 cursor.deletePreviousChar()
1646
1660
1647 elif act.action == 'carriage-return':
1661 elif act.action == 'carriage-return':
1648 cursor.movePosition(
1662 cursor.movePosition(
1649 cursor.StartOfLine, cursor.KeepAnchor)
1663 cursor.StartOfLine, cursor.KeepAnchor)
1650
1664
1651 elif act.action == 'beep':
1665 elif act.action == 'beep':
1652 QtGui.qApp.beep()
1666 QtGui.qApp.beep()
1653
1667
1654 elif act.action == 'backspace':
1668 elif act.action == 'backspace':
1655 if not cursor.atBlockStart():
1669 if not cursor.atBlockStart():
1656 cursor.movePosition(
1670 cursor.movePosition(
1657 cursor.PreviousCharacter, cursor.KeepAnchor)
1671 cursor.PreviousCharacter, cursor.KeepAnchor)
1658
1672
1659 elif act.action == 'newline':
1673 elif act.action == 'newline':
1660 cursor.movePosition(cursor.EndOfLine)
1674 cursor.movePosition(cursor.EndOfLine)
1661
1675
1662 format = self._ansi_processor.get_format()
1676 format = self._ansi_processor.get_format()
1663
1677
1664 selection = cursor.selectedText()
1678 selection = cursor.selectedText()
1665 if len(selection) == 0:
1679 if len(selection) == 0:
1666 cursor.insertText(substring, format)
1680 cursor.insertText(substring, format)
1667 elif substring is not None:
1681 elif substring is not None:
1668 # BS and CR are treated as a change in print
1682 # BS and CR are treated as a change in print
1669 # position, rather than a backwards character
1683 # position, rather than a backwards character
1670 # deletion for output equivalence with (I)Python
1684 # deletion for output equivalence with (I)Python
1671 # terminal.
1685 # terminal.
1672 if len(substring) >= len(selection):
1686 if len(substring) >= len(selection):
1673 cursor.insertText(substring, format)
1687 cursor.insertText(substring, format)
1674 else:
1688 else:
1675 old_text = selection[len(substring):]
1689 old_text = selection[len(substring):]
1676 cursor.insertText(substring + old_text, format)
1690 cursor.insertText(substring + old_text, format)
1677 cursor.movePosition(cursor.PreviousCharacter,
1691 cursor.movePosition(cursor.PreviousCharacter,
1678 cursor.KeepAnchor, len(old_text))
1692 cursor.KeepAnchor, len(old_text))
1679 else:
1693 else:
1680 cursor.insertText(text)
1694 cursor.insertText(text)
1681 cursor.endEditBlock()
1695 cursor.endEditBlock()
1682
1696
1683 def _insert_plain_text_into_buffer(self, cursor, text):
1697 def _insert_plain_text_into_buffer(self, cursor, text):
1684 """ Inserts text into the input buffer using the specified cursor (which
1698 """ Inserts text into the input buffer using the specified cursor (which
1685 must be in the input buffer), ensuring that continuation prompts are
1699 must be in the input buffer), ensuring that continuation prompts are
1686 inserted as necessary.
1700 inserted as necessary.
1687 """
1701 """
1688 lines = text.splitlines(True)
1702 lines = text.splitlines(True)
1689 if lines:
1703 if lines:
1690 cursor.beginEditBlock()
1704 cursor.beginEditBlock()
1691 cursor.insertText(lines[0])
1705 cursor.insertText(lines[0])
1692 for line in lines[1:]:
1706 for line in lines[1:]:
1693 if self._continuation_prompt_html is None:
1707 if self._continuation_prompt_html is None:
1694 cursor.insertText(self._continuation_prompt)
1708 cursor.insertText(self._continuation_prompt)
1695 else:
1709 else:
1696 self._continuation_prompt = \
1710 self._continuation_prompt = \
1697 self._insert_html_fetching_plain_text(
1711 self._insert_html_fetching_plain_text(
1698 cursor, self._continuation_prompt_html)
1712 cursor, self._continuation_prompt_html)
1699 cursor.insertText(line)
1713 cursor.insertText(line)
1700 cursor.endEditBlock()
1714 cursor.endEditBlock()
1701
1715
1702 def _in_buffer(self, position=None):
1716 def _in_buffer(self, position=None):
1703 """ Returns whether the current cursor (or, if specified, a position) is
1717 """ Returns whether the current cursor (or, if specified, a position) is
1704 inside the editing region.
1718 inside the editing region.
1705 """
1719 """
1706 cursor = self._control.textCursor()
1720 cursor = self._control.textCursor()
1707 if position is None:
1721 if position is None:
1708 position = cursor.position()
1722 position = cursor.position()
1709 else:
1723 else:
1710 cursor.setPosition(position)
1724 cursor.setPosition(position)
1711 line = cursor.blockNumber()
1725 line = cursor.blockNumber()
1712 prompt_line = self._get_prompt_cursor().blockNumber()
1726 prompt_line = self._get_prompt_cursor().blockNumber()
1713 if line == prompt_line:
1727 if line == prompt_line:
1714 return position >= self._prompt_pos
1728 return position >= self._prompt_pos
1715 elif line > prompt_line:
1729 elif line > prompt_line:
1716 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1730 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1717 prompt_pos = cursor.position() + len(self._continuation_prompt)
1731 prompt_pos = cursor.position() + len(self._continuation_prompt)
1718 return position >= prompt_pos
1732 return position >= prompt_pos
1719 return False
1733 return False
1720
1734
1721 def _keep_cursor_in_buffer(self):
1735 def _keep_cursor_in_buffer(self):
1722 """ Ensures that the cursor is inside the editing region. Returns
1736 """ Ensures that the cursor is inside the editing region. Returns
1723 whether the cursor was moved.
1737 whether the cursor was moved.
1724 """
1738 """
1725 moved = not self._in_buffer()
1739 moved = not self._in_buffer()
1726 if moved:
1740 if moved:
1727 cursor = self._control.textCursor()
1741 cursor = self._control.textCursor()
1728 cursor.movePosition(QtGui.QTextCursor.End)
1742 cursor.movePosition(QtGui.QTextCursor.End)
1729 self._control.setTextCursor(cursor)
1743 self._control.setTextCursor(cursor)
1730 return moved
1744 return moved
1731
1745
1732 def _keyboard_quit(self):
1746 def _keyboard_quit(self):
1733 """ Cancels the current editing task ala Ctrl-G in Emacs.
1747 """ Cancels the current editing task ala Ctrl-G in Emacs.
1734 """
1748 """
1735 if self._temp_buffer_filled :
1749 if self._temp_buffer_filled :
1736 self._cancel_completion()
1750 self._cancel_completion()
1737 self._clear_temporary_buffer()
1751 self._clear_temporary_buffer()
1738 else:
1752 else:
1739 self.input_buffer = ''
1753 self.input_buffer = ''
1740
1754
1741 def _page(self, text, html=False):
1755 def _page(self, text, html=False):
1742 """ Displays text using the pager if it exceeds the height of the
1756 """ Displays text using the pager if it exceeds the height of the
1743 viewport.
1757 viewport.
1744
1758
1745 Parameters:
1759 Parameters:
1746 -----------
1760 -----------
1747 html : bool, optional (default False)
1761 html : bool, optional (default False)
1748 If set, the text will be interpreted as HTML instead of plain text.
1762 If set, the text will be interpreted as HTML instead of plain text.
1749 """
1763 """
1750 line_height = QtGui.QFontMetrics(self.font).height()
1764 line_height = QtGui.QFontMetrics(self.font).height()
1751 minlines = self._control.viewport().height() / line_height
1765 minlines = self._control.viewport().height() / line_height
1752 if self.paging != 'none' and \
1766 if self.paging != 'none' and \
1753 re.match("(?:[^\n]*\n){%i}" % minlines, text):
1767 re.match("(?:[^\n]*\n){%i}" % minlines, text):
1754 if self.paging == 'custom':
1768 if self.paging == 'custom':
1755 self.custom_page_requested.emit(text)
1769 self.custom_page_requested.emit(text)
1756 else:
1770 else:
1757 self._page_control.clear()
1771 self._page_control.clear()
1758 cursor = self._page_control.textCursor()
1772 cursor = self._page_control.textCursor()
1759 if html:
1773 if html:
1760 self._insert_html(cursor, text)
1774 self._insert_html(cursor, text)
1761 else:
1775 else:
1762 self._insert_plain_text(cursor, text)
1776 self._insert_plain_text(cursor, text)
1763 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1777 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1764
1778
1765 self._page_control.viewport().resize(self._control.size())
1779 self._page_control.viewport().resize(self._control.size())
1766 if self._splitter:
1780 if self._splitter:
1767 self._page_control.show()
1781 self._page_control.show()
1768 self._page_control.setFocus()
1782 self._page_control.setFocus()
1769 else:
1783 else:
1770 self.layout().setCurrentWidget(self._page_control)
1784 self.layout().setCurrentWidget(self._page_control)
1771 elif html:
1785 elif html:
1772 self._append_html(text)
1786 self._append_html(text)
1773 else:
1787 else:
1774 self._append_plain_text(text)
1788 self._append_plain_text(text)
1775
1789
1790 def _set_paging(self, paging):
1791 """
1792 Change the pager to `paging` style.
1793
1794 XXX: currently, this is limited to switching between 'hsplit' and
1795 'vsplit'.
1796
1797 Parameters:
1798 -----------
1799 paging : string
1800 Either "hsplit", "vsplit", or "inside"
1801 """
1802 if self._splitter is None:
1803 raise NotImplementedError("""can only switch if --paging=hsplit or
1804 --paging=vsplit is used.""")
1805 if paging == 'hsplit':
1806 self._splitter.setOrientation(QtCore.Qt.Horizontal)
1807 elif paging == 'vsplit':
1808 self._splitter.setOrientation(QtCore.Qt.Vertical)
1809 elif paging == 'inside':
1810 raise NotImplementedError("""switching to 'inside' paging not
1811 supported yet.""")
1812 else:
1813 raise ValueError("unknown paging method '%s'" % paging)
1814 self.paging = paging
1815
1776 def _prompt_finished(self):
1816 def _prompt_finished(self):
1777 """ Called immediately after a prompt is finished, i.e. when some input
1817 """ Called immediately after a prompt is finished, i.e. when some input
1778 will be processed and a new prompt displayed.
1818 will be processed and a new prompt displayed.
1779 """
1819 """
1780 self._control.setReadOnly(True)
1820 self._control.setReadOnly(True)
1781 self._prompt_finished_hook()
1821 self._prompt_finished_hook()
1782
1822
1783 def _prompt_started(self):
1823 def _prompt_started(self):
1784 """ Called immediately after a new prompt is displayed.
1824 """ Called immediately after a new prompt is displayed.
1785 """
1825 """
1786 # Temporarily disable the maximum block count to permit undo/redo and
1826 # Temporarily disable the maximum block count to permit undo/redo and
1787 # to ensure that the prompt position does not change due to truncation.
1827 # to ensure that the prompt position does not change due to truncation.
1788 self._control.document().setMaximumBlockCount(0)
1828 self._control.document().setMaximumBlockCount(0)
1789 self._control.setUndoRedoEnabled(True)
1829 self._control.setUndoRedoEnabled(True)
1790
1830
1791 # Work around bug in QPlainTextEdit: input method is not re-enabled
1831 # Work around bug in QPlainTextEdit: input method is not re-enabled
1792 # when read-only is disabled.
1832 # when read-only is disabled.
1793 self._control.setReadOnly(False)
1833 self._control.setReadOnly(False)
1794 self._control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1834 self._control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1795
1835
1796 if not self._reading:
1836 if not self._reading:
1797 self._executing = False
1837 self._executing = False
1798 self._prompt_started_hook()
1838 self._prompt_started_hook()
1799
1839
1800 # If the input buffer has changed while executing, load it.
1840 # If the input buffer has changed while executing, load it.
1801 if self._input_buffer_pending:
1841 if self._input_buffer_pending:
1802 self.input_buffer = self._input_buffer_pending
1842 self.input_buffer = self._input_buffer_pending
1803 self._input_buffer_pending = ''
1843 self._input_buffer_pending = ''
1804
1844
1805 self._control.moveCursor(QtGui.QTextCursor.End)
1845 self._control.moveCursor(QtGui.QTextCursor.End)
1806
1846
1807 def _readline(self, prompt='', callback=None):
1847 def _readline(self, prompt='', callback=None):
1808 """ Reads one line of input from the user.
1848 """ Reads one line of input from the user.
1809
1849
1810 Parameters
1850 Parameters
1811 ----------
1851 ----------
1812 prompt : str, optional
1852 prompt : str, optional
1813 The prompt to print before reading the line.
1853 The prompt to print before reading the line.
1814
1854
1815 callback : callable, optional
1855 callback : callable, optional
1816 A callback to execute with the read line. If not specified, input is
1856 A callback to execute with the read line. If not specified, input is
1817 read *synchronously* and this method does not return until it has
1857 read *synchronously* and this method does not return until it has
1818 been read.
1858 been read.
1819
1859
1820 Returns
1860 Returns
1821 -------
1861 -------
1822 If a callback is specified, returns nothing. Otherwise, returns the
1862 If a callback is specified, returns nothing. Otherwise, returns the
1823 input string with the trailing newline stripped.
1863 input string with the trailing newline stripped.
1824 """
1864 """
1825 if self._reading:
1865 if self._reading:
1826 raise RuntimeError('Cannot read a line. Widget is already reading.')
1866 raise RuntimeError('Cannot read a line. Widget is already reading.')
1827
1867
1828 if not callback and not self.isVisible():
1868 if not callback and not self.isVisible():
1829 # If the user cannot see the widget, this function cannot return.
1869 # If the user cannot see the widget, this function cannot return.
1830 raise RuntimeError('Cannot synchronously read a line if the widget '
1870 raise RuntimeError('Cannot synchronously read a line if the widget '
1831 'is not visible!')
1871 'is not visible!')
1832
1872
1833 self._reading = True
1873 self._reading = True
1834 self._show_prompt(prompt, newline=False)
1874 self._show_prompt(prompt, newline=False)
1835
1875
1836 if callback is None:
1876 if callback is None:
1837 self._reading_callback = None
1877 self._reading_callback = None
1838 while self._reading:
1878 while self._reading:
1839 QtCore.QCoreApplication.processEvents()
1879 QtCore.QCoreApplication.processEvents()
1840 return self._get_input_buffer(force=True).rstrip('\n')
1880 return self._get_input_buffer(force=True).rstrip('\n')
1841
1881
1842 else:
1882 else:
1843 self._reading_callback = lambda: \
1883 self._reading_callback = lambda: \
1844 callback(self._get_input_buffer(force=True).rstrip('\n'))
1884 callback(self._get_input_buffer(force=True).rstrip('\n'))
1845
1885
1846 def _set_continuation_prompt(self, prompt, html=False):
1886 def _set_continuation_prompt(self, prompt, html=False):
1847 """ Sets the continuation prompt.
1887 """ Sets the continuation prompt.
1848
1888
1849 Parameters
1889 Parameters
1850 ----------
1890 ----------
1851 prompt : str
1891 prompt : str
1852 The prompt to show when more input is needed.
1892 The prompt to show when more input is needed.
1853
1893
1854 html : bool, optional (default False)
1894 html : bool, optional (default False)
1855 If set, the prompt will be inserted as formatted HTML. Otherwise,
1895 If set, the prompt will be inserted as formatted HTML. Otherwise,
1856 the prompt will be treated as plain text, though ANSI color codes
1896 the prompt will be treated as plain text, though ANSI color codes
1857 will be handled.
1897 will be handled.
1858 """
1898 """
1859 if html:
1899 if html:
1860 self._continuation_prompt_html = prompt
1900 self._continuation_prompt_html = prompt
1861 else:
1901 else:
1862 self._continuation_prompt = prompt
1902 self._continuation_prompt = prompt
1863 self._continuation_prompt_html = None
1903 self._continuation_prompt_html = None
1864
1904
1865 def _set_cursor(self, cursor):
1905 def _set_cursor(self, cursor):
1866 """ Convenience method to set the current cursor.
1906 """ Convenience method to set the current cursor.
1867 """
1907 """
1868 self._control.setTextCursor(cursor)
1908 self._control.setTextCursor(cursor)
1869
1909
1870 def _set_top_cursor(self, cursor):
1910 def _set_top_cursor(self, cursor):
1871 """ Scrolls the viewport so that the specified cursor is at the top.
1911 """ Scrolls the viewport so that the specified cursor is at the top.
1872 """
1912 """
1873 scrollbar = self._control.verticalScrollBar()
1913 scrollbar = self._control.verticalScrollBar()
1874 scrollbar.setValue(scrollbar.maximum())
1914 scrollbar.setValue(scrollbar.maximum())
1875 original_cursor = self._control.textCursor()
1915 original_cursor = self._control.textCursor()
1876 self._control.setTextCursor(cursor)
1916 self._control.setTextCursor(cursor)
1877 self._control.ensureCursorVisible()
1917 self._control.ensureCursorVisible()
1878 self._control.setTextCursor(original_cursor)
1918 self._control.setTextCursor(original_cursor)
1879
1919
1880 def _show_prompt(self, prompt=None, html=False, newline=True):
1920 def _show_prompt(self, prompt=None, html=False, newline=True):
1881 """ Writes a new prompt at the end of the buffer.
1921 """ Writes a new prompt at the end of the buffer.
1882
1922
1883 Parameters
1923 Parameters
1884 ----------
1924 ----------
1885 prompt : str, optional
1925 prompt : str, optional
1886 The prompt to show. If not specified, the previous prompt is used.
1926 The prompt to show. If not specified, the previous prompt is used.
1887
1927
1888 html : bool, optional (default False)
1928 html : bool, optional (default False)
1889 Only relevant when a prompt is specified. If set, the prompt will
1929 Only relevant when a prompt is specified. If set, the prompt will
1890 be inserted as formatted HTML. Otherwise, the prompt will be treated
1930 be inserted as formatted HTML. Otherwise, the prompt will be treated
1891 as plain text, though ANSI color codes will be handled.
1931 as plain text, though ANSI color codes will be handled.
1892
1932
1893 newline : bool, optional (default True)
1933 newline : bool, optional (default True)
1894 If set, a new line will be written before showing the prompt if
1934 If set, a new line will be written before showing the prompt if
1895 there is not already a newline at the end of the buffer.
1935 there is not already a newline at the end of the buffer.
1896 """
1936 """
1897 # Save the current end position to support _append*(before_prompt=True).
1937 # Save the current end position to support _append*(before_prompt=True).
1898 cursor = self._get_end_cursor()
1938 cursor = self._get_end_cursor()
1899 self._append_before_prompt_pos = cursor.position()
1939 self._append_before_prompt_pos = cursor.position()
1900
1940
1901 # Insert a preliminary newline, if necessary.
1941 # Insert a preliminary newline, if necessary.
1902 if newline and cursor.position() > 0:
1942 if newline and cursor.position() > 0:
1903 cursor.movePosition(QtGui.QTextCursor.Left,
1943 cursor.movePosition(QtGui.QTextCursor.Left,
1904 QtGui.QTextCursor.KeepAnchor)
1944 QtGui.QTextCursor.KeepAnchor)
1905 if cursor.selection().toPlainText() != '\n':
1945 if cursor.selection().toPlainText() != '\n':
1906 self._append_block()
1946 self._append_block()
1907
1947
1908 # Write the prompt.
1948 # Write the prompt.
1909 self._append_plain_text(self._prompt_sep)
1949 self._append_plain_text(self._prompt_sep)
1910 if prompt is None:
1950 if prompt is None:
1911 if self._prompt_html is None:
1951 if self._prompt_html is None:
1912 self._append_plain_text(self._prompt)
1952 self._append_plain_text(self._prompt)
1913 else:
1953 else:
1914 self._append_html(self._prompt_html)
1954 self._append_html(self._prompt_html)
1915 else:
1955 else:
1916 if html:
1956 if html:
1917 self._prompt = self._append_html_fetching_plain_text(prompt)
1957 self._prompt = self._append_html_fetching_plain_text(prompt)
1918 self._prompt_html = prompt
1958 self._prompt_html = prompt
1919 else:
1959 else:
1920 self._append_plain_text(prompt)
1960 self._append_plain_text(prompt)
1921 self._prompt = prompt
1961 self._prompt = prompt
1922 self._prompt_html = None
1962 self._prompt_html = None
1923
1963
1924 self._prompt_pos = self._get_end_cursor().position()
1964 self._prompt_pos = self._get_end_cursor().position()
1925 self._prompt_started()
1965 self._prompt_started()
1926
1966
1927 #------ Signal handlers ----------------------------------------------------
1967 #------ Signal handlers ----------------------------------------------------
1928
1968
1929 def _adjust_scrollbars(self):
1969 def _adjust_scrollbars(self):
1930 """ Expands the vertical scrollbar beyond the range set by Qt.
1970 """ Expands the vertical scrollbar beyond the range set by Qt.
1931 """
1971 """
1932 # This code is adapted from _q_adjustScrollbars in qplaintextedit.cpp
1972 # This code is adapted from _q_adjustScrollbars in qplaintextedit.cpp
1933 # and qtextedit.cpp.
1973 # and qtextedit.cpp.
1934 document = self._control.document()
1974 document = self._control.document()
1935 scrollbar = self._control.verticalScrollBar()
1975 scrollbar = self._control.verticalScrollBar()
1936 viewport_height = self._control.viewport().height()
1976 viewport_height = self._control.viewport().height()
1937 if isinstance(self._control, QtGui.QPlainTextEdit):
1977 if isinstance(self._control, QtGui.QPlainTextEdit):
1938 maximum = max(0, document.lineCount() - 1)
1978 maximum = max(0, document.lineCount() - 1)
1939 step = viewport_height / self._control.fontMetrics().lineSpacing()
1979 step = viewport_height / self._control.fontMetrics().lineSpacing()
1940 else:
1980 else:
1941 # QTextEdit does not do line-based layout and blocks will not in
1981 # QTextEdit does not do line-based layout and blocks will not in
1942 # general have the same height. Therefore it does not make sense to
1982 # general have the same height. Therefore it does not make sense to
1943 # attempt to scroll in line height increments.
1983 # attempt to scroll in line height increments.
1944 maximum = document.size().height()
1984 maximum = document.size().height()
1945 step = viewport_height
1985 step = viewport_height
1946 diff = maximum - scrollbar.maximum()
1986 diff = maximum - scrollbar.maximum()
1947 scrollbar.setRange(0, maximum)
1987 scrollbar.setRange(0, maximum)
1948 scrollbar.setPageStep(step)
1988 scrollbar.setPageStep(step)
1949
1989
1950 # Compensate for undesirable scrolling that occurs automatically due to
1990 # Compensate for undesirable scrolling that occurs automatically due to
1951 # maximumBlockCount() text truncation.
1991 # maximumBlockCount() text truncation.
1952 if diff < 0 and document.blockCount() == document.maximumBlockCount():
1992 if diff < 0 and document.blockCount() == document.maximumBlockCount():
1953 scrollbar.setValue(scrollbar.value() + diff)
1993 scrollbar.setValue(scrollbar.value() + diff)
1954
1994
1955 def _custom_context_menu_requested(self, pos):
1995 def _custom_context_menu_requested(self, pos):
1956 """ Shows a context menu at the given QPoint (in widget coordinates).
1996 """ Shows a context menu at the given QPoint (in widget coordinates).
1957 """
1997 """
1958 menu = self._context_menu_make(pos)
1998 menu = self._context_menu_make(pos)
1959 menu.exec_(self._control.mapToGlobal(pos))
1999 menu.exec_(self._control.mapToGlobal(pos))
@@ -1,969 +1,990 b''
1 """The Qt MainWindow for the QtConsole
1 """The Qt MainWindow for the QtConsole
2
2
3 This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
3 This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
4 common actions.
4 common actions.
5
5
6 Authors:
6 Authors:
7
7
8 * Evan Patterson
8 * Evan Patterson
9 * Min RK
9 * Min RK
10 * Erik Tollerud
10 * Erik Tollerud
11 * Fernando Perez
11 * Fernando Perez
12 * Bussonnier Matthias
12 * Bussonnier Matthias
13 * Thomas Kluyver
13 * Thomas Kluyver
14 * Paul Ivanov
14 * Paul Ivanov
15
15
16 """
16 """
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # stdlib imports
22 # stdlib imports
23 import sys
23 import sys
24 import re
24 import re
25 import webbrowser
25 import webbrowser
26 import ast
26 import ast
27 from threading import Thread
27 from threading import Thread
28
28
29 # System library imports
29 # System library imports
30 from IPython.external.qt import QtGui,QtCore
30 from IPython.external.qt import QtGui,QtCore
31
31
32 def background(f):
32 def background(f):
33 """call a function in a simple thread, to prevent blocking"""
33 """call a function in a simple thread, to prevent blocking"""
34 t = Thread(target=f)
34 t = Thread(target=f)
35 t.start()
35 t.start()
36 return t
36 return t
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Classes
39 # Classes
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 class MainWindow(QtGui.QMainWindow):
42 class MainWindow(QtGui.QMainWindow):
43
43
44 #---------------------------------------------------------------------------
44 #---------------------------------------------------------------------------
45 # 'object' interface
45 # 'object' interface
46 #---------------------------------------------------------------------------
46 #---------------------------------------------------------------------------
47
47
48 _magic_menu_dict = {}
48 _magic_menu_dict = {}
49
49
50 def __init__(self, app,
50 def __init__(self, app,
51 confirm_exit=True,
51 confirm_exit=True,
52 new_frontend_factory=None, slave_frontend_factory=None,
52 new_frontend_factory=None, slave_frontend_factory=None,
53 ):
53 ):
54 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
54 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
55
55
56 Parameters
56 Parameters
57 ----------
57 ----------
58
58
59 app : reference to QApplication parent
59 app : reference to QApplication parent
60 confirm_exit : bool, optional
60 confirm_exit : bool, optional
61 Whether we should prompt on close of tabs
61 Whether we should prompt on close of tabs
62 new_frontend_factory : callable
62 new_frontend_factory : callable
63 A callable that returns a new IPythonWidget instance, attached to
63 A callable that returns a new IPythonWidget instance, attached to
64 its own running kernel.
64 its own running kernel.
65 slave_frontend_factory : callable
65 slave_frontend_factory : callable
66 A callable that takes an existing IPythonWidget, and returns a new
66 A callable that takes an existing IPythonWidget, and returns a new
67 IPythonWidget instance, attached to the same kernel.
67 IPythonWidget instance, attached to the same kernel.
68 """
68 """
69
69
70 super(MainWindow, self).__init__()
70 super(MainWindow, self).__init__()
71 self._kernel_counter = 0
71 self._kernel_counter = 0
72 self._app = app
72 self._app = app
73 self.confirm_exit = confirm_exit
73 self.confirm_exit = confirm_exit
74 self.new_frontend_factory = new_frontend_factory
74 self.new_frontend_factory = new_frontend_factory
75 self.slave_frontend_factory = slave_frontend_factory
75 self.slave_frontend_factory = slave_frontend_factory
76
76
77 self.tab_widget = QtGui.QTabWidget(self)
77 self.tab_widget = QtGui.QTabWidget(self)
78 self.tab_widget.setDocumentMode(True)
78 self.tab_widget.setDocumentMode(True)
79 self.tab_widget.setTabsClosable(True)
79 self.tab_widget.setTabsClosable(True)
80 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
80 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
81
81
82 self.setCentralWidget(self.tab_widget)
82 self.setCentralWidget(self.tab_widget)
83 # hide tab bar at first, since we have no tabs:
83 # hide tab bar at first, since we have no tabs:
84 self.tab_widget.tabBar().setVisible(False)
84 self.tab_widget.tabBar().setVisible(False)
85 # prevent focus in tab bar
85 # prevent focus in tab bar
86 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
86 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
87
87
88 def update_tab_bar_visibility(self):
88 def update_tab_bar_visibility(self):
89 """ update visibility of the tabBar depending of the number of tab
89 """ update visibility of the tabBar depending of the number of tab
90
90
91 0 or 1 tab, tabBar hidden
91 0 or 1 tab, tabBar hidden
92 2+ tabs, tabBar visible
92 2+ tabs, tabBar visible
93
93
94 send a self.close if number of tab ==0
94 send a self.close if number of tab ==0
95
95
96 need to be called explicitly, or be connected to tabInserted/tabRemoved
96 need to be called explicitly, or be connected to tabInserted/tabRemoved
97 """
97 """
98 if self.tab_widget.count() <= 1:
98 if self.tab_widget.count() <= 1:
99 self.tab_widget.tabBar().setVisible(False)
99 self.tab_widget.tabBar().setVisible(False)
100 else:
100 else:
101 self.tab_widget.tabBar().setVisible(True)
101 self.tab_widget.tabBar().setVisible(True)
102 if self.tab_widget.count()==0 :
102 if self.tab_widget.count()==0 :
103 self.close()
103 self.close()
104
104
105 @property
105 @property
106 def next_kernel_id(self):
106 def next_kernel_id(self):
107 """constantly increasing counter for kernel IDs"""
107 """constantly increasing counter for kernel IDs"""
108 c = self._kernel_counter
108 c = self._kernel_counter
109 self._kernel_counter += 1
109 self._kernel_counter += 1
110 return c
110 return c
111
111
112 @property
112 @property
113 def active_frontend(self):
113 def active_frontend(self):
114 return self.tab_widget.currentWidget()
114 return self.tab_widget.currentWidget()
115
115
116 def create_tab_with_new_frontend(self):
116 def create_tab_with_new_frontend(self):
117 """create a new frontend and attach it to a new tab"""
117 """create a new frontend and attach it to a new tab"""
118 widget = self.new_frontend_factory()
118 widget = self.new_frontend_factory()
119 self.add_tab_with_frontend(widget)
119 self.add_tab_with_frontend(widget)
120
120
121 def create_tab_with_current_kernel(self):
121 def create_tab_with_current_kernel(self):
122 """create a new frontend attached to the same kernel as the current tab"""
122 """create a new frontend attached to the same kernel as the current tab"""
123 current_widget = self.tab_widget.currentWidget()
123 current_widget = self.tab_widget.currentWidget()
124 current_widget_index = self.tab_widget.indexOf(current_widget)
124 current_widget_index = self.tab_widget.indexOf(current_widget)
125 current_widget_name = self.tab_widget.tabText(current_widget_index)
125 current_widget_name = self.tab_widget.tabText(current_widget_index)
126 widget = self.slave_frontend_factory(current_widget)
126 widget = self.slave_frontend_factory(current_widget)
127 if 'slave' in current_widget_name:
127 if 'slave' in current_widget_name:
128 # don't keep stacking slaves
128 # don't keep stacking slaves
129 name = current_widget_name
129 name = current_widget_name
130 else:
130 else:
131 name = '(%s) slave' % current_widget_name
131 name = '(%s) slave' % current_widget_name
132 self.add_tab_with_frontend(widget,name=name)
132 self.add_tab_with_frontend(widget,name=name)
133
133
134 def close_tab(self,current_tab):
134 def close_tab(self,current_tab):
135 """ Called when you need to try to close a tab.
135 """ Called when you need to try to close a tab.
136
136
137 It takes the number of the tab to be closed as argument, or a reference
137 It takes the number of the tab to be closed as argument, or a reference
138 to the widget inside this tab
138 to the widget inside this tab
139 """
139 """
140
140
141 # let's be sure "tab" and "closing widget" are respectively the index
141 # let's be sure "tab" and "closing widget" are respectively the index
142 # of the tab to close and a reference to the frontend to close
142 # of the tab to close and a reference to the frontend to close
143 if type(current_tab) is not int :
143 if type(current_tab) is not int :
144 current_tab = self.tab_widget.indexOf(current_tab)
144 current_tab = self.tab_widget.indexOf(current_tab)
145 closing_widget=self.tab_widget.widget(current_tab)
145 closing_widget=self.tab_widget.widget(current_tab)
146
146
147
147
148 # when trying to be closed, widget might re-send a request to be
148 # when trying to be closed, widget might re-send a request to be
149 # closed again, but will be deleted when event will be processed. So
149 # closed again, but will be deleted when event will be processed. So
150 # need to check that widget still exists and skip if not. One example
150 # need to check that widget still exists and skip if not. One example
151 # of this is when 'exit' is sent in a slave tab. 'exit' will be
151 # of this is when 'exit' is sent in a slave tab. 'exit' will be
152 # re-sent by this function on the master widget, which ask all slave
152 # re-sent by this function on the master widget, which ask all slave
153 # widgets to exit
153 # widgets to exit
154 if closing_widget==None:
154 if closing_widget==None:
155 return
155 return
156
156
157 #get a list of all slave widgets on the same kernel.
157 #get a list of all slave widgets on the same kernel.
158 slave_tabs = self.find_slave_widgets(closing_widget)
158 slave_tabs = self.find_slave_widgets(closing_widget)
159
159
160 keepkernel = None #Use the prompt by default
160 keepkernel = None #Use the prompt by default
161 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
161 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
162 keepkernel = closing_widget._keep_kernel_on_exit
162 keepkernel = closing_widget._keep_kernel_on_exit
163 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
163 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
164 # we set local slave tabs._hidden to True to avoid prompting for kernel
164 # we set local slave tabs._hidden to True to avoid prompting for kernel
165 # restart when they get the signal. and then "forward" the 'exit'
165 # restart when they get the signal. and then "forward" the 'exit'
166 # to the main window
166 # to the main window
167 if keepkernel is not None:
167 if keepkernel is not None:
168 for tab in slave_tabs:
168 for tab in slave_tabs:
169 tab._hidden = True
169 tab._hidden = True
170 if closing_widget in slave_tabs:
170 if closing_widget in slave_tabs:
171 try :
171 try :
172 self.find_master_tab(closing_widget).execute('exit')
172 self.find_master_tab(closing_widget).execute('exit')
173 except AttributeError:
173 except AttributeError:
174 self.log.info("Master already closed or not local, closing only current tab")
174 self.log.info("Master already closed or not local, closing only current tab")
175 self.tab_widget.removeTab(current_tab)
175 self.tab_widget.removeTab(current_tab)
176 self.update_tab_bar_visibility()
176 self.update_tab_bar_visibility()
177 return
177 return
178
178
179 kernel_manager = closing_widget.kernel_manager
179 kernel_manager = closing_widget.kernel_manager
180
180
181 if keepkernel is None and not closing_widget._confirm_exit:
181 if keepkernel is None and not closing_widget._confirm_exit:
182 # don't prompt, just terminate the kernel if we own it
182 # don't prompt, just terminate the kernel if we own it
183 # or leave it alone if we don't
183 # or leave it alone if we don't
184 keepkernel = closing_widget._existing
184 keepkernel = closing_widget._existing
185 if keepkernel is None: #show prompt
185 if keepkernel is None: #show prompt
186 if kernel_manager and kernel_manager.channels_running:
186 if kernel_manager and kernel_manager.channels_running:
187 title = self.window().windowTitle()
187 title = self.window().windowTitle()
188 cancel = QtGui.QMessageBox.Cancel
188 cancel = QtGui.QMessageBox.Cancel
189 okay = QtGui.QMessageBox.Ok
189 okay = QtGui.QMessageBox.Ok
190 if closing_widget._may_close:
190 if closing_widget._may_close:
191 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
191 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
192 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
192 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
193 justthis = QtGui.QPushButton("&No, just this Tab", self)
193 justthis = QtGui.QPushButton("&No, just this Tab", self)
194 justthis.setShortcut('N')
194 justthis.setShortcut('N')
195 closeall = QtGui.QPushButton("&Yes, close all", self)
195 closeall = QtGui.QPushButton("&Yes, close all", self)
196 closeall.setShortcut('Y')
196 closeall.setShortcut('Y')
197 # allow ctrl-d ctrl-d exit, like in terminal
197 # allow ctrl-d ctrl-d exit, like in terminal
198 closeall.setShortcut('Ctrl+D')
198 closeall.setShortcut('Ctrl+D')
199 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
199 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
200 title, msg)
200 title, msg)
201 box.setInformativeText(info)
201 box.setInformativeText(info)
202 box.addButton(cancel)
202 box.addButton(cancel)
203 box.addButton(justthis, QtGui.QMessageBox.NoRole)
203 box.addButton(justthis, QtGui.QMessageBox.NoRole)
204 box.addButton(closeall, QtGui.QMessageBox.YesRole)
204 box.addButton(closeall, QtGui.QMessageBox.YesRole)
205 box.setDefaultButton(closeall)
205 box.setDefaultButton(closeall)
206 box.setEscapeButton(cancel)
206 box.setEscapeButton(cancel)
207 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
207 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
208 box.setIconPixmap(pixmap)
208 box.setIconPixmap(pixmap)
209 reply = box.exec_()
209 reply = box.exec_()
210 if reply == 1: # close All
210 if reply == 1: # close All
211 for slave in slave_tabs:
211 for slave in slave_tabs:
212 background(slave.kernel_manager.stop_channels)
212 background(slave.kernel_manager.stop_channels)
213 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
213 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
214 closing_widget.execute("exit")
214 closing_widget.execute("exit")
215 self.tab_widget.removeTab(current_tab)
215 self.tab_widget.removeTab(current_tab)
216 background(kernel_manager.stop_channels)
216 background(kernel_manager.stop_channels)
217 elif reply == 0: # close Console
217 elif reply == 0: # close Console
218 if not closing_widget._existing:
218 if not closing_widget._existing:
219 # Have kernel: don't quit, just close the tab
219 # Have kernel: don't quit, just close the tab
220 closing_widget.execute("exit True")
220 closing_widget.execute("exit True")
221 self.tab_widget.removeTab(current_tab)
221 self.tab_widget.removeTab(current_tab)
222 background(kernel_manager.stop_channels)
222 background(kernel_manager.stop_channels)
223 else:
223 else:
224 reply = QtGui.QMessageBox.question(self, title,
224 reply = QtGui.QMessageBox.question(self, title,
225 "Are you sure you want to close this Console?"+
225 "Are you sure you want to close this Console?"+
226 "\nThe Kernel and other Consoles will remain active.",
226 "\nThe Kernel and other Consoles will remain active.",
227 okay|cancel,
227 okay|cancel,
228 defaultButton=okay
228 defaultButton=okay
229 )
229 )
230 if reply == okay:
230 if reply == okay:
231 self.tab_widget.removeTab(current_tab)
231 self.tab_widget.removeTab(current_tab)
232 elif keepkernel: #close console but leave kernel running (no prompt)
232 elif keepkernel: #close console but leave kernel running (no prompt)
233 self.tab_widget.removeTab(current_tab)
233 self.tab_widget.removeTab(current_tab)
234 background(kernel_manager.stop_channels)
234 background(kernel_manager.stop_channels)
235 else: #close console and kernel (no prompt)
235 else: #close console and kernel (no prompt)
236 self.tab_widget.removeTab(current_tab)
236 self.tab_widget.removeTab(current_tab)
237 if kernel_manager and kernel_manager.channels_running:
237 if kernel_manager and kernel_manager.channels_running:
238 for slave in slave_tabs:
238 for slave in slave_tabs:
239 background(slave.kernel_manager.stop_channels)
239 background(slave.kernel_manager.stop_channels)
240 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
240 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
241 kernel_manager.shutdown_kernel()
241 kernel_manager.shutdown_kernel()
242 background(kernel_manager.stop_channels)
242 background(kernel_manager.stop_channels)
243
243
244 self.update_tab_bar_visibility()
244 self.update_tab_bar_visibility()
245
245
246 def add_tab_with_frontend(self,frontend,name=None):
246 def add_tab_with_frontend(self,frontend,name=None):
247 """ insert a tab with a given frontend in the tab bar, and give it a name
247 """ insert a tab with a given frontend in the tab bar, and give it a name
248
248
249 """
249 """
250 if not name:
250 if not name:
251 name = 'kernel %i' % self.next_kernel_id
251 name = 'kernel %i' % self.next_kernel_id
252 self.tab_widget.addTab(frontend,name)
252 self.tab_widget.addTab(frontend,name)
253 self.update_tab_bar_visibility()
253 self.update_tab_bar_visibility()
254 self.make_frontend_visible(frontend)
254 self.make_frontend_visible(frontend)
255 frontend.exit_requested.connect(self.close_tab)
255 frontend.exit_requested.connect(self.close_tab)
256
256
257 def next_tab(self):
257 def next_tab(self):
258 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
258 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
259
259
260 def prev_tab(self):
260 def prev_tab(self):
261 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
261 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
262
262
263 def make_frontend_visible(self,frontend):
263 def make_frontend_visible(self,frontend):
264 widget_index=self.tab_widget.indexOf(frontend)
264 widget_index=self.tab_widget.indexOf(frontend)
265 if widget_index > 0 :
265 if widget_index > 0 :
266 self.tab_widget.setCurrentIndex(widget_index)
266 self.tab_widget.setCurrentIndex(widget_index)
267
267
268 def find_master_tab(self,tab,as_list=False):
268 def find_master_tab(self,tab,as_list=False):
269 """
269 """
270 Try to return the frontend that owns the kernel attached to the given widget/tab.
270 Try to return the frontend that owns the kernel attached to the given widget/tab.
271
271
272 Only finds frontend owned by the current application. Selection
272 Only finds frontend owned by the current application. Selection
273 based on port of the kernel might be inaccurate if several kernel
273 based on port of the kernel might be inaccurate if several kernel
274 on different ip use same port number.
274 on different ip use same port number.
275
275
276 This function does the conversion tabNumber/widget if needed.
276 This function does the conversion tabNumber/widget if needed.
277 Might return None if no master widget (non local kernel)
277 Might return None if no master widget (non local kernel)
278 Will crash IPython if more than 1 masterWidget
278 Will crash IPython if more than 1 masterWidget
279
279
280 When asList set to True, always return a list of widget(s) owning
280 When asList set to True, always return a list of widget(s) owning
281 the kernel. The list might be empty or containing several Widget.
281 the kernel. The list might be empty or containing several Widget.
282 """
282 """
283
283
284 #convert from/to int/richIpythonWidget if needed
284 #convert from/to int/richIpythonWidget if needed
285 if isinstance(tab, int):
285 if isinstance(tab, int):
286 tab = self.tab_widget.widget(tab)
286 tab = self.tab_widget.widget(tab)
287 km=tab.kernel_manager
287 km=tab.kernel_manager
288
288
289 #build list of all widgets
289 #build list of all widgets
290 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
290 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
291
291
292 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
292 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
293 # And should have a _may_close attribute
293 # And should have a _may_close attribute
294 filtered_widget_list = [ widget for widget in widget_list if
294 filtered_widget_list = [ widget for widget in widget_list if
295 widget.kernel_manager.connection_file == km.connection_file and
295 widget.kernel_manager.connection_file == km.connection_file and
296 hasattr(widget,'_may_close') ]
296 hasattr(widget,'_may_close') ]
297 # the master widget is the one that may close the kernel
297 # the master widget is the one that may close the kernel
298 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
298 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
299 if as_list:
299 if as_list:
300 return master_widget
300 return master_widget
301 assert(len(master_widget)<=1 )
301 assert(len(master_widget)<=1 )
302 if len(master_widget)==0:
302 if len(master_widget)==0:
303 return None
303 return None
304
304
305 return master_widget[0]
305 return master_widget[0]
306
306
307 def find_slave_widgets(self,tab):
307 def find_slave_widgets(self,tab):
308 """return all the frontends that do not own the kernel attached to the given widget/tab.
308 """return all the frontends that do not own the kernel attached to the given widget/tab.
309
309
310 Only find frontends owned by the current application. Selection
310 Only find frontends owned by the current application. Selection
311 based on connection file of the kernel.
311 based on connection file of the kernel.
312
312
313 This function does the conversion tabNumber/widget if needed.
313 This function does the conversion tabNumber/widget if needed.
314 """
314 """
315 #convert from/to int/richIpythonWidget if needed
315 #convert from/to int/richIpythonWidget if needed
316 if isinstance(tab, int):
316 if isinstance(tab, int):
317 tab = self.tab_widget.widget(tab)
317 tab = self.tab_widget.widget(tab)
318 km=tab.kernel_manager
318 km=tab.kernel_manager
319
319
320 #build list of all widgets
320 #build list of all widgets
321 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
321 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
322
322
323 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
323 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
324 filtered_widget_list = ( widget for widget in widget_list if
324 filtered_widget_list = ( widget for widget in widget_list if
325 widget.kernel_manager.connection_file == km.connection_file)
325 widget.kernel_manager.connection_file == km.connection_file)
326 # Get a list of all widget owning the same kernel and removed it from
326 # Get a list of all widget owning the same kernel and removed it from
327 # the previous cadidate. (better using sets ?)
327 # the previous cadidate. (better using sets ?)
328 master_widget_list = self.find_master_tab(tab, as_list=True)
328 master_widget_list = self.find_master_tab(tab, as_list=True)
329 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
329 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
330
330
331 return slave_list
331 return slave_list
332
332
333 # Populate the menu bar with common actions and shortcuts
333 # Populate the menu bar with common actions and shortcuts
334 def add_menu_action(self, menu, action, defer_shortcut=False):
334 def add_menu_action(self, menu, action, defer_shortcut=False):
335 """Add action to menu as well as self
335 """Add action to menu as well as self
336
336
337 So that when the menu bar is invisible, its actions are still available.
337 So that when the menu bar is invisible, its actions are still available.
338
338
339 If defer_shortcut is True, set the shortcut context to widget-only,
339 If defer_shortcut is True, set the shortcut context to widget-only,
340 where it will avoid conflict with shortcuts already bound to the
340 where it will avoid conflict with shortcuts already bound to the
341 widgets themselves.
341 widgets themselves.
342 """
342 """
343 menu.addAction(action)
343 menu.addAction(action)
344 self.addAction(action)
344 self.addAction(action)
345
345
346 if defer_shortcut:
346 if defer_shortcut:
347 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
347 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
348
348
349 def init_menu_bar(self):
349 def init_menu_bar(self):
350 #create menu in the order they should appear in the menu bar
350 #create menu in the order they should appear in the menu bar
351 self.init_file_menu()
351 self.init_file_menu()
352 self.init_edit_menu()
352 self.init_edit_menu()
353 self.init_view_menu()
353 self.init_view_menu()
354 self.init_kernel_menu()
354 self.init_kernel_menu()
355 self.init_magic_menu()
355 self.init_magic_menu()
356 self.init_window_menu()
356 self.init_window_menu()
357 self.init_help_menu()
357 self.init_help_menu()
358
358
359 def init_file_menu(self):
359 def init_file_menu(self):
360 self.file_menu = self.menuBar().addMenu("&File")
360 self.file_menu = self.menuBar().addMenu("&File")
361
361
362 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
362 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
363 self,
363 self,
364 shortcut="Ctrl+T",
364 shortcut="Ctrl+T",
365 triggered=self.create_tab_with_new_frontend)
365 triggered=self.create_tab_with_new_frontend)
366 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
366 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
367
367
368 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
368 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
369 self,
369 self,
370 shortcut="Ctrl+Shift+T",
370 shortcut="Ctrl+Shift+T",
371 triggered=self.create_tab_with_current_kernel)
371 triggered=self.create_tab_with_current_kernel)
372 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
372 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
373
373
374 self.file_menu.addSeparator()
374 self.file_menu.addSeparator()
375
375
376 self.close_action=QtGui.QAction("&Close Tab",
376 self.close_action=QtGui.QAction("&Close Tab",
377 self,
377 self,
378 shortcut=QtGui.QKeySequence.Close,
378 shortcut=QtGui.QKeySequence.Close,
379 triggered=self.close_active_frontend
379 triggered=self.close_active_frontend
380 )
380 )
381 self.add_menu_action(self.file_menu, self.close_action)
381 self.add_menu_action(self.file_menu, self.close_action)
382
382
383 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
383 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
384 self,
384 self,
385 shortcut=QtGui.QKeySequence.Save,
385 shortcut=QtGui.QKeySequence.Save,
386 triggered=self.export_action_active_frontend
386 triggered=self.export_action_active_frontend
387 )
387 )
388 self.add_menu_action(self.file_menu, self.export_action, True)
388 self.add_menu_action(self.file_menu, self.export_action, True)
389
389
390 self.file_menu.addSeparator()
390 self.file_menu.addSeparator()
391
391
392 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
392 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
393 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
393 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
394 # Only override the default if there is a collision.
394 # Only override the default if there is a collision.
395 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
395 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
396 printkey = "Ctrl+Shift+P"
396 printkey = "Ctrl+Shift+P"
397 self.print_action = QtGui.QAction("&Print",
397 self.print_action = QtGui.QAction("&Print",
398 self,
398 self,
399 shortcut=printkey,
399 shortcut=printkey,
400 triggered=self.print_action_active_frontend)
400 triggered=self.print_action_active_frontend)
401 self.add_menu_action(self.file_menu, self.print_action, True)
401 self.add_menu_action(self.file_menu, self.print_action, True)
402
402
403 if sys.platform != 'darwin':
403 if sys.platform != 'darwin':
404 # OSX always has Quit in the Application menu, only add it
404 # OSX always has Quit in the Application menu, only add it
405 # to the File menu elsewhere.
405 # to the File menu elsewhere.
406
406
407 self.file_menu.addSeparator()
407 self.file_menu.addSeparator()
408
408
409 self.quit_action = QtGui.QAction("&Quit",
409 self.quit_action = QtGui.QAction("&Quit",
410 self,
410 self,
411 shortcut=QtGui.QKeySequence.Quit,
411 shortcut=QtGui.QKeySequence.Quit,
412 triggered=self.close,
412 triggered=self.close,
413 )
413 )
414 self.add_menu_action(self.file_menu, self.quit_action)
414 self.add_menu_action(self.file_menu, self.quit_action)
415
415
416
416
417 def init_edit_menu(self):
417 def init_edit_menu(self):
418 self.edit_menu = self.menuBar().addMenu("&Edit")
418 self.edit_menu = self.menuBar().addMenu("&Edit")
419
419
420 self.undo_action = QtGui.QAction("&Undo",
420 self.undo_action = QtGui.QAction("&Undo",
421 self,
421 self,
422 shortcut=QtGui.QKeySequence.Undo,
422 shortcut=QtGui.QKeySequence.Undo,
423 statusTip="Undo last action if possible",
423 statusTip="Undo last action if possible",
424 triggered=self.undo_active_frontend
424 triggered=self.undo_active_frontend
425 )
425 )
426 self.add_menu_action(self.edit_menu, self.undo_action)
426 self.add_menu_action(self.edit_menu, self.undo_action)
427
427
428 self.redo_action = QtGui.QAction("&Redo",
428 self.redo_action = QtGui.QAction("&Redo",
429 self,
429 self,
430 shortcut=QtGui.QKeySequence.Redo,
430 shortcut=QtGui.QKeySequence.Redo,
431 statusTip="Redo last action if possible",
431 statusTip="Redo last action if possible",
432 triggered=self.redo_active_frontend)
432 triggered=self.redo_active_frontend)
433 self.add_menu_action(self.edit_menu, self.redo_action)
433 self.add_menu_action(self.edit_menu, self.redo_action)
434
434
435 self.edit_menu.addSeparator()
435 self.edit_menu.addSeparator()
436
436
437 self.cut_action = QtGui.QAction("&Cut",
437 self.cut_action = QtGui.QAction("&Cut",
438 self,
438 self,
439 shortcut=QtGui.QKeySequence.Cut,
439 shortcut=QtGui.QKeySequence.Cut,
440 triggered=self.cut_active_frontend
440 triggered=self.cut_active_frontend
441 )
441 )
442 self.add_menu_action(self.edit_menu, self.cut_action, True)
442 self.add_menu_action(self.edit_menu, self.cut_action, True)
443
443
444 self.copy_action = QtGui.QAction("&Copy",
444 self.copy_action = QtGui.QAction("&Copy",
445 self,
445 self,
446 shortcut=QtGui.QKeySequence.Copy,
446 shortcut=QtGui.QKeySequence.Copy,
447 triggered=self.copy_active_frontend
447 triggered=self.copy_active_frontend
448 )
448 )
449 self.add_menu_action(self.edit_menu, self.copy_action, True)
449 self.add_menu_action(self.edit_menu, self.copy_action, True)
450
450
451 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
451 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
452 self,
452 self,
453 shortcut="Ctrl+Shift+C",
453 shortcut="Ctrl+Shift+C",
454 triggered=self.copy_raw_active_frontend
454 triggered=self.copy_raw_active_frontend
455 )
455 )
456 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
456 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
457
457
458 self.paste_action = QtGui.QAction("&Paste",
458 self.paste_action = QtGui.QAction("&Paste",
459 self,
459 self,
460 shortcut=QtGui.QKeySequence.Paste,
460 shortcut=QtGui.QKeySequence.Paste,
461 triggered=self.paste_active_frontend
461 triggered=self.paste_active_frontend
462 )
462 )
463 self.add_menu_action(self.edit_menu, self.paste_action, True)
463 self.add_menu_action(self.edit_menu, self.paste_action, True)
464
464
465 self.edit_menu.addSeparator()
465 self.edit_menu.addSeparator()
466
466
467 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
467 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
468 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
468 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
469 # Only override the default if there is a collision.
469 # Only override the default if there is a collision.
470 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
470 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
471 selectall = "Ctrl+Shift+A"
471 selectall = "Ctrl+Shift+A"
472 self.select_all_action = QtGui.QAction("Select &All",
472 self.select_all_action = QtGui.QAction("Select &All",
473 self,
473 self,
474 shortcut=selectall,
474 shortcut=selectall,
475 triggered=self.select_all_active_frontend
475 triggered=self.select_all_active_frontend
476 )
476 )
477 self.add_menu_action(self.edit_menu, self.select_all_action, True)
477 self.add_menu_action(self.edit_menu, self.select_all_action, True)
478
478
479
479
480 def init_view_menu(self):
480 def init_view_menu(self):
481 self.view_menu = self.menuBar().addMenu("&View")
481 self.view_menu = self.menuBar().addMenu("&View")
482
482
483 if sys.platform != 'darwin':
483 if sys.platform != 'darwin':
484 # disable on OSX, where there is always a menu bar
484 # disable on OSX, where there is always a menu bar
485 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
485 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
486 self,
486 self,
487 shortcut="Ctrl+Shift+M",
487 shortcut="Ctrl+Shift+M",
488 statusTip="Toggle visibility of menubar",
488 statusTip="Toggle visibility of menubar",
489 triggered=self.toggle_menu_bar)
489 triggered=self.toggle_menu_bar)
490 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
490 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
491
491
492 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
492 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
493 self.full_screen_act = QtGui.QAction("&Full Screen",
493 self.full_screen_act = QtGui.QAction("&Full Screen",
494 self,
494 self,
495 shortcut=fs_key,
495 shortcut=fs_key,
496 statusTip="Toggle between Fullscreen and Normal Size",
496 statusTip="Toggle between Fullscreen and Normal Size",
497 triggered=self.toggleFullScreen)
497 triggered=self.toggleFullScreen)
498 self.add_menu_action(self.view_menu, self.full_screen_act)
498 self.add_menu_action(self.view_menu, self.full_screen_act)
499
499
500 self.view_menu.addSeparator()
500 self.view_menu.addSeparator()
501
501
502 self.increase_font_size = QtGui.QAction("Zoom &In",
502 self.increase_font_size = QtGui.QAction("Zoom &In",
503 self,
503 self,
504 shortcut=QtGui.QKeySequence.ZoomIn,
504 shortcut=QtGui.QKeySequence.ZoomIn,
505 triggered=self.increase_font_size_active_frontend
505 triggered=self.increase_font_size_active_frontend
506 )
506 )
507 self.add_menu_action(self.view_menu, self.increase_font_size, True)
507 self.add_menu_action(self.view_menu, self.increase_font_size, True)
508
508
509 self.decrease_font_size = QtGui.QAction("Zoom &Out",
509 self.decrease_font_size = QtGui.QAction("Zoom &Out",
510 self,
510 self,
511 shortcut=QtGui.QKeySequence.ZoomOut,
511 shortcut=QtGui.QKeySequence.ZoomOut,
512 triggered=self.decrease_font_size_active_frontend
512 triggered=self.decrease_font_size_active_frontend
513 )
513 )
514 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
514 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
515
515
516 self.reset_font_size = QtGui.QAction("Zoom &Reset",
516 self.reset_font_size = QtGui.QAction("Zoom &Reset",
517 self,
517 self,
518 shortcut="Ctrl+0",
518 shortcut="Ctrl+0",
519 triggered=self.reset_font_size_active_frontend
519 triggered=self.reset_font_size_active_frontend
520 )
520 )
521 self.add_menu_action(self.view_menu, self.reset_font_size, True)
521 self.add_menu_action(self.view_menu, self.reset_font_size, True)
522
522
523 self.view_menu.addSeparator()
523 self.view_menu.addSeparator()
524
524
525 self.clear_action = QtGui.QAction("&Clear Screen",
525 self.clear_action = QtGui.QAction("&Clear Screen",
526 self,
526 self,
527 shortcut='Ctrl+L',
527 shortcut='Ctrl+L',
528 statusTip="Clear the console",
528 statusTip="Clear the console",
529 triggered=self.clear_magic_active_frontend)
529 triggered=self.clear_magic_active_frontend)
530 self.add_menu_action(self.view_menu, self.clear_action)
530 self.add_menu_action(self.view_menu, self.clear_action)
531
531
532 self.pager_menu = self.view_menu.addMenu("&Pager")
533
534 hsplit_action = QtGui.QAction(".. &Horizontal Split",
535 self,
536 triggered=lambda: self.set_paging_active_frontend('hsplit'))
537
538 vsplit_action = QtGui.QAction(" : &Vertical Split",
539 self,
540 triggered=lambda: self.set_paging_active_frontend('vsplit'))
541
542 inside_action = QtGui.QAction(" &Inside Pager",
543 self,
544 triggered=lambda: self.set_paging_active_frontend('inside'))
545
546 self.pager_menu.addAction(hsplit_action)
547 self.pager_menu.addAction(vsplit_action)
548 self.pager_menu.addAction(inside_action)
549
532 def init_kernel_menu(self):
550 def init_kernel_menu(self):
533 self.kernel_menu = self.menuBar().addMenu("&Kernel")
551 self.kernel_menu = self.menuBar().addMenu("&Kernel")
534 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
552 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
535 # keep the signal shortcuts to ctrl, rather than
553 # keep the signal shortcuts to ctrl, rather than
536 # platform-default like we do elsewhere.
554 # platform-default like we do elsewhere.
537
555
538 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
556 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
539
557
540 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
558 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
541 self,
559 self,
542 triggered=self.interrupt_kernel_active_frontend,
560 triggered=self.interrupt_kernel_active_frontend,
543 shortcut=ctrl+"+C",
561 shortcut=ctrl+"+C",
544 )
562 )
545 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
563 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
546
564
547 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
565 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
548 self,
566 self,
549 triggered=self.restart_kernel_active_frontend,
567 triggered=self.restart_kernel_active_frontend,
550 shortcut=ctrl+"+.",
568 shortcut=ctrl+"+.",
551 )
569 )
552 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
570 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
553
571
554 self.kernel_menu.addSeparator()
572 self.kernel_menu.addSeparator()
555
573
556 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
574 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
557 self,
575 self,
558 checkable=True,
576 checkable=True,
559 checked=self.active_frontend.confirm_restart,
577 checked=self.active_frontend.confirm_restart,
560 triggered=self.toggle_confirm_restart_active_frontend
578 triggered=self.toggle_confirm_restart_active_frontend
561 )
579 )
562
580
563 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
581 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
564 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
582 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
565
583
566 def _make_dynamic_magic(self,magic):
584 def _make_dynamic_magic(self,magic):
567 """Return a function `fun` that will execute `magic` on active frontend.
585 """Return a function `fun` that will execute `magic` on active frontend.
568
586
569 Parameters
587 Parameters
570 ----------
588 ----------
571 magic : string
589 magic : string
572 string that will be executed as is when the returned function is called
590 string that will be executed as is when the returned function is called
573
591
574 Returns
592 Returns
575 -------
593 -------
576 fun : function
594 fun : function
577 function with no parameters, when called will execute `magic` on the
595 function with no parameters, when called will execute `magic` on the
578 current active frontend at call time
596 current active frontend at call time
579
597
580 See Also
598 See Also
581 --------
599 --------
582 populate_all_magic_menu : generate the "All Magics..." menu
600 populate_all_magic_menu : generate the "All Magics..." menu
583
601
584 Notes
602 Notes
585 -----
603 -----
586 `fun` executes `magic` in active frontend at the moment it is triggered,
604 `fun` executes `magic` in active frontend at the moment it is triggered,
587 not the active frontend at the moment it was created.
605 not the active frontend at the moment it was created.
588
606
589 This function is mostly used to create the "All Magics..." Menu at run time.
607 This function is mostly used to create the "All Magics..." Menu at run time.
590 """
608 """
591 # need two level nested function to be sure to pass magic
609 # need two level nested function to be sure to pass magic
592 # to active frontend **at run time**.
610 # to active frontend **at run time**.
593 def inner_dynamic_magic():
611 def inner_dynamic_magic():
594 self.active_frontend.execute(magic)
612 self.active_frontend.execute(magic)
595 inner_dynamic_magic.__name__ = "dynamics_magic_s"
613 inner_dynamic_magic.__name__ = "dynamics_magic_s"
596 return inner_dynamic_magic
614 return inner_dynamic_magic
597
615
598 def populate_all_magic_menu(self, listofmagic=None):
616 def populate_all_magic_menu(self, listofmagic=None):
599 """Clean "All Magics..." menu and repopulate it with `listofmagic`
617 """Clean "All Magics..." menu and repopulate it with `listofmagic`
600
618
601 Parameters
619 Parameters
602 ----------
620 ----------
603 listofmagic : string,
621 listofmagic : string,
604 repr() of a list of strings, send back by the kernel
622 repr() of a list of strings, send back by the kernel
605
623
606 Notes
624 Notes
607 -----
625 -----
608 `listofmagic`is a repr() of list because it is fed with the result of
626 `listofmagic`is a repr() of list because it is fed with the result of
609 a 'user_expression'
627 a 'user_expression'
610 """
628 """
611 for k,v in self._magic_menu_dict.items():
629 for k,v in self._magic_menu_dict.items():
612 v.clear()
630 v.clear()
613 self.all_magic_menu.clear()
631 self.all_magic_menu.clear()
614
632
615
633
616 mlist=ast.literal_eval(listofmagic)
634 mlist=ast.literal_eval(listofmagic)
617 for magic in mlist:
635 for magic in mlist:
618 cell = (magic['type'] == 'cell')
636 cell = (magic['type'] == 'cell')
619 name = magic['name']
637 name = magic['name']
620 mclass = magic['class']
638 mclass = magic['class']
621 if cell :
639 if cell :
622 prefix='%%'
640 prefix='%%'
623 else :
641 else :
624 prefix='%'
642 prefix='%'
625 magic_menu = self._get_magic_menu(mclass)
643 magic_menu = self._get_magic_menu(mclass)
626
644
627 pmagic = '%s%s'%(prefix,name)
645 pmagic = '%s%s'%(prefix,name)
628
646
629 xaction = QtGui.QAction(pmagic,
647 xaction = QtGui.QAction(pmagic,
630 self,
648 self,
631 triggered=self._make_dynamic_magic(pmagic)
649 triggered=self._make_dynamic_magic(pmagic)
632 )
650 )
633 magic_menu.addAction(xaction)
651 magic_menu.addAction(xaction)
634 self.all_magic_menu.addAction(xaction)
652 self.all_magic_menu.addAction(xaction)
635
653
636 def update_all_magic_menu(self):
654 def update_all_magic_menu(self):
637 """ Update the list of magics in the "All Magics..." Menu
655 """ Update the list of magics in the "All Magics..." Menu
638
656
639 Request the kernel with the list of available magics and populate the
657 Request the kernel with the list of available magics and populate the
640 menu with the list received back
658 menu with the list received back
641
659
642 """
660 """
643 self.active_frontend._silent_exec_callback('get_ipython().magics_manager.lsmagic_info()',
661 self.active_frontend._silent_exec_callback('get_ipython().magics_manager.lsmagic_info()',
644 self.populate_all_magic_menu)
662 self.populate_all_magic_menu)
645
663
646 def _get_magic_menu(self,menuidentifier, menulabel=None):
664 def _get_magic_menu(self,menuidentifier, menulabel=None):
647 """return a submagic menu by name, and create it if needed
665 """return a submagic menu by name, and create it if needed
648
666
649 parameters:
667 parameters:
650 -----------
668 -----------
651
669
652 menulabel : str
670 menulabel : str
653 Label for the menu
671 Label for the menu
654
672
655 Will infere the menu name from the identifier at creation if menulabel not given.
673 Will infere the menu name from the identifier at creation if menulabel not given.
656 To do so you have too give menuidentifier as a CamelCassedString
674 To do so you have too give menuidentifier as a CamelCassedString
657 """
675 """
658 menu = self._magic_menu_dict.get(menuidentifier,None)
676 menu = self._magic_menu_dict.get(menuidentifier,None)
659 if not menu :
677 if not menu :
660 if not menulabel:
678 if not menulabel:
661 menulabel = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>",menuidentifier)
679 menulabel = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>",menuidentifier)
662 menu = QtGui.QMenu(menulabel,self.magic_menu)
680 menu = QtGui.QMenu(menulabel,self.magic_menu)
663 self._magic_menu_dict[menuidentifier]=menu
681 self._magic_menu_dict[menuidentifier]=menu
664 self.magic_menu.insertMenu(self.magic_menu_separator,menu)
682 self.magic_menu.insertMenu(self.magic_menu_separator,menu)
665 return menu
683 return menu
666
684
667
685
668
686
669 def init_magic_menu(self):
687 def init_magic_menu(self):
670 self.magic_menu = self.menuBar().addMenu("&Magic")
688 self.magic_menu = self.menuBar().addMenu("&Magic")
671 self.magic_menu_separator = self.magic_menu.addSeparator()
689 self.magic_menu_separator = self.magic_menu.addSeparator()
672
690
673 self.all_magic_menu = self._get_magic_menu("AllMagics", menulabel="&All Magics...")
691 self.all_magic_menu = self._get_magic_menu("AllMagics", menulabel="&All Magics...")
674
692
675 # This action should usually not appear as it will be cleared when menu
693 # This action should usually not appear as it will be cleared when menu
676 # is updated at first kernel response. Though, it is necessary when
694 # is updated at first kernel response. Though, it is necessary when
677 # connecting through X-forwarding, as in this case, the menu is not
695 # connecting through X-forwarding, as in this case, the menu is not
678 # auto updated, SO DO NOT DELETE.
696 # auto updated, SO DO NOT DELETE.
679 self.pop = QtGui.QAction("&Update All Magic Menu ",
697 self.pop = QtGui.QAction("&Update All Magic Menu ",
680 self, triggered=self.update_all_magic_menu)
698 self, triggered=self.update_all_magic_menu)
681 self.add_menu_action(self.all_magic_menu, self.pop)
699 self.add_menu_action(self.all_magic_menu, self.pop)
682 # we need to populate the 'Magic Menu' once the kernel has answer at
700 # we need to populate the 'Magic Menu' once the kernel has answer at
683 # least once let's do it immediately, but it's assured to works
701 # least once let's do it immediately, but it's assured to works
684 self.pop.trigger()
702 self.pop.trigger()
685
703
686 self.reset_action = QtGui.QAction("&Reset",
704 self.reset_action = QtGui.QAction("&Reset",
687 self,
705 self,
688 statusTip="Clear all variables from workspace",
706 statusTip="Clear all variables from workspace",
689 triggered=self.reset_magic_active_frontend)
707 triggered=self.reset_magic_active_frontend)
690 self.add_menu_action(self.magic_menu, self.reset_action)
708 self.add_menu_action(self.magic_menu, self.reset_action)
691
709
692 self.history_action = QtGui.QAction("&History",
710 self.history_action = QtGui.QAction("&History",
693 self,
711 self,
694 statusTip="show command history",
712 statusTip="show command history",
695 triggered=self.history_magic_active_frontend)
713 triggered=self.history_magic_active_frontend)
696 self.add_menu_action(self.magic_menu, self.history_action)
714 self.add_menu_action(self.magic_menu, self.history_action)
697
715
698 self.save_action = QtGui.QAction("E&xport History ",
716 self.save_action = QtGui.QAction("E&xport History ",
699 self,
717 self,
700 statusTip="Export History as Python File",
718 statusTip="Export History as Python File",
701 triggered=self.save_magic_active_frontend)
719 triggered=self.save_magic_active_frontend)
702 self.add_menu_action(self.magic_menu, self.save_action)
720 self.add_menu_action(self.magic_menu, self.save_action)
703
721
704 self.who_action = QtGui.QAction("&Who",
722 self.who_action = QtGui.QAction("&Who",
705 self,
723 self,
706 statusTip="List interactive variables",
724 statusTip="List interactive variables",
707 triggered=self.who_magic_active_frontend)
725 triggered=self.who_magic_active_frontend)
708 self.add_menu_action(self.magic_menu, self.who_action)
726 self.add_menu_action(self.magic_menu, self.who_action)
709
727
710 self.who_ls_action = QtGui.QAction("Wh&o ls",
728 self.who_ls_action = QtGui.QAction("Wh&o ls",
711 self,
729 self,
712 statusTip="Return a list of interactive variables",
730 statusTip="Return a list of interactive variables",
713 triggered=self.who_ls_magic_active_frontend)
731 triggered=self.who_ls_magic_active_frontend)
714 self.add_menu_action(self.magic_menu, self.who_ls_action)
732 self.add_menu_action(self.magic_menu, self.who_ls_action)
715
733
716 self.whos_action = QtGui.QAction("Who&s",
734 self.whos_action = QtGui.QAction("Who&s",
717 self,
735 self,
718 statusTip="List interactive variables with details",
736 statusTip="List interactive variables with details",
719 triggered=self.whos_magic_active_frontend)
737 triggered=self.whos_magic_active_frontend)
720 self.add_menu_action(self.magic_menu, self.whos_action)
738 self.add_menu_action(self.magic_menu, self.whos_action)
721
739
722 def init_window_menu(self):
740 def init_window_menu(self):
723 self.window_menu = self.menuBar().addMenu("&Window")
741 self.window_menu = self.menuBar().addMenu("&Window")
724 if sys.platform == 'darwin':
742 if sys.platform == 'darwin':
725 # add min/maximize actions to OSX, which lacks default bindings.
743 # add min/maximize actions to OSX, which lacks default bindings.
726 self.minimizeAct = QtGui.QAction("Mini&mize",
744 self.minimizeAct = QtGui.QAction("Mini&mize",
727 self,
745 self,
728 shortcut="Ctrl+m",
746 shortcut="Ctrl+m",
729 statusTip="Minimize the window/Restore Normal Size",
747 statusTip="Minimize the window/Restore Normal Size",
730 triggered=self.toggleMinimized)
748 triggered=self.toggleMinimized)
731 # maximize is called 'Zoom' on OSX for some reason
749 # maximize is called 'Zoom' on OSX for some reason
732 self.maximizeAct = QtGui.QAction("&Zoom",
750 self.maximizeAct = QtGui.QAction("&Zoom",
733 self,
751 self,
734 shortcut="Ctrl+Shift+M",
752 shortcut="Ctrl+Shift+M",
735 statusTip="Maximize the window/Restore Normal Size",
753 statusTip="Maximize the window/Restore Normal Size",
736 triggered=self.toggleMaximized)
754 triggered=self.toggleMaximized)
737
755
738 self.add_menu_action(self.window_menu, self.minimizeAct)
756 self.add_menu_action(self.window_menu, self.minimizeAct)
739 self.add_menu_action(self.window_menu, self.maximizeAct)
757 self.add_menu_action(self.window_menu, self.maximizeAct)
740 self.window_menu.addSeparator()
758 self.window_menu.addSeparator()
741
759
742 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
760 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
743 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
761 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
744 self,
762 self,
745 shortcut=prev_key,
763 shortcut=prev_key,
746 statusTip="Select previous tab",
764 statusTip="Select previous tab",
747 triggered=self.prev_tab)
765 triggered=self.prev_tab)
748 self.add_menu_action(self.window_menu, self.prev_tab_act)
766 self.add_menu_action(self.window_menu, self.prev_tab_act)
749
767
750 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
768 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
751 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
769 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
752 self,
770 self,
753 shortcut=next_key,
771 shortcut=next_key,
754 statusTip="Select next tab",
772 statusTip="Select next tab",
755 triggered=self.next_tab)
773 triggered=self.next_tab)
756 self.add_menu_action(self.window_menu, self.next_tab_act)
774 self.add_menu_action(self.window_menu, self.next_tab_act)
757
775
758 def init_help_menu(self):
776 def init_help_menu(self):
759 # please keep the Help menu in Mac Os even if empty. It will
777 # please keep the Help menu in Mac Os even if empty. It will
760 # automatically contain a search field to search inside menus and
778 # automatically contain a search field to search inside menus and
761 # please keep it spelled in English, as long as Qt Doesn't support
779 # please keep it spelled in English, as long as Qt Doesn't support
762 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
780 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
763 # this search field functionality
781 # this search field functionality
764
782
765 self.help_menu = self.menuBar().addMenu("&Help")
783 self.help_menu = self.menuBar().addMenu("&Help")
766
784
767
785
768 # Help Menu
786 # Help Menu
769
787
770 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
788 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
771 self,
789 self,
772 triggered=self.intro_active_frontend
790 triggered=self.intro_active_frontend
773 )
791 )
774 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
792 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
775
793
776 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
794 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
777 self,
795 self,
778 triggered=self.quickref_active_frontend
796 triggered=self.quickref_active_frontend
779 )
797 )
780 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
798 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
781
799
782 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
800 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
783 self,
801 self,
784 triggered=self.guiref_active_frontend
802 triggered=self.guiref_active_frontend
785 )
803 )
786 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
804 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
787
805
788 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
806 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
789 self,
807 self,
790 triggered=self._open_online_help)
808 triggered=self._open_online_help)
791 self.add_menu_action(self.help_menu, self.onlineHelpAct)
809 self.add_menu_action(self.help_menu, self.onlineHelpAct)
792
810
793 # minimize/maximize/fullscreen actions:
811 # minimize/maximize/fullscreen actions:
794
812
795 def toggle_menu_bar(self):
813 def toggle_menu_bar(self):
796 menu_bar = self.menuBar()
814 menu_bar = self.menuBar()
797 if menu_bar.isVisible():
815 if menu_bar.isVisible():
798 menu_bar.setVisible(False)
816 menu_bar.setVisible(False)
799 else:
817 else:
800 menu_bar.setVisible(True)
818 menu_bar.setVisible(True)
801
819
802 def toggleMinimized(self):
820 def toggleMinimized(self):
803 if not self.isMinimized():
821 if not self.isMinimized():
804 self.showMinimized()
822 self.showMinimized()
805 else:
823 else:
806 self.showNormal()
824 self.showNormal()
807
825
808 def _open_online_help(self):
826 def _open_online_help(self):
809 filename="http://ipython.org/ipython-doc/stable/index.html"
827 filename="http://ipython.org/ipython-doc/stable/index.html"
810 webbrowser.open(filename, new=1, autoraise=True)
828 webbrowser.open(filename, new=1, autoraise=True)
811
829
812 def toggleMaximized(self):
830 def toggleMaximized(self):
813 if not self.isMaximized():
831 if not self.isMaximized():
814 self.showMaximized()
832 self.showMaximized()
815 else:
833 else:
816 self.showNormal()
834 self.showNormal()
817
835
818 # Min/Max imizing while in full screen give a bug
836 # Min/Max imizing while in full screen give a bug
819 # when going out of full screen, at least on OSX
837 # when going out of full screen, at least on OSX
820 def toggleFullScreen(self):
838 def toggleFullScreen(self):
821 if not self.isFullScreen():
839 if not self.isFullScreen():
822 self.showFullScreen()
840 self.showFullScreen()
823 if sys.platform == 'darwin':
841 if sys.platform == 'darwin':
824 self.maximizeAct.setEnabled(False)
842 self.maximizeAct.setEnabled(False)
825 self.minimizeAct.setEnabled(False)
843 self.minimizeAct.setEnabled(False)
826 else:
844 else:
827 self.showNormal()
845 self.showNormal()
828 if sys.platform == 'darwin':
846 if sys.platform == 'darwin':
829 self.maximizeAct.setEnabled(True)
847 self.maximizeAct.setEnabled(True)
830 self.minimizeAct.setEnabled(True)
848 self.minimizeAct.setEnabled(True)
831
849
850 def set_paging_active_frontend(self, paging):
851 self.active_frontend._set_paging(paging)
852
832 def close_active_frontend(self):
853 def close_active_frontend(self):
833 self.close_tab(self.active_frontend)
854 self.close_tab(self.active_frontend)
834
855
835 def restart_kernel_active_frontend(self):
856 def restart_kernel_active_frontend(self):
836 self.active_frontend.request_restart_kernel()
857 self.active_frontend.request_restart_kernel()
837
858
838 def interrupt_kernel_active_frontend(self):
859 def interrupt_kernel_active_frontend(self):
839 self.active_frontend.request_interrupt_kernel()
860 self.active_frontend.request_interrupt_kernel()
840
861
841 def toggle_confirm_restart_active_frontend(self):
862 def toggle_confirm_restart_active_frontend(self):
842 widget = self.active_frontend
863 widget = self.active_frontend
843 widget.confirm_restart = not widget.confirm_restart
864 widget.confirm_restart = not widget.confirm_restart
844 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
865 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
845
866
846 def update_restart_checkbox(self):
867 def update_restart_checkbox(self):
847 if self.active_frontend is None:
868 if self.active_frontend is None:
848 return
869 return
849 widget = self.active_frontend
870 widget = self.active_frontend
850 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
871 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
851
872
852 def cut_active_frontend(self):
873 def cut_active_frontend(self):
853 widget = self.active_frontend
874 widget = self.active_frontend
854 if widget.can_cut():
875 if widget.can_cut():
855 widget.cut()
876 widget.cut()
856
877
857 def copy_active_frontend(self):
878 def copy_active_frontend(self):
858 widget = self.active_frontend
879 widget = self.active_frontend
859 widget.copy()
880 widget.copy()
860
881
861 def copy_raw_active_frontend(self):
882 def copy_raw_active_frontend(self):
862 self.active_frontend._copy_raw_action.trigger()
883 self.active_frontend._copy_raw_action.trigger()
863
884
864 def paste_active_frontend(self):
885 def paste_active_frontend(self):
865 widget = self.active_frontend
886 widget = self.active_frontend
866 if widget.can_paste():
887 if widget.can_paste():
867 widget.paste()
888 widget.paste()
868
889
869 def undo_active_frontend(self):
890 def undo_active_frontend(self):
870 self.active_frontend.undo()
891 self.active_frontend.undo()
871
892
872 def redo_active_frontend(self):
893 def redo_active_frontend(self):
873 self.active_frontend.redo()
894 self.active_frontend.redo()
874
895
875 def reset_magic_active_frontend(self):
896 def reset_magic_active_frontend(self):
876 self.active_frontend.execute("%reset")
897 self.active_frontend.execute("%reset")
877
898
878 def history_magic_active_frontend(self):
899 def history_magic_active_frontend(self):
879 self.active_frontend.execute("%history")
900 self.active_frontend.execute("%history")
880
901
881 def save_magic_active_frontend(self):
902 def save_magic_active_frontend(self):
882 self.active_frontend.save_magic()
903 self.active_frontend.save_magic()
883
904
884 def clear_magic_active_frontend(self):
905 def clear_magic_active_frontend(self):
885 self.active_frontend.execute("%clear")
906 self.active_frontend.execute("%clear")
886
907
887 def who_magic_active_frontend(self):
908 def who_magic_active_frontend(self):
888 self.active_frontend.execute("%who")
909 self.active_frontend.execute("%who")
889
910
890 def who_ls_magic_active_frontend(self):
911 def who_ls_magic_active_frontend(self):
891 self.active_frontend.execute("%who_ls")
912 self.active_frontend.execute("%who_ls")
892
913
893 def whos_magic_active_frontend(self):
914 def whos_magic_active_frontend(self):
894 self.active_frontend.execute("%whos")
915 self.active_frontend.execute("%whos")
895
916
896 def print_action_active_frontend(self):
917 def print_action_active_frontend(self):
897 self.active_frontend.print_action.trigger()
918 self.active_frontend.print_action.trigger()
898
919
899 def export_action_active_frontend(self):
920 def export_action_active_frontend(self):
900 self.active_frontend.export_action.trigger()
921 self.active_frontend.export_action.trigger()
901
922
902 def select_all_active_frontend(self):
923 def select_all_active_frontend(self):
903 self.active_frontend.select_all_action.trigger()
924 self.active_frontend.select_all_action.trigger()
904
925
905 def increase_font_size_active_frontend(self):
926 def increase_font_size_active_frontend(self):
906 self.active_frontend.increase_font_size.trigger()
927 self.active_frontend.increase_font_size.trigger()
907
928
908 def decrease_font_size_active_frontend(self):
929 def decrease_font_size_active_frontend(self):
909 self.active_frontend.decrease_font_size.trigger()
930 self.active_frontend.decrease_font_size.trigger()
910
931
911 def reset_font_size_active_frontend(self):
932 def reset_font_size_active_frontend(self):
912 self.active_frontend.reset_font_size.trigger()
933 self.active_frontend.reset_font_size.trigger()
913
934
914 def guiref_active_frontend(self):
935 def guiref_active_frontend(self):
915 self.active_frontend.execute("%guiref")
936 self.active_frontend.execute("%guiref")
916
937
917 def intro_active_frontend(self):
938 def intro_active_frontend(self):
918 self.active_frontend.execute("?")
939 self.active_frontend.execute("?")
919
940
920 def quickref_active_frontend(self):
941 def quickref_active_frontend(self):
921 self.active_frontend.execute("%quickref")
942 self.active_frontend.execute("%quickref")
922 #---------------------------------------------------------------------------
943 #---------------------------------------------------------------------------
923 # QWidget interface
944 # QWidget interface
924 #---------------------------------------------------------------------------
945 #---------------------------------------------------------------------------
925
946
926 def closeEvent(self, event):
947 def closeEvent(self, event):
927 """ Forward the close event to every tabs contained by the windows
948 """ Forward the close event to every tabs contained by the windows
928 """
949 """
929 if self.tab_widget.count() == 0:
950 if self.tab_widget.count() == 0:
930 # no tabs, just close
951 # no tabs, just close
931 event.accept()
952 event.accept()
932 return
953 return
933 # Do Not loop on the widget count as it change while closing
954 # Do Not loop on the widget count as it change while closing
934 title = self.window().windowTitle()
955 title = self.window().windowTitle()
935 cancel = QtGui.QMessageBox.Cancel
956 cancel = QtGui.QMessageBox.Cancel
936 okay = QtGui.QMessageBox.Ok
957 okay = QtGui.QMessageBox.Ok
937
958
938 if self.confirm_exit:
959 if self.confirm_exit:
939 if self.tab_widget.count() > 1:
960 if self.tab_widget.count() > 1:
940 msg = "Close all tabs, stop all kernels, and Quit?"
961 msg = "Close all tabs, stop all kernels, and Quit?"
941 else:
962 else:
942 msg = "Close console, stop kernel, and Quit?"
963 msg = "Close console, stop kernel, and Quit?"
943 info = "Kernels not started here (e.g. notebooks) will be left alone."
964 info = "Kernels not started here (e.g. notebooks) will be left alone."
944 closeall = QtGui.QPushButton("&Quit", self)
965 closeall = QtGui.QPushButton("&Quit", self)
945 closeall.setShortcut('Q')
966 closeall.setShortcut('Q')
946 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
967 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
947 title, msg)
968 title, msg)
948 box.setInformativeText(info)
969 box.setInformativeText(info)
949 box.addButton(cancel)
970 box.addButton(cancel)
950 box.addButton(closeall, QtGui.QMessageBox.YesRole)
971 box.addButton(closeall, QtGui.QMessageBox.YesRole)
951 box.setDefaultButton(closeall)
972 box.setDefaultButton(closeall)
952 box.setEscapeButton(cancel)
973 box.setEscapeButton(cancel)
953 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
974 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
954 box.setIconPixmap(pixmap)
975 box.setIconPixmap(pixmap)
955 reply = box.exec_()
976 reply = box.exec_()
956 else:
977 else:
957 reply = okay
978 reply = okay
958
979
959 if reply == cancel:
980 if reply == cancel:
960 event.ignore()
981 event.ignore()
961 return
982 return
962 if reply == okay:
983 if reply == okay:
963 while self.tab_widget.count() >= 1:
984 while self.tab_widget.count() >= 1:
964 # prevent further confirmations:
985 # prevent further confirmations:
965 widget = self.active_frontend
986 widget = self.active_frontend
966 widget._confirm_exit = False
987 widget._confirm_exit = False
967 self.close_tab(widget)
988 self.close_tab(widget)
968 event.accept()
989 event.accept()
969
990
@@ -1,243 +1,260 b''
1 """Various display related classes.
1 """Various display related classes.
2
2
3 Authors : MinRK, gregcaporaso
3 Authors : MinRK, gregcaporaso, dannystaple
4 """
4 """
5 import urllib
5
6
6 from os.path import exists, isfile, splitext, abspath, join, isdir, walk
7 from os.path import exists, isfile, splitext, abspath, join, isdir, walk
7
8
8
9
9 class YouTubeVideo(object):
10 class YouTubeVideo(object):
10 """Class for embedding a YouTube Video in an IPython session, based on its video id.
11 """Class for embedding a YouTube Video in an IPython session, based on its video id.
11
12
12 e.g. to embed the video on this page:
13 e.g. to embed the video on this page:
13
14
14 http://www.youtube.com/watch?v=foo
15 http://www.youtube.com/watch?v=foo
15
16
16 you would do:
17 you would do:
17
18
18 vid = YouTubeVideo("foo")
19 vid = YouTubeVideo("foo")
19 display(vid)
20 display(vid)
21
22 To start from 30 seconds:
23
24 vid = YouTubeVideo("abc", start=30)
25 display(vid)
26
27 To calculate seconds from time as hours, minutes, seconds use:
28 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
29
30 Other parameters can be provided as documented at
31 https://developers.google.com/youtube/player_parameters#parameter-subheader
20 """
32 """
21
33
22 def __init__(self, id, width=400, height=300):
34 def __init__(self, id, width=400, height=300, **kwargs):
23 self.id = id
35 self.id = id
24 self.width = width
36 self.width = width
25 self.height = height
37 self.height = height
38 self.params = kwargs
26
39
27 def _repr_html_(self):
40 def _repr_html_(self):
28 """return YouTube embed iframe for this video id"""
41 """return YouTube embed iframe for this video id"""
42 if self.params:
43 params = "?" + urllib.urlencode(self.params)
44 else:
45 params = ""
29 return """
46 return """
30 <iframe
47 <iframe
31 width="%i"
48 width="%i"
32 height="%i"
49 height="%i"
33 src="http://www.youtube.com/embed/%s"
50 src="http://www.youtube.com/embed/%s%s"
34 frameborder="0"
51 frameborder="0"
35 allowfullscreen
52 allowfullscreen
36 ></iframe>
53 ></iframe>
37 """%(self.width, self.height, self.id)
54 """ % (self.width, self.height, self.id, params)
38
55
39 class FileLink(object):
56 class FileLink(object):
40 """Class for embedding a local file link in an IPython session, based on path
57 """Class for embedding a local file link in an IPython session, based on path
41
58
42 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
59 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
43
60
44 you would do:
61 you would do:
45
62
46 local_file = FileLink("my/data.txt")
63 local_file = FileLink("my/data.txt")
47 display(local_file)
64 display(local_file)
48
65
49 or in the HTML notebook, just
66 or in the HTML notebook, just
50
67
51 FileLink("my/data.txt")
68 FileLink("my/data.txt")
52 """
69 """
53
70
54 html_link_str = "<a href='%s' target='_blank'>%s</a>"
71 html_link_str = "<a href='%s' target='_blank'>%s</a>"
55
72
56 def __init__(self,
73 def __init__(self,
57 path,
74 path,
58 url_prefix='files/',
75 url_prefix='files/',
59 result_html_prefix='',
76 result_html_prefix='',
60 result_html_suffix='<br>'):
77 result_html_suffix='<br>'):
61 """
78 """
62 path : path to the file or directory that should be formatted
79 path : path to the file or directory that should be formatted
63 directory_prefix : prefix to be prepended to all files to form a
80 directory_prefix : prefix to be prepended to all files to form a
64 working link [default: 'files']
81 working link [default: 'files']
65 result_html_prefix : text to append to beginning to link
82 result_html_prefix : text to append to beginning to link
66 [default: none]
83 [default: none]
67 result_html_suffix : text to append at the end of link
84 result_html_suffix : text to append at the end of link
68 [default: '<br>']
85 [default: '<br>']
69 """
86 """
70 self.path = path
87 self.path = path
71 self.url_prefix = url_prefix
88 self.url_prefix = url_prefix
72 self.result_html_prefix = result_html_prefix
89 self.result_html_prefix = result_html_prefix
73 self.result_html_suffix = result_html_suffix
90 self.result_html_suffix = result_html_suffix
74
91
75 def _format_path(self):
92 def _format_path(self):
76 fp = ''.join([self.url_prefix,self.path])
93 fp = ''.join([self.url_prefix,self.path])
77 return ''.join([self.result_html_prefix,
94 return ''.join([self.result_html_prefix,
78 self.html_link_str % (fp, self.path),
95 self.html_link_str % (fp, self.path),
79 self.result_html_suffix])
96 self.result_html_suffix])
80
97
81 def _repr_html_(self):
98 def _repr_html_(self):
82 """return html link to file
99 """return html link to file
83 """
100 """
84 if not exists(self.path):
101 if not exists(self.path):
85 return ("Path (<tt>%s</tt>) doesn't exist. "
102 return ("Path (<tt>%s</tt>) doesn't exist. "
86 "It may still be in the process of "
103 "It may still be in the process of "
87 "being generated, or you may have the "
104 "being generated, or you may have the "
88 "incorrect path." % self.path)
105 "incorrect path." % self.path)
89
106
90 return self._format_path()
107 return self._format_path()
91
108
92 def __repr__(self):
109 def __repr__(self):
93 """return absolute path to file
110 """return absolute path to file
94 """
111 """
95 return abspath(self.path)
112 return abspath(self.path)
96
113
97 # Create an alias for formatting a single directory name as a link.
114 # Create an alias for formatting a single directory name as a link.
98 # Right now this is the same as a formatting for a single file, but
115 # Right now this is the same as a formatting for a single file, but
99 # we'll encourage users to reference these with a different class in
116 # we'll encourage users to reference these with a different class in
100 # case we want to change this in the future.
117 # case we want to change this in the future.
101 DirectoryLink = FileLink
118 DirectoryLink = FileLink
102
119
103 class FileLinks(FileLink):
120 class FileLinks(FileLink):
104 """Class for embedding local file links in an IPython session, based on path
121 """Class for embedding local file links in an IPython session, based on path
105
122
106 e.g. to embed links to files that were generated in the IPython notebook under my/data
123 e.g. to embed links to files that were generated in the IPython notebook under my/data
107
124
108 you would do:
125 you would do:
109
126
110 local_files = FileLinks("my/data")
127 local_files = FileLinks("my/data")
111 display(local_files)
128 display(local_files)
112
129
113 or in the HTML notebook, just
130 or in the HTML notebook, just
114
131
115 FileLinks("my/data")
132 FileLinks("my/data")
116
133
117 """
134 """
118 def __init__(self,
135 def __init__(self,
119 path,
136 path,
120 url_prefix='files/',
137 url_prefix='files/',
121 included_suffixes=None,
138 included_suffixes=None,
122 result_html_prefix='',
139 result_html_prefix='',
123 result_html_suffix='<br>',
140 result_html_suffix='<br>',
124 notebook_display_formatter=None,
141 notebook_display_formatter=None,
125 terminal_display_formatter=None):
142 terminal_display_formatter=None):
126 """
143 """
127 included_suffixes : list of filename suffixes to include when
144 included_suffixes : list of filename suffixes to include when
128 formatting output [default: include all files]
145 formatting output [default: include all files]
129
146
130 See the FileLink (baseclass of LocalDirectory) docstring for
147 See the FileLink (baseclass of LocalDirectory) docstring for
131 information on additional parameters.
148 information on additional parameters.
132
149
133 notebook_display_formatter : func passed to os.path.walk when
150 notebook_display_formatter : func passed to os.path.walk when
134 formatting links for display in the notebook
151 formatting links for display in the notebook
135
152
136 terminal_display_formatter : func passed to os.path.walk when
153 terminal_display_formatter : func passed to os.path.walk when
137 formatting links for display in the terminal
154 formatting links for display in the terminal
138
155
139 """
156 """
140 self.included_suffixes = included_suffixes
157 self.included_suffixes = included_suffixes
141 # remove trailing slashs for more consistent output formatting
158 # remove trailing slashs for more consistent output formatting
142 path = path.rstrip('/')
159 path = path.rstrip('/')
143 FileLink.__init__(self,
160 FileLink.__init__(self,
144 path,
161 path,
145 url_prefix,
162 url_prefix,
146 result_html_prefix,
163 result_html_prefix,
147 result_html_suffix)
164 result_html_suffix)
148
165
149 self.notebook_display_formatter = \
166 self.notebook_display_formatter = \
150 notebook_display_formatter or self._get_notebook_display_formatter()
167 notebook_display_formatter or self._get_notebook_display_formatter()
151 self.terminal_display_formatter = \
168 self.terminal_display_formatter = \
152 terminal_display_formatter or self._get_terminal_display_formatter()
169 terminal_display_formatter or self._get_terminal_display_formatter()
153
170
154 def _get_display_formatter(self,
171 def _get_display_formatter(self,
155 dirname_output_format,
172 dirname_output_format,
156 fname_output_format,
173 fname_output_format,
157 fp_format):
174 fp_format):
158 """ generate func to pass to os.path.walk
175 """ generate func to pass to os.path.walk
159
176
160 dirname_output_format: string to use for formatting directory
177 dirname_output_format: string to use for formatting directory
161 names, dirname will be substituted for a single "%s" which
178 names, dirname will be substituted for a single "%s" which
162 must appear in this string
179 must appear in this string
163 fname_output_format: string to use for formatting file names,
180 fname_output_format: string to use for formatting file names,
164 if a single "%s" appears in the string, fname will be substituted
181 if a single "%s" appears in the string, fname will be substituted
165 if two "%s" appear in the string, the path to fname will be
182 if two "%s" appear in the string, the path to fname will be
166 substituted for the first and fname will be substituted for the
183 substituted for the first and fname will be substituted for the
167 second
184 second
168 fp_format: string to use for formatting filepaths, must contain
185 fp_format: string to use for formatting filepaths, must contain
169 exactly two "%s" and the dirname will be subsituted for the first
186 exactly two "%s" and the dirname will be subsituted for the first
170 and fname will be substituted for the second
187 and fname will be substituted for the second
171 """
188 """
172
189
173 included_suffixes = self.included_suffixes
190 included_suffixes = self.included_suffixes
174
191
175 def f(output_lines, dirname, fnames):
192 def f(output_lines, dirname, fnames):
176 """ func to be passed to os.path.walk """
193 """ func to be passed to os.path.walk """
177 # begin by figuring out which filenames, if any,
194 # begin by figuring out which filenames, if any,
178 # are going to be displayed
195 # are going to be displayed
179 display_fnames = []
196 display_fnames = []
180 for fname in fnames:
197 for fname in fnames:
181 if (isfile(join(dirname,fname)) and
198 if (isfile(join(dirname,fname)) and
182 (included_suffixes == None or
199 (included_suffixes == None or
183 splitext(fname)[1] in included_suffixes)):
200 splitext(fname)[1] in included_suffixes)):
184 display_fnames.append(fname)
201 display_fnames.append(fname)
185
202
186 if len(display_fnames) == 0:
203 if len(display_fnames) == 0:
187 # if there are no filenames to display, don't print anything
204 # if there are no filenames to display, don't print anything
188 # (not even the directory name)
205 # (not even the directory name)
189 pass
206 pass
190 else:
207 else:
191 # otherwise print the formatted directory name followed by
208 # otherwise print the formatted directory name followed by
192 # the formatted filenames
209 # the formatted filenames
193 dirname_output_line = dirname_output_format % dirname
210 dirname_output_line = dirname_output_format % dirname
194 output_lines.append(dirname_output_line)
211 output_lines.append(dirname_output_line)
195 for fname in display_fnames:
212 for fname in display_fnames:
196 fp = fp_format % (dirname,fname)
213 fp = fp_format % (dirname,fname)
197 try:
214 try:
198 # output can include both a filepath and a filename...
215 # output can include both a filepath and a filename...
199 fname_output_line = fname_output_format % (fp, fname)
216 fname_output_line = fname_output_format % (fp, fname)
200 except TypeError:
217 except TypeError:
201 # ... or just a single filepath
218 # ... or just a single filepath
202 fname_output_line = fname_output_format % fname
219 fname_output_line = fname_output_format % fname
203 output_lines.append(fname_output_line)
220 output_lines.append(fname_output_line)
204 return
221 return
205 return f
222 return f
206
223
207 def _get_notebook_display_formatter(self,
224 def _get_notebook_display_formatter(self,
208 spacer="&nbsp;&nbsp;"):
225 spacer="&nbsp;&nbsp;"):
209 """ generate func to pass to os.path.walk for notebook formatting
226 """ generate func to pass to os.path.walk for notebook formatting
210 """
227 """
211 dirname_output_format = \
228 dirname_output_format = \
212 self.result_html_prefix + "%s/" + self.result_html_suffix
229 self.result_html_prefix + "%s/" + self.result_html_suffix
213 fname_output_format = \
230 fname_output_format = \
214 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
231 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
215 fp_format = self.url_prefix + '%s/%s'
232 fp_format = self.url_prefix + '%s/%s'
216
233
217 return self._get_display_formatter(dirname_output_format,
234 return self._get_display_formatter(dirname_output_format,
218 fname_output_format,
235 fname_output_format,
219 fp_format)
236 fp_format)
220
237
221 def _get_terminal_display_formatter(self,
238 def _get_terminal_display_formatter(self,
222 spacer=" "):
239 spacer=" "):
223 """ generate func to pass to os.path.walk for terminal formatting
240 """ generate func to pass to os.path.walk for terminal formatting
224 """
241 """
225 dirname_output_format = "%s/"
242 dirname_output_format = "%s/"
226 fname_output_format = spacer + "%s"
243 fname_output_format = spacer + "%s"
227 fp_format = '%s/%s'
244 fp_format = '%s/%s'
228
245
229 return self._get_display_formatter(dirname_output_format,
246 return self._get_display_formatter(dirname_output_format,
230 fname_output_format,
247 fname_output_format,
231 fp_format)
248 fp_format)
232
249
233 def _format_path(self):
250 def _format_path(self):
234 result_lines = []
251 result_lines = []
235 walk(self.path, self.notebook_display_formatter, result_lines)
252 walk(self.path, self.notebook_display_formatter, result_lines)
236 return '\n'.join(result_lines)
253 return '\n'.join(result_lines)
237
254
238 def __repr__(self):
255 def __repr__(self):
239 """return newline-separated absolute paths
256 """return newline-separated absolute paths
240 """
257 """
241 result_lines = []
258 result_lines = []
242 walk(self.path, self.terminal_display_formatter, result_lines)
259 walk(self.path, self.terminal_display_formatter, result_lines)
243 return '\n'.join(result_lines) No newline at end of file
260 return '\n'.join(result_lines)
@@ -1,122 +1,144 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Qt4's inputhook support function
3 Qt4's inputhook support function
4
4
5 Author: Christian Boos
5 Author: Christian Boos
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
9 # Copyright (C) 2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 from IPython.core.interactiveshell import InteractiveShell
19 from IPython.core.interactiveshell import InteractiveShell
20 from IPython.external.qt_for_kernel import QtCore, QtGui
20 from IPython.external.qt_for_kernel import QtCore, QtGui
21 from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
21 from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Code
24 # Code
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 def create_inputhook_qt4(mgr, app=None):
27 def create_inputhook_qt4(mgr, app=None):
28 """Create an input hook for running the Qt4 application event loop.
28 """Create an input hook for running the Qt4 application event loop.
29
29
30 Parameters
30 Parameters
31 ----------
31 ----------
32 mgr : an InputHookManager
32 mgr : an InputHookManager
33
33
34 app : Qt Application, optional.
34 app : Qt Application, optional.
35 Running application to use. If not given, we probe Qt for an
35 Running application to use. If not given, we probe Qt for an
36 existing application object, and create a new one if none is found.
36 existing application object, and create a new one if none is found.
37
37
38 Returns
38 Returns
39 -------
39 -------
40 A pair consisting of a Qt Application (either the one given or the
40 A pair consisting of a Qt Application (either the one given or the
41 one found or created) and a inputhook.
41 one found or created) and a inputhook.
42
42
43 Notes
43 Notes
44 -----
44 -----
45 We use a custom input hook instead of PyQt4's default one, as it
45 We use a custom input hook instead of PyQt4's default one, as it
46 interacts better with the readline packages (issue #481).
46 interacts better with the readline packages (issue #481).
47
47
48 The inputhook function works in tandem with a 'pre_prompt_hook'
48 The inputhook function works in tandem with a 'pre_prompt_hook'
49 which automatically restores the hook as an inputhook in case the
49 which automatically restores the hook as an inputhook in case the
50 latter has been temporarily disabled after having intercepted a
50 latter has been temporarily disabled after having intercepted a
51 KeyboardInterrupt.
51 KeyboardInterrupt.
52 """
52 """
53
53
54 if app is None:
54 if app is None:
55 app = QtCore.QCoreApplication.instance()
55 app = QtCore.QCoreApplication.instance()
56 if app is None:
56 if app is None:
57 app = QtGui.QApplication([" "])
57 app = QtGui.QApplication([" "])
58
58
59 # Re-use previously created inputhook if any
59 # Re-use previously created inputhook if any
60 ip = InteractiveShell.instance()
60 ip = InteractiveShell.instance()
61 if hasattr(ip, '_inputhook_qt4'):
61 if hasattr(ip, '_inputhook_qt4'):
62 return app, ip._inputhook_qt4
62 return app, ip._inputhook_qt4
63
63
64 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of
64 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of
65 # hooks (they both share the got_kbdint flag)
65 # hooks (they both share the got_kbdint flag)
66
66
67 got_kbdint = [False]
67 got_kbdint = [False]
68
68
69 def inputhook_qt4():
69 def inputhook_qt4():
70 """PyOS_InputHook python hook for Qt4.
70 """PyOS_InputHook python hook for Qt4.
71
71
72 Process pending Qt events and if there's no pending keyboard
72 Process pending Qt events and if there's no pending keyboard
73 input, spend a short slice of time (50ms) running the Qt event
73 input, spend a short slice of time (50ms) running the Qt event
74 loop.
74 loop.
75
75
76 As a Python ctypes callback can't raise an exception, we catch
76 As a Python ctypes callback can't raise an exception, we catch
77 the KeyboardInterrupt and temporarily deactivate the hook,
77 the KeyboardInterrupt and temporarily deactivate the hook,
78 which will let a *second* CTRL+C be processed normally and go
78 which will let a *second* CTRL+C be processed normally and go
79 back to a clean prompt line.
79 back to a clean prompt line.
80 """
80 """
81 try:
81 try:
82 allow_CTRL_C()
82 allow_CTRL_C()
83 app = QtCore.QCoreApplication.instance()
83 app = QtCore.QCoreApplication.instance()
84 if not app: # shouldn't happen, but safer if it happens anyway...
84 if not app: # shouldn't happen, but safer if it happens anyway...
85 return 0
85 return 0
86 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
86 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
87 if not stdin_ready():
87 if not stdin_ready():
88 # Generally a program would run QCoreApplication::exec()
89 # from main() to enter and process the Qt event loop until
90 # quit() or exit() is called and the program terminates.
91 #
92 # For our input hook integration, we need to repeatedly
93 # enter and process the Qt event loop for only a short
94 # amount of time (say 50ms) to ensure that Python stays
95 # responsive to other user inputs.
96 #
97 # A naive approach would be to repeatedly call
98 # QCoreApplication::exec(), using a timer to quit after a
99 # short amount of time. Unfortunately, QCoreApplication
100 # emits an aboutToQuit signal before stopping, which has
101 # the undesirable effect of closing all modal windows.
102 #
103 # To work around this problem, we instead create a
104 # QEventLoop and call QEventLoop::exec(). Other than
105 # setting some state variables which do not seem to be
106 # used anywhere, the only thing QCoreApplication adds is
107 # the aboutToQuit signal which is precisely what we are
108 # trying to avoid.
88 timer = QtCore.QTimer()
109 timer = QtCore.QTimer()
89 timer.timeout.connect(app.quit)
110 event_loop = QtCore.QEventLoop()
111 timer.timeout.connect(event_loop.quit)
90 while not stdin_ready():
112 while not stdin_ready():
91 timer.start(50)
113 timer.start(50)
92 app.exec_()
114 event_loop.exec_()
93 timer.stop()
115 timer.stop()
94 except KeyboardInterrupt:
116 except KeyboardInterrupt:
95 ignore_CTRL_C()
117 ignore_CTRL_C()
96 got_kbdint[0] = True
118 got_kbdint[0] = True
97 print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
119 print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
98 mgr.clear_inputhook()
120 mgr.clear_inputhook()
99 except: # NO exceptions are allowed to escape from a ctypes callback
121 except: # NO exceptions are allowed to escape from a ctypes callback
100 ignore_CTRL_C()
122 ignore_CTRL_C()
101 from traceback import print_exc
123 from traceback import print_exc
102 print_exc()
124 print_exc()
103 print("Got exception from inputhook_qt4, unregistering.")
125 print("Got exception from inputhook_qt4, unregistering.")
104 mgr.clear_inputhook()
126 mgr.clear_inputhook()
105 finally:
127 finally:
106 allow_CTRL_C()
128 allow_CTRL_C()
107 return 0
129 return 0
108
130
109 def preprompthook_qt4(ishell):
131 def preprompthook_qt4(ishell):
110 """'pre_prompt_hook' used to restore the Qt4 input hook
132 """'pre_prompt_hook' used to restore the Qt4 input hook
111
133
112 (in case the latter was temporarily deactivated after a
134 (in case the latter was temporarily deactivated after a
113 CTRL+C)
135 CTRL+C)
114 """
136 """
115 if got_kbdint[0]:
137 if got_kbdint[0]:
116 mgr.set_inputhook(inputhook_qt4)
138 mgr.set_inputhook(inputhook_qt4)
117 got_kbdint[0] = False
139 got_kbdint[0] = False
118
140
119 ip._inputhook_qt4 = inputhook_qt4
141 ip._inputhook_qt4 = inputhook_qt4
120 ip.set_hook('pre_prompt_hook', preprompthook_qt4)
142 ip.set_hook('pre_prompt_hook', preprompthook_qt4)
121
143
122 return app, inputhook_qt4
144 return app, inputhook_qt4
@@ -1,398 +1,399 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The IPython engine application
4 The IPython engine application
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * MinRK
9 * MinRK
10
10
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008-2011 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 import json
24 import json
25 import os
25 import os
26 import sys
26 import sys
27 import time
27 import time
28
28
29 import zmq
29 import zmq
30 from zmq.eventloop import ioloop
30 from zmq.eventloop import ioloop
31
31
32 from IPython.core.profiledir import ProfileDir
32 from IPython.core.profiledir import ProfileDir
33 from IPython.parallel.apps.baseapp import (
33 from IPython.parallel.apps.baseapp import (
34 BaseParallelApplication,
34 BaseParallelApplication,
35 base_aliases,
35 base_aliases,
36 base_flags,
36 base_flags,
37 catch_config_error,
37 catch_config_error,
38 )
38 )
39 from IPython.zmq.log import EnginePUBHandler
39 from IPython.zmq.log import EnginePUBHandler
40 from IPython.zmq.ipkernel import Kernel, IPKernelApp
40 from IPython.zmq.ipkernel import Kernel, IPKernelApp
41 from IPython.zmq.session import (
41 from IPython.zmq.session import (
42 Session, session_aliases, session_flags
42 Session, session_aliases, session_flags
43 )
43 )
44 from IPython.zmq.zmqshell import ZMQInteractiveShell
44
45
45 from IPython.config.configurable import Configurable
46 from IPython.config.configurable import Configurable
46
47
47 from IPython.parallel.engine.engine import EngineFactory
48 from IPython.parallel.engine.engine import EngineFactory
48 from IPython.parallel.util import disambiguate_ip_address
49 from IPython.parallel.util import disambiguate_ip_address
49
50
50 from IPython.utils.importstring import import_item
51 from IPython.utils.importstring import import_item
51 from IPython.utils.py3compat import cast_bytes
52 from IPython.utils.py3compat import cast_bytes
52 from IPython.utils.traitlets import Bool, Unicode, Dict, List, Float, Instance
53 from IPython.utils.traitlets import Bool, Unicode, Dict, List, Float, Instance
53
54
54
55
55 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
56 # Module level variables
57 # Module level variables
57 #-----------------------------------------------------------------------------
58 #-----------------------------------------------------------------------------
58
59
59 #: The default config file name for this application
60 #: The default config file name for this application
60 default_config_file_name = u'ipengine_config.py'
61 default_config_file_name = u'ipengine_config.py'
61
62
62 _description = """Start an IPython engine for parallel computing.
63 _description = """Start an IPython engine for parallel computing.
63
64
64 IPython engines run in parallel and perform computations on behalf of a client
65 IPython engines run in parallel and perform computations on behalf of a client
65 and controller. A controller needs to be started before the engines. The
66 and controller. A controller needs to be started before the engines. The
66 engine can be configured using command line options or using a cluster
67 engine can be configured using command line options or using a cluster
67 directory. Cluster directories contain config, log and security files and are
68 directory. Cluster directories contain config, log and security files and are
68 usually located in your ipython directory and named as "profile_name".
69 usually located in your ipython directory and named as "profile_name".
69 See the `profile` and `profile-dir` options for details.
70 See the `profile` and `profile-dir` options for details.
70 """
71 """
71
72
72 _examples = """
73 _examples = """
73 ipengine --ip=192.168.0.1 --port=1000 # connect to hub at ip and port
74 ipengine --ip=192.168.0.1 --port=1000 # connect to hub at ip and port
74 ipengine --log-to-file --log-level=DEBUG # log to a file with DEBUG verbosity
75 ipengine --log-to-file --log-level=DEBUG # log to a file with DEBUG verbosity
75 """
76 """
76
77
77 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
78 # MPI configuration
79 # MPI configuration
79 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
80
81
81 mpi4py_init = """from mpi4py import MPI as mpi
82 mpi4py_init = """from mpi4py import MPI as mpi
82 mpi.size = mpi.COMM_WORLD.Get_size()
83 mpi.size = mpi.COMM_WORLD.Get_size()
83 mpi.rank = mpi.COMM_WORLD.Get_rank()
84 mpi.rank = mpi.COMM_WORLD.Get_rank()
84 """
85 """
85
86
86
87
87 pytrilinos_init = """from PyTrilinos import Epetra
88 pytrilinos_init = """from PyTrilinos import Epetra
88 class SimpleStruct:
89 class SimpleStruct:
89 pass
90 pass
90 mpi = SimpleStruct()
91 mpi = SimpleStruct()
91 mpi.rank = 0
92 mpi.rank = 0
92 mpi.size = 0
93 mpi.size = 0
93 """
94 """
94
95
95 class MPI(Configurable):
96 class MPI(Configurable):
96 """Configurable for MPI initialization"""
97 """Configurable for MPI initialization"""
97 use = Unicode('', config=True,
98 use = Unicode('', config=True,
98 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).'
99 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).'
99 )
100 )
100
101
101 def _use_changed(self, name, old, new):
102 def _use_changed(self, name, old, new):
102 # load default init script if it's not set
103 # load default init script if it's not set
103 if not self.init_script:
104 if not self.init_script:
104 self.init_script = self.default_inits.get(new, '')
105 self.init_script = self.default_inits.get(new, '')
105
106
106 init_script = Unicode('', config=True,
107 init_script = Unicode('', config=True,
107 help="Initialization code for MPI")
108 help="Initialization code for MPI")
108
109
109 default_inits = Dict({'mpi4py' : mpi4py_init, 'pytrilinos':pytrilinos_init},
110 default_inits = Dict({'mpi4py' : mpi4py_init, 'pytrilinos':pytrilinos_init},
110 config=True)
111 config=True)
111
112
112
113
113 #-----------------------------------------------------------------------------
114 #-----------------------------------------------------------------------------
114 # Main application
115 # Main application
115 #-----------------------------------------------------------------------------
116 #-----------------------------------------------------------------------------
116 aliases = dict(
117 aliases = dict(
117 file = 'IPEngineApp.url_file',
118 file = 'IPEngineApp.url_file',
118 c = 'IPEngineApp.startup_command',
119 c = 'IPEngineApp.startup_command',
119 s = 'IPEngineApp.startup_script',
120 s = 'IPEngineApp.startup_script',
120
121
121 url = 'EngineFactory.url',
122 url = 'EngineFactory.url',
122 ssh = 'EngineFactory.sshserver',
123 ssh = 'EngineFactory.sshserver',
123 sshkey = 'EngineFactory.sshkey',
124 sshkey = 'EngineFactory.sshkey',
124 ip = 'EngineFactory.ip',
125 ip = 'EngineFactory.ip',
125 transport = 'EngineFactory.transport',
126 transport = 'EngineFactory.transport',
126 port = 'EngineFactory.regport',
127 port = 'EngineFactory.regport',
127 location = 'EngineFactory.location',
128 location = 'EngineFactory.location',
128
129
129 timeout = 'EngineFactory.timeout',
130 timeout = 'EngineFactory.timeout',
130
131
131 mpi = 'MPI.use',
132 mpi = 'MPI.use',
132
133
133 )
134 )
134 aliases.update(base_aliases)
135 aliases.update(base_aliases)
135 aliases.update(session_aliases)
136 aliases.update(session_aliases)
136 flags = {}
137 flags = {}
137 flags.update(base_flags)
138 flags.update(base_flags)
138 flags.update(session_flags)
139 flags.update(session_flags)
139
140
140 class IPEngineApp(BaseParallelApplication):
141 class IPEngineApp(BaseParallelApplication):
141
142
142 name = 'ipengine'
143 name = 'ipengine'
143 description = _description
144 description = _description
144 examples = _examples
145 examples = _examples
145 config_file_name = Unicode(default_config_file_name)
146 config_file_name = Unicode(default_config_file_name)
146 classes = List([ProfileDir, Session, EngineFactory, Kernel, MPI])
147 classes = List([ZMQInteractiveShell, ProfileDir, Session, EngineFactory, Kernel, MPI])
147
148
148 startup_script = Unicode(u'', config=True,
149 startup_script = Unicode(u'', config=True,
149 help='specify a script to be run at startup')
150 help='specify a script to be run at startup')
150 startup_command = Unicode('', config=True,
151 startup_command = Unicode('', config=True,
151 help='specify a command to be run at startup')
152 help='specify a command to be run at startup')
152
153
153 url_file = Unicode(u'', config=True,
154 url_file = Unicode(u'', config=True,
154 help="""The full location of the file containing the connection information for
155 help="""The full location of the file containing the connection information for
155 the controller. If this is not given, the file must be in the
156 the controller. If this is not given, the file must be in the
156 security directory of the cluster directory. This location is
157 security directory of the cluster directory. This location is
157 resolved using the `profile` or `profile_dir` options.""",
158 resolved using the `profile` or `profile_dir` options.""",
158 )
159 )
159 wait_for_url_file = Float(5, config=True,
160 wait_for_url_file = Float(5, config=True,
160 help="""The maximum number of seconds to wait for url_file to exist.
161 help="""The maximum number of seconds to wait for url_file to exist.
161 This is useful for batch-systems and shared-filesystems where the
162 This is useful for batch-systems and shared-filesystems where the
162 controller and engine are started at the same time and it
163 controller and engine are started at the same time and it
163 may take a moment for the controller to write the connector files.""")
164 may take a moment for the controller to write the connector files.""")
164
165
165 url_file_name = Unicode(u'ipcontroller-engine.json', config=True)
166 url_file_name = Unicode(u'ipcontroller-engine.json', config=True)
166
167
167 def _cluster_id_changed(self, name, old, new):
168 def _cluster_id_changed(self, name, old, new):
168 if new:
169 if new:
169 base = 'ipcontroller-%s' % new
170 base = 'ipcontroller-%s' % new
170 else:
171 else:
171 base = 'ipcontroller'
172 base = 'ipcontroller'
172 self.url_file_name = "%s-engine.json" % base
173 self.url_file_name = "%s-engine.json" % base
173
174
174 log_url = Unicode('', config=True,
175 log_url = Unicode('', config=True,
175 help="""The URL for the iploggerapp instance, for forwarding
176 help="""The URL for the iploggerapp instance, for forwarding
176 logging to a central location.""")
177 logging to a central location.""")
177
178
178 # an IPKernelApp instance, used to setup listening for shell frontends
179 # an IPKernelApp instance, used to setup listening for shell frontends
179 kernel_app = Instance(IPKernelApp)
180 kernel_app = Instance(IPKernelApp)
180
181
181 aliases = Dict(aliases)
182 aliases = Dict(aliases)
182 flags = Dict(flags)
183 flags = Dict(flags)
183
184
184 @property
185 @property
185 def kernel(self):
186 def kernel(self):
186 """allow access to the Kernel object, so I look like IPKernelApp"""
187 """allow access to the Kernel object, so I look like IPKernelApp"""
187 return self.engine.kernel
188 return self.engine.kernel
188
189
189 def find_url_file(self):
190 def find_url_file(self):
190 """Set the url file.
191 """Set the url file.
191
192
192 Here we don't try to actually see if it exists for is valid as that
193 Here we don't try to actually see if it exists for is valid as that
193 is hadled by the connection logic.
194 is hadled by the connection logic.
194 """
195 """
195 config = self.config
196 config = self.config
196 # Find the actual controller key file
197 # Find the actual controller key file
197 if not self.url_file:
198 if not self.url_file:
198 self.url_file = os.path.join(
199 self.url_file = os.path.join(
199 self.profile_dir.security_dir,
200 self.profile_dir.security_dir,
200 self.url_file_name
201 self.url_file_name
201 )
202 )
202
203
203 def load_connector_file(self):
204 def load_connector_file(self):
204 """load config from a JSON connector file,
205 """load config from a JSON connector file,
205 at a *lower* priority than command-line/config files.
206 at a *lower* priority than command-line/config files.
206 """
207 """
207
208
208 self.log.info("Loading url_file %r", self.url_file)
209 self.log.info("Loading url_file %r", self.url_file)
209 config = self.config
210 config = self.config
210
211
211 with open(self.url_file) as f:
212 with open(self.url_file) as f:
212 d = json.loads(f.read())
213 d = json.loads(f.read())
213
214
214 # allow hand-override of location for disambiguation
215 # allow hand-override of location for disambiguation
215 # and ssh-server
216 # and ssh-server
216 try:
217 try:
217 config.EngineFactory.location
218 config.EngineFactory.location
218 except AttributeError:
219 except AttributeError:
219 config.EngineFactory.location = d['location']
220 config.EngineFactory.location = d['location']
220
221
221 try:
222 try:
222 config.EngineFactory.sshserver
223 config.EngineFactory.sshserver
223 except AttributeError:
224 except AttributeError:
224 config.EngineFactory.sshserver = d.get('ssh')
225 config.EngineFactory.sshserver = d.get('ssh')
225
226
226 location = config.EngineFactory.location
227 location = config.EngineFactory.location
227
228
228 proto, ip = d['interface'].split('://')
229 proto, ip = d['interface'].split('://')
229 ip = disambiguate_ip_address(ip, location)
230 ip = disambiguate_ip_address(ip, location)
230 d['interface'] = '%s://%s' % (proto, ip)
231 d['interface'] = '%s://%s' % (proto, ip)
231
232
232 # DO NOT allow override of basic URLs, serialization, or exec_key
233 # DO NOT allow override of basic URLs, serialization, or exec_key
233 # JSON file takes top priority there
234 # JSON file takes top priority there
234 config.Session.key = cast_bytes(d['exec_key'])
235 config.Session.key = cast_bytes(d['exec_key'])
235
236
236 config.EngineFactory.url = d['interface'] + ':%i' % d['registration']
237 config.EngineFactory.url = d['interface'] + ':%i' % d['registration']
237
238
238 config.Session.packer = d['pack']
239 config.Session.packer = d['pack']
239 config.Session.unpacker = d['unpack']
240 config.Session.unpacker = d['unpack']
240
241
241 self.log.debug("Config changed:")
242 self.log.debug("Config changed:")
242 self.log.debug("%r", config)
243 self.log.debug("%r", config)
243 self.connection_info = d
244 self.connection_info = d
244
245
245 def bind_kernel(self, **kwargs):
246 def bind_kernel(self, **kwargs):
246 """Promote engine to listening kernel, accessible to frontends."""
247 """Promote engine to listening kernel, accessible to frontends."""
247 if self.kernel_app is not None:
248 if self.kernel_app is not None:
248 return
249 return
249
250
250 self.log.info("Opening ports for direct connections as an IPython kernel")
251 self.log.info("Opening ports for direct connections as an IPython kernel")
251
252
252 kernel = self.kernel
253 kernel = self.kernel
253
254
254 kwargs.setdefault('config', self.config)
255 kwargs.setdefault('config', self.config)
255 kwargs.setdefault('log', self.log)
256 kwargs.setdefault('log', self.log)
256 kwargs.setdefault('profile_dir', self.profile_dir)
257 kwargs.setdefault('profile_dir', self.profile_dir)
257 kwargs.setdefault('session', self.engine.session)
258 kwargs.setdefault('session', self.engine.session)
258
259
259 app = self.kernel_app = IPKernelApp(**kwargs)
260 app = self.kernel_app = IPKernelApp(**kwargs)
260
261
261 # allow IPKernelApp.instance():
262 # allow IPKernelApp.instance():
262 IPKernelApp._instance = app
263 IPKernelApp._instance = app
263
264
264 app.init_connection_file()
265 app.init_connection_file()
265 # relevant contents of init_sockets:
266 # relevant contents of init_sockets:
266
267
267 app.shell_port = app._bind_socket(kernel.shell_streams[0], app.shell_port)
268 app.shell_port = app._bind_socket(kernel.shell_streams[0], app.shell_port)
268 app.log.debug("shell ROUTER Channel on port: %i", app.shell_port)
269 app.log.debug("shell ROUTER Channel on port: %i", app.shell_port)
269
270
270 app.iopub_port = app._bind_socket(kernel.iopub_socket, app.iopub_port)
271 app.iopub_port = app._bind_socket(kernel.iopub_socket, app.iopub_port)
271 app.log.debug("iopub PUB Channel on port: %i", app.iopub_port)
272 app.log.debug("iopub PUB Channel on port: %i", app.iopub_port)
272
273
273 kernel.stdin_socket = self.engine.context.socket(zmq.ROUTER)
274 kernel.stdin_socket = self.engine.context.socket(zmq.ROUTER)
274 app.stdin_port = app._bind_socket(kernel.stdin_socket, app.stdin_port)
275 app.stdin_port = app._bind_socket(kernel.stdin_socket, app.stdin_port)
275 app.log.debug("stdin ROUTER Channel on port: %i", app.stdin_port)
276 app.log.debug("stdin ROUTER Channel on port: %i", app.stdin_port)
276
277
277 # start the heartbeat, and log connection info:
278 # start the heartbeat, and log connection info:
278
279
279 app.init_heartbeat()
280 app.init_heartbeat()
280
281
281 app.log_connection_info()
282 app.log_connection_info()
282 app.write_connection_file()
283 app.write_connection_file()
283
284
284
285
285 def init_engine(self):
286 def init_engine(self):
286 # This is the working dir by now.
287 # This is the working dir by now.
287 sys.path.insert(0, '')
288 sys.path.insert(0, '')
288 config = self.config
289 config = self.config
289 # print config
290 # print config
290 self.find_url_file()
291 self.find_url_file()
291
292
292 # was the url manually specified?
293 # was the url manually specified?
293 keys = set(self.config.EngineFactory.keys())
294 keys = set(self.config.EngineFactory.keys())
294 keys = keys.union(set(self.config.RegistrationFactory.keys()))
295 keys = keys.union(set(self.config.RegistrationFactory.keys()))
295
296
296 if keys.intersection(set(['ip', 'url', 'port'])):
297 if keys.intersection(set(['ip', 'url', 'port'])):
297 # Connection info was specified, don't wait for the file
298 # Connection info was specified, don't wait for the file
298 url_specified = True
299 url_specified = True
299 self.wait_for_url_file = 0
300 self.wait_for_url_file = 0
300 else:
301 else:
301 url_specified = False
302 url_specified = False
302
303
303 if self.wait_for_url_file and not os.path.exists(self.url_file):
304 if self.wait_for_url_file and not os.path.exists(self.url_file):
304 self.log.warn("url_file %r not found", self.url_file)
305 self.log.warn("url_file %r not found", self.url_file)
305 self.log.warn("Waiting up to %.1f seconds for it to arrive.", self.wait_for_url_file)
306 self.log.warn("Waiting up to %.1f seconds for it to arrive.", self.wait_for_url_file)
306 tic = time.time()
307 tic = time.time()
307 while not os.path.exists(self.url_file) and (time.time()-tic < self.wait_for_url_file):
308 while not os.path.exists(self.url_file) and (time.time()-tic < self.wait_for_url_file):
308 # wait for url_file to exist, or until time limit
309 # wait for url_file to exist, or until time limit
309 time.sleep(0.1)
310 time.sleep(0.1)
310
311
311 if os.path.exists(self.url_file):
312 if os.path.exists(self.url_file):
312 self.load_connector_file()
313 self.load_connector_file()
313 elif not url_specified:
314 elif not url_specified:
314 self.log.fatal("Fatal: url file never arrived: %s", self.url_file)
315 self.log.fatal("Fatal: url file never arrived: %s", self.url_file)
315 self.exit(1)
316 self.exit(1)
316
317
317
318
318 try:
319 try:
319 exec_lines = config.IPKernelApp.exec_lines
320 exec_lines = config.IPKernelApp.exec_lines
320 except AttributeError:
321 except AttributeError:
321 try:
322 try:
322 exec_lines = config.InteractiveShellApp.exec_lines
323 exec_lines = config.InteractiveShellApp.exec_lines
323 except AttributeError:
324 except AttributeError:
324 exec_lines = config.IPKernelApp.exec_lines = []
325 exec_lines = config.IPKernelApp.exec_lines = []
325 try:
326 try:
326 exec_files = config.IPKernelApp.exec_files
327 exec_files = config.IPKernelApp.exec_files
327 except AttributeError:
328 except AttributeError:
328 try:
329 try:
329 exec_files = config.InteractiveShellApp.exec_files
330 exec_files = config.InteractiveShellApp.exec_files
330 except AttributeError:
331 except AttributeError:
331 exec_files = config.IPKernelApp.exec_files = []
332 exec_files = config.IPKernelApp.exec_files = []
332
333
333 if self.startup_script:
334 if self.startup_script:
334 exec_files.append(self.startup_script)
335 exec_files.append(self.startup_script)
335 if self.startup_command:
336 if self.startup_command:
336 exec_lines.append(self.startup_command)
337 exec_lines.append(self.startup_command)
337
338
338 # Create the underlying shell class and Engine
339 # Create the underlying shell class and Engine
339 # shell_class = import_item(self.master_config.Global.shell_class)
340 # shell_class = import_item(self.master_config.Global.shell_class)
340 # print self.config
341 # print self.config
341 try:
342 try:
342 self.engine = EngineFactory(config=config, log=self.log,
343 self.engine = EngineFactory(config=config, log=self.log,
343 connection_info=self.connection_info,
344 connection_info=self.connection_info,
344 )
345 )
345 except:
346 except:
346 self.log.error("Couldn't start the Engine", exc_info=True)
347 self.log.error("Couldn't start the Engine", exc_info=True)
347 self.exit(1)
348 self.exit(1)
348
349
349 def forward_logging(self):
350 def forward_logging(self):
350 if self.log_url:
351 if self.log_url:
351 self.log.info("Forwarding logging to %s", self.log_url)
352 self.log.info("Forwarding logging to %s", self.log_url)
352 context = self.engine.context
353 context = self.engine.context
353 lsock = context.socket(zmq.PUB)
354 lsock = context.socket(zmq.PUB)
354 lsock.connect(self.log_url)
355 lsock.connect(self.log_url)
355 handler = EnginePUBHandler(self.engine, lsock)
356 handler = EnginePUBHandler(self.engine, lsock)
356 handler.setLevel(self.log_level)
357 handler.setLevel(self.log_level)
357 self.log.addHandler(handler)
358 self.log.addHandler(handler)
358
359
359 def init_mpi(self):
360 def init_mpi(self):
360 global mpi
361 global mpi
361 self.mpi = MPI(config=self.config)
362 self.mpi = MPI(config=self.config)
362
363
363 mpi_import_statement = self.mpi.init_script
364 mpi_import_statement = self.mpi.init_script
364 if mpi_import_statement:
365 if mpi_import_statement:
365 try:
366 try:
366 self.log.info("Initializing MPI:")
367 self.log.info("Initializing MPI:")
367 self.log.info(mpi_import_statement)
368 self.log.info(mpi_import_statement)
368 exec mpi_import_statement in globals()
369 exec mpi_import_statement in globals()
369 except:
370 except:
370 mpi = None
371 mpi = None
371 else:
372 else:
372 mpi = None
373 mpi = None
373
374
374 @catch_config_error
375 @catch_config_error
375 def initialize(self, argv=None):
376 def initialize(self, argv=None):
376 super(IPEngineApp, self).initialize(argv)
377 super(IPEngineApp, self).initialize(argv)
377 self.init_mpi()
378 self.init_mpi()
378 self.init_engine()
379 self.init_engine()
379 self.forward_logging()
380 self.forward_logging()
380
381
381 def start(self):
382 def start(self):
382 self.engine.start()
383 self.engine.start()
383 try:
384 try:
384 self.engine.loop.start()
385 self.engine.loop.start()
385 except KeyboardInterrupt:
386 except KeyboardInterrupt:
386 self.log.critical("Engine Interrupted, shutting down...\n")
387 self.log.critical("Engine Interrupted, shutting down...\n")
387
388
388
389
389 def launch_new_instance():
390 def launch_new_instance():
390 """Create and run the IPython engine"""
391 """Create and run the IPython engine"""
391 app = IPEngineApp.instance()
392 app = IPEngineApp.instance()
392 app.initialize()
393 app.initialize()
393 app.start()
394 app.start()
394
395
395
396
396 if __name__ == '__main__':
397 if __name__ == '__main__':
397 launch_new_instance()
398 launch_new_instance()
398
399
@@ -1,1345 +1,1354 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Facilities for launching IPython processes asynchronously.
3 Facilities for launching IPython processes asynchronously.
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 * MinRK
8 * MinRK
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import copy
22 import copy
23 import logging
23 import logging
24 import os
24 import os
25 import pipes
25 import pipes
26 import stat
26 import stat
27 import sys
27 import sys
28 import time
28 import time
29
29
30 # signal imports, handling various platforms, versions
30 # signal imports, handling various platforms, versions
31
31
32 from signal import SIGINT, SIGTERM
32 from signal import SIGINT, SIGTERM
33 try:
33 try:
34 from signal import SIGKILL
34 from signal import SIGKILL
35 except ImportError:
35 except ImportError:
36 # Windows
36 # Windows
37 SIGKILL=SIGTERM
37 SIGKILL=SIGTERM
38
38
39 try:
39 try:
40 # Windows >= 2.7, 3.2
40 # Windows >= 2.7, 3.2
41 from signal import CTRL_C_EVENT as SIGINT
41 from signal import CTRL_C_EVENT as SIGINT
42 except ImportError:
42 except ImportError:
43 pass
43 pass
44
44
45 from subprocess import Popen, PIPE, STDOUT
45 from subprocess import Popen, PIPE, STDOUT
46 try:
46 try:
47 from subprocess import check_output
47 from subprocess import check_output
48 except ImportError:
48 except ImportError:
49 # pre-2.7, define check_output with Popen
49 # pre-2.7, define check_output with Popen
50 def check_output(*args, **kwargs):
50 def check_output(*args, **kwargs):
51 kwargs.update(dict(stdout=PIPE))
51 kwargs.update(dict(stdout=PIPE))
52 p = Popen(*args, **kwargs)
52 p = Popen(*args, **kwargs)
53 out,err = p.communicate()
53 out,err = p.communicate()
54 return out
54 return out
55
55
56 from zmq.eventloop import ioloop
56 from zmq.eventloop import ioloop
57
57
58 from IPython.config.application import Application
58 from IPython.config.application import Application
59 from IPython.config.configurable import LoggingConfigurable
59 from IPython.config.configurable import LoggingConfigurable
60 from IPython.utils.text import EvalFormatter
60 from IPython.utils.text import EvalFormatter
61 from IPython.utils.traitlets import (
61 from IPython.utils.traitlets import (
62 Any, Integer, CFloat, List, Unicode, Dict, Instance, HasTraits, CRegExp
62 Any, Integer, CFloat, List, Unicode, Dict, Instance, HasTraits, CRegExp
63 )
63 )
64 from IPython.utils.path import get_home_dir
64 from IPython.utils.path import get_home_dir
65 from IPython.utils.process import find_cmd, FindCmdError
65 from IPython.utils.process import find_cmd, FindCmdError
66
66
67 from .win32support import forward_read_events
67 from .win32support import forward_read_events
68
68
69 from .winhpcjob import IPControllerTask, IPEngineTask, IPControllerJob, IPEngineSetJob
69 from .winhpcjob import IPControllerTask, IPEngineTask, IPControllerJob, IPEngineSetJob
70
70
71 WINDOWS = os.name == 'nt'
71 WINDOWS = os.name == 'nt'
72
72
73 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
74 # Paths to the kernel apps
74 # Paths to the kernel apps
75 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
76
76
77 cmd = "from IPython.parallel.apps.%s import launch_new_instance; launch_new_instance()"
77 cmd = "from IPython.parallel.apps.%s import launch_new_instance; launch_new_instance()"
78
78
79 ipcluster_cmd_argv = [sys.executable, "-c", cmd % "ipclusterapp"]
79 ipcluster_cmd_argv = [sys.executable, "-c", cmd % "ipclusterapp"]
80
80
81 ipengine_cmd_argv = [sys.executable, "-c", cmd % "ipengineapp"]
81 ipengine_cmd_argv = [sys.executable, "-c", cmd % "ipengineapp"]
82
82
83 ipcontroller_cmd_argv = [sys.executable, "-c", cmd % "ipcontrollerapp"]
83 ipcontroller_cmd_argv = [sys.executable, "-c", cmd % "ipcontrollerapp"]
84
84
85 #-----------------------------------------------------------------------------
85 #-----------------------------------------------------------------------------
86 # Base launchers and errors
86 # Base launchers and errors
87 #-----------------------------------------------------------------------------
87 #-----------------------------------------------------------------------------
88
88
89
89
90 class LauncherError(Exception):
90 class LauncherError(Exception):
91 pass
91 pass
92
92
93
93
94 class ProcessStateError(LauncherError):
94 class ProcessStateError(LauncherError):
95 pass
95 pass
96
96
97
97
98 class UnknownStatus(LauncherError):
98 class UnknownStatus(LauncherError):
99 pass
99 pass
100
100
101
101
102 class BaseLauncher(LoggingConfigurable):
102 class BaseLauncher(LoggingConfigurable):
103 """An asbtraction for starting, stopping and signaling a process."""
103 """An asbtraction for starting, stopping and signaling a process."""
104
104
105 # In all of the launchers, the work_dir is where child processes will be
105 # In all of the launchers, the work_dir is where child processes will be
106 # run. This will usually be the profile_dir, but may not be. any work_dir
106 # run. This will usually be the profile_dir, but may not be. any work_dir
107 # passed into the __init__ method will override the config value.
107 # passed into the __init__ method will override the config value.
108 # This should not be used to set the work_dir for the actual engine
108 # This should not be used to set the work_dir for the actual engine
109 # and controller. Instead, use their own config files or the
109 # and controller. Instead, use their own config files or the
110 # controller_args, engine_args attributes of the launchers to add
110 # controller_args, engine_args attributes of the launchers to add
111 # the work_dir option.
111 # the work_dir option.
112 work_dir = Unicode(u'.')
112 work_dir = Unicode(u'.')
113 loop = Instance('zmq.eventloop.ioloop.IOLoop')
113 loop = Instance('zmq.eventloop.ioloop.IOLoop')
114
114
115 start_data = Any()
115 start_data = Any()
116 stop_data = Any()
116 stop_data = Any()
117
117
118 def _loop_default(self):
118 def _loop_default(self):
119 return ioloop.IOLoop.instance()
119 return ioloop.IOLoop.instance()
120
120
121 def __init__(self, work_dir=u'.', config=None, **kwargs):
121 def __init__(self, work_dir=u'.', config=None, **kwargs):
122 super(BaseLauncher, self).__init__(work_dir=work_dir, config=config, **kwargs)
122 super(BaseLauncher, self).__init__(work_dir=work_dir, config=config, **kwargs)
123 self.state = 'before' # can be before, running, after
123 self.state = 'before' # can be before, running, after
124 self.stop_callbacks = []
124 self.stop_callbacks = []
125 self.start_data = None
125 self.start_data = None
126 self.stop_data = None
126 self.stop_data = None
127
127
128 @property
128 @property
129 def args(self):
129 def args(self):
130 """A list of cmd and args that will be used to start the process.
130 """A list of cmd and args that will be used to start the process.
131
131
132 This is what is passed to :func:`spawnProcess` and the first element
132 This is what is passed to :func:`spawnProcess` and the first element
133 will be the process name.
133 will be the process name.
134 """
134 """
135 return self.find_args()
135 return self.find_args()
136
136
137 def find_args(self):
137 def find_args(self):
138 """The ``.args`` property calls this to find the args list.
138 """The ``.args`` property calls this to find the args list.
139
139
140 Subcommand should implement this to construct the cmd and args.
140 Subcommand should implement this to construct the cmd and args.
141 """
141 """
142 raise NotImplementedError('find_args must be implemented in a subclass')
142 raise NotImplementedError('find_args must be implemented in a subclass')
143
143
144 @property
144 @property
145 def arg_str(self):
145 def arg_str(self):
146 """The string form of the program arguments."""
146 """The string form of the program arguments."""
147 return ' '.join(self.args)
147 return ' '.join(self.args)
148
148
149 @property
149 @property
150 def running(self):
150 def running(self):
151 """Am I running."""
151 """Am I running."""
152 if self.state == 'running':
152 if self.state == 'running':
153 return True
153 return True
154 else:
154 else:
155 return False
155 return False
156
156
157 def start(self):
157 def start(self):
158 """Start the process."""
158 """Start the process."""
159 raise NotImplementedError('start must be implemented in a subclass')
159 raise NotImplementedError('start must be implemented in a subclass')
160
160
161 def stop(self):
161 def stop(self):
162 """Stop the process and notify observers of stopping.
162 """Stop the process and notify observers of stopping.
163
163
164 This method will return None immediately.
164 This method will return None immediately.
165 To observe the actual process stopping, see :meth:`on_stop`.
165 To observe the actual process stopping, see :meth:`on_stop`.
166 """
166 """
167 raise NotImplementedError('stop must be implemented in a subclass')
167 raise NotImplementedError('stop must be implemented in a subclass')
168
168
169 def on_stop(self, f):
169 def on_stop(self, f):
170 """Register a callback to be called with this Launcher's stop_data
170 """Register a callback to be called with this Launcher's stop_data
171 when the process actually finishes.
171 when the process actually finishes.
172 """
172 """
173 if self.state=='after':
173 if self.state=='after':
174 return f(self.stop_data)
174 return f(self.stop_data)
175 else:
175 else:
176 self.stop_callbacks.append(f)
176 self.stop_callbacks.append(f)
177
177
178 def notify_start(self, data):
178 def notify_start(self, data):
179 """Call this to trigger startup actions.
179 """Call this to trigger startup actions.
180
180
181 This logs the process startup and sets the state to 'running'. It is
181 This logs the process startup and sets the state to 'running'. It is
182 a pass-through so it can be used as a callback.
182 a pass-through so it can be used as a callback.
183 """
183 """
184
184
185 self.log.debug('Process %r started: %r', self.args[0], data)
185 self.log.debug('Process %r started: %r', self.args[0], data)
186 self.start_data = data
186 self.start_data = data
187 self.state = 'running'
187 self.state = 'running'
188 return data
188 return data
189
189
190 def notify_stop(self, data):
190 def notify_stop(self, data):
191 """Call this to trigger process stop actions.
191 """Call this to trigger process stop actions.
192
192
193 This logs the process stopping and sets the state to 'after'. Call
193 This logs the process stopping and sets the state to 'after'. Call
194 this to trigger callbacks registered via :meth:`on_stop`."""
194 this to trigger callbacks registered via :meth:`on_stop`."""
195
195
196 self.log.debug('Process %r stopped: %r', self.args[0], data)
196 self.log.debug('Process %r stopped: %r', self.args[0], data)
197 self.stop_data = data
197 self.stop_data = data
198 self.state = 'after'
198 self.state = 'after'
199 for i in range(len(self.stop_callbacks)):
199 for i in range(len(self.stop_callbacks)):
200 d = self.stop_callbacks.pop()
200 d = self.stop_callbacks.pop()
201 d(data)
201 d(data)
202 return data
202 return data
203
203
204 def signal(self, sig):
204 def signal(self, sig):
205 """Signal the process.
205 """Signal the process.
206
206
207 Parameters
207 Parameters
208 ----------
208 ----------
209 sig : str or int
209 sig : str or int
210 'KILL', 'INT', etc., or any signal number
210 'KILL', 'INT', etc., or any signal number
211 """
211 """
212 raise NotImplementedError('signal must be implemented in a subclass')
212 raise NotImplementedError('signal must be implemented in a subclass')
213
213
214 class ClusterAppMixin(HasTraits):
214 class ClusterAppMixin(HasTraits):
215 """MixIn for cluster args as traits"""
215 """MixIn for cluster args as traits"""
216 profile_dir=Unicode('')
216 profile_dir=Unicode('')
217 cluster_id=Unicode('')
217 cluster_id=Unicode('')
218
218
219 @property
219 @property
220 def cluster_args(self):
220 def cluster_args(self):
221 return ['--profile-dir', self.profile_dir, '--cluster-id', self.cluster_id]
221 return ['--profile-dir', self.profile_dir, '--cluster-id', self.cluster_id]
222
222
223 class ControllerMixin(ClusterAppMixin):
223 class ControllerMixin(ClusterAppMixin):
224 controller_cmd = List(ipcontroller_cmd_argv, config=True,
224 controller_cmd = List(ipcontroller_cmd_argv, config=True,
225 help="""Popen command to launch ipcontroller.""")
225 help="""Popen command to launch ipcontroller.""")
226 # Command line arguments to ipcontroller.
226 # Command line arguments to ipcontroller.
227 controller_args = List(['--log-to-file','--log-level=%i' % logging.INFO], config=True,
227 controller_args = List(['--log-to-file','--log-level=%i' % logging.INFO], config=True,
228 help="""command-line args to pass to ipcontroller""")
228 help="""command-line args to pass to ipcontroller""")
229
229
230 class EngineMixin(ClusterAppMixin):
230 class EngineMixin(ClusterAppMixin):
231 engine_cmd = List(ipengine_cmd_argv, config=True,
231 engine_cmd = List(ipengine_cmd_argv, config=True,
232 help="""command to launch the Engine.""")
232 help="""command to launch the Engine.""")
233 # Command line arguments for ipengine.
233 # Command line arguments for ipengine.
234 engine_args = List(['--log-to-file','--log-level=%i' % logging.INFO], config=True,
234 engine_args = List(['--log-to-file','--log-level=%i' % logging.INFO], config=True,
235 help="command-line arguments to pass to ipengine"
235 help="command-line arguments to pass to ipengine"
236 )
236 )
237
237
238
238
239 #-----------------------------------------------------------------------------
239 #-----------------------------------------------------------------------------
240 # Local process launchers
240 # Local process launchers
241 #-----------------------------------------------------------------------------
241 #-----------------------------------------------------------------------------
242
242
243
243
244 class LocalProcessLauncher(BaseLauncher):
244 class LocalProcessLauncher(BaseLauncher):
245 """Start and stop an external process in an asynchronous manner.
245 """Start and stop an external process in an asynchronous manner.
246
246
247 This will launch the external process with a working directory of
247 This will launch the external process with a working directory of
248 ``self.work_dir``.
248 ``self.work_dir``.
249 """
249 """
250
250
251 # This is used to to construct self.args, which is passed to
251 # This is used to to construct self.args, which is passed to
252 # spawnProcess.
252 # spawnProcess.
253 cmd_and_args = List([])
253 cmd_and_args = List([])
254 poll_frequency = Integer(100) # in ms
254 poll_frequency = Integer(100) # in ms
255
255
256 def __init__(self, work_dir=u'.', config=None, **kwargs):
256 def __init__(self, work_dir=u'.', config=None, **kwargs):
257 super(LocalProcessLauncher, self).__init__(
257 super(LocalProcessLauncher, self).__init__(
258 work_dir=work_dir, config=config, **kwargs
258 work_dir=work_dir, config=config, **kwargs
259 )
259 )
260 self.process = None
260 self.process = None
261 self.poller = None
261 self.poller = None
262
262
263 def find_args(self):
263 def find_args(self):
264 return self.cmd_and_args
264 return self.cmd_and_args
265
265
266 def start(self):
266 def start(self):
267 self.log.debug("Starting %s: %r", self.__class__.__name__, self.args)
267 self.log.debug("Starting %s: %r", self.__class__.__name__, self.args)
268 if self.state == 'before':
268 if self.state == 'before':
269 self.process = Popen(self.args,
269 self.process = Popen(self.args,
270 stdout=PIPE,stderr=PIPE,stdin=PIPE,
270 stdout=PIPE,stderr=PIPE,stdin=PIPE,
271 env=os.environ,
271 env=os.environ,
272 cwd=self.work_dir
272 cwd=self.work_dir
273 )
273 )
274 if WINDOWS:
274 if WINDOWS:
275 self.stdout = forward_read_events(self.process.stdout)
275 self.stdout = forward_read_events(self.process.stdout)
276 self.stderr = forward_read_events(self.process.stderr)
276 self.stderr = forward_read_events(self.process.stderr)
277 else:
277 else:
278 self.stdout = self.process.stdout.fileno()
278 self.stdout = self.process.stdout.fileno()
279 self.stderr = self.process.stderr.fileno()
279 self.stderr = self.process.stderr.fileno()
280 self.loop.add_handler(self.stdout, self.handle_stdout, self.loop.READ)
280 self.loop.add_handler(self.stdout, self.handle_stdout, self.loop.READ)
281 self.loop.add_handler(self.stderr, self.handle_stderr, self.loop.READ)
281 self.loop.add_handler(self.stderr, self.handle_stderr, self.loop.READ)
282 self.poller = ioloop.PeriodicCallback(self.poll, self.poll_frequency, self.loop)
282 self.poller = ioloop.PeriodicCallback(self.poll, self.poll_frequency, self.loop)
283 self.poller.start()
283 self.poller.start()
284 self.notify_start(self.process.pid)
284 self.notify_start(self.process.pid)
285 else:
285 else:
286 s = 'The process was already started and has state: %r' % self.state
286 s = 'The process was already started and has state: %r' % self.state
287 raise ProcessStateError(s)
287 raise ProcessStateError(s)
288
288
289 def stop(self):
289 def stop(self):
290 return self.interrupt_then_kill()
290 return self.interrupt_then_kill()
291
291
292 def signal(self, sig):
292 def signal(self, sig):
293 if self.state == 'running':
293 if self.state == 'running':
294 if WINDOWS and sig != SIGINT:
294 if WINDOWS and sig != SIGINT:
295 # use Windows tree-kill for better child cleanup
295 # use Windows tree-kill for better child cleanup
296 check_output(['taskkill', '-pid', str(self.process.pid), '-t', '-f'])
296 check_output(['taskkill', '-pid', str(self.process.pid), '-t', '-f'])
297 else:
297 else:
298 self.process.send_signal(sig)
298 self.process.send_signal(sig)
299
299
300 def interrupt_then_kill(self, delay=2.0):
300 def interrupt_then_kill(self, delay=2.0):
301 """Send INT, wait a delay and then send KILL."""
301 """Send INT, wait a delay and then send KILL."""
302 try:
302 try:
303 self.signal(SIGINT)
303 self.signal(SIGINT)
304 except Exception:
304 except Exception:
305 self.log.debug("interrupt failed")
305 self.log.debug("interrupt failed")
306 pass
306 pass
307 self.killer = ioloop.DelayedCallback(lambda : self.signal(SIGKILL), delay*1000, self.loop)
307 self.killer = ioloop.DelayedCallback(lambda : self.signal(SIGKILL), delay*1000, self.loop)
308 self.killer.start()
308 self.killer.start()
309
309
310 # callbacks, etc:
310 # callbacks, etc:
311
311
312 def handle_stdout(self, fd, events):
312 def handle_stdout(self, fd, events):
313 if WINDOWS:
313 if WINDOWS:
314 line = self.stdout.recv()
314 line = self.stdout.recv()
315 else:
315 else:
316 line = self.process.stdout.readline()
316 line = self.process.stdout.readline()
317 # a stopped process will be readable but return empty strings
317 # a stopped process will be readable but return empty strings
318 if line:
318 if line:
319 self.log.debug(line[:-1])
319 self.log.debug(line[:-1])
320 else:
320 else:
321 self.poll()
321 self.poll()
322
322
323 def handle_stderr(self, fd, events):
323 def handle_stderr(self, fd, events):
324 if WINDOWS:
324 if WINDOWS:
325 line = self.stderr.recv()
325 line = self.stderr.recv()
326 else:
326 else:
327 line = self.process.stderr.readline()
327 line = self.process.stderr.readline()
328 # a stopped process will be readable but return empty strings
328 # a stopped process will be readable but return empty strings
329 if line:
329 if line:
330 self.log.debug(line[:-1])
330 self.log.debug(line[:-1])
331 else:
331 else:
332 self.poll()
332 self.poll()
333
333
334 def poll(self):
334 def poll(self):
335 status = self.process.poll()
335 status = self.process.poll()
336 if status is not None:
336 if status is not None:
337 self.poller.stop()
337 self.poller.stop()
338 self.loop.remove_handler(self.stdout)
338 self.loop.remove_handler(self.stdout)
339 self.loop.remove_handler(self.stderr)
339 self.loop.remove_handler(self.stderr)
340 self.notify_stop(dict(exit_code=status, pid=self.process.pid))
340 self.notify_stop(dict(exit_code=status, pid=self.process.pid))
341 return status
341 return status
342
342
343 class LocalControllerLauncher(LocalProcessLauncher, ControllerMixin):
343 class LocalControllerLauncher(LocalProcessLauncher, ControllerMixin):
344 """Launch a controller as a regular external process."""
344 """Launch a controller as a regular external process."""
345
345
346 def find_args(self):
346 def find_args(self):
347 return self.controller_cmd + self.cluster_args + self.controller_args
347 return self.controller_cmd + self.cluster_args + self.controller_args
348
348
349 def start(self):
349 def start(self):
350 """Start the controller by profile_dir."""
350 """Start the controller by profile_dir."""
351 return super(LocalControllerLauncher, self).start()
351 return super(LocalControllerLauncher, self).start()
352
352
353
353
354 class LocalEngineLauncher(LocalProcessLauncher, EngineMixin):
354 class LocalEngineLauncher(LocalProcessLauncher, EngineMixin):
355 """Launch a single engine as a regular externall process."""
355 """Launch a single engine as a regular externall process."""
356
356
357 def find_args(self):
357 def find_args(self):
358 return self.engine_cmd + self.cluster_args + self.engine_args
358 return self.engine_cmd + self.cluster_args + self.engine_args
359
359
360
360
361 class LocalEngineSetLauncher(LocalEngineLauncher):
361 class LocalEngineSetLauncher(LocalEngineLauncher):
362 """Launch a set of engines as regular external processes."""
362 """Launch a set of engines as regular external processes."""
363
363
364 delay = CFloat(0.1, config=True,
364 delay = CFloat(0.1, config=True,
365 help="""delay (in seconds) between starting each engine after the first.
365 help="""delay (in seconds) between starting each engine after the first.
366 This can help force the engines to get their ids in order, or limit
366 This can help force the engines to get their ids in order, or limit
367 process flood when starting many engines."""
367 process flood when starting many engines."""
368 )
368 )
369
369
370 # launcher class
370 # launcher class
371 launcher_class = LocalEngineLauncher
371 launcher_class = LocalEngineLauncher
372
372
373 launchers = Dict()
373 launchers = Dict()
374 stop_data = Dict()
374 stop_data = Dict()
375
375
376 def __init__(self, work_dir=u'.', config=None, **kwargs):
376 def __init__(self, work_dir=u'.', config=None, **kwargs):
377 super(LocalEngineSetLauncher, self).__init__(
377 super(LocalEngineSetLauncher, self).__init__(
378 work_dir=work_dir, config=config, **kwargs
378 work_dir=work_dir, config=config, **kwargs
379 )
379 )
380 self.stop_data = {}
380 self.stop_data = {}
381
381
382 def start(self, n):
382 def start(self, n):
383 """Start n engines by profile or profile_dir."""
383 """Start n engines by profile or profile_dir."""
384 dlist = []
384 dlist = []
385 for i in range(n):
385 for i in range(n):
386 if i > 0:
386 if i > 0:
387 time.sleep(self.delay)
387 time.sleep(self.delay)
388 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
388 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
389 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
389 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
390 )
390 )
391
391
392 # Copy the engine args over to each engine launcher.
392 # Copy the engine args over to each engine launcher.
393 el.engine_cmd = copy.deepcopy(self.engine_cmd)
393 el.engine_cmd = copy.deepcopy(self.engine_cmd)
394 el.engine_args = copy.deepcopy(self.engine_args)
394 el.engine_args = copy.deepcopy(self.engine_args)
395 el.on_stop(self._notice_engine_stopped)
395 el.on_stop(self._notice_engine_stopped)
396 d = el.start()
396 d = el.start()
397 self.launchers[i] = el
397 self.launchers[i] = el
398 dlist.append(d)
398 dlist.append(d)
399 self.notify_start(dlist)
399 self.notify_start(dlist)
400 return dlist
400 return dlist
401
401
402 def find_args(self):
402 def find_args(self):
403 return ['engine set']
403 return ['engine set']
404
404
405 def signal(self, sig):
405 def signal(self, sig):
406 dlist = []
406 dlist = []
407 for el in self.launchers.itervalues():
407 for el in self.launchers.itervalues():
408 d = el.signal(sig)
408 d = el.signal(sig)
409 dlist.append(d)
409 dlist.append(d)
410 return dlist
410 return dlist
411
411
412 def interrupt_then_kill(self, delay=1.0):
412 def interrupt_then_kill(self, delay=1.0):
413 dlist = []
413 dlist = []
414 for el in self.launchers.itervalues():
414 for el in self.launchers.itervalues():
415 d = el.interrupt_then_kill(delay)
415 d = el.interrupt_then_kill(delay)
416 dlist.append(d)
416 dlist.append(d)
417 return dlist
417 return dlist
418
418
419 def stop(self):
419 def stop(self):
420 return self.interrupt_then_kill()
420 return self.interrupt_then_kill()
421
421
422 def _notice_engine_stopped(self, data):
422 def _notice_engine_stopped(self, data):
423 pid = data['pid']
423 pid = data['pid']
424 for idx,el in self.launchers.iteritems():
424 for idx,el in self.launchers.iteritems():
425 if el.process.pid == pid:
425 if el.process.pid == pid:
426 break
426 break
427 self.launchers.pop(idx)
427 self.launchers.pop(idx)
428 self.stop_data[idx] = data
428 self.stop_data[idx] = data
429 if not self.launchers:
429 if not self.launchers:
430 self.notify_stop(self.stop_data)
430 self.notify_stop(self.stop_data)
431
431
432
432
433 #-----------------------------------------------------------------------------
433 #-----------------------------------------------------------------------------
434 # MPI launchers
434 # MPI launchers
435 #-----------------------------------------------------------------------------
435 #-----------------------------------------------------------------------------
436
436
437
437
438 class MPILauncher(LocalProcessLauncher):
438 class MPILauncher(LocalProcessLauncher):
439 """Launch an external process using mpiexec."""
439 """Launch an external process using mpiexec."""
440
440
441 mpi_cmd = List(['mpiexec'], config=True,
441 mpi_cmd = List(['mpiexec'], config=True,
442 help="The mpiexec command to use in starting the process."
442 help="The mpiexec command to use in starting the process."
443 )
443 )
444 mpi_args = List([], config=True,
444 mpi_args = List([], config=True,
445 help="The command line arguments to pass to mpiexec."
445 help="The command line arguments to pass to mpiexec."
446 )
446 )
447 program = List(['date'],
447 program = List(['date'],
448 help="The program to start via mpiexec.")
448 help="The program to start via mpiexec.")
449 program_args = List([],
449 program_args = List([],
450 help="The command line argument to the program."
450 help="The command line argument to the program."
451 )
451 )
452 n = Integer(1)
452 n = Integer(1)
453
453
454 def __init__(self, *args, **kwargs):
454 def __init__(self, *args, **kwargs):
455 # deprecation for old MPIExec names:
455 # deprecation for old MPIExec names:
456 config = kwargs.get('config', {})
456 config = kwargs.get('config', {})
457 for oldname in ('MPIExecLauncher', 'MPIExecControllerLauncher', 'MPIExecEngineSetLauncher'):
457 for oldname in ('MPIExecLauncher', 'MPIExecControllerLauncher', 'MPIExecEngineSetLauncher'):
458 deprecated = config.get(oldname)
458 deprecated = config.get(oldname)
459 if deprecated:
459 if deprecated:
460 newname = oldname.replace('MPIExec', 'MPI')
460 newname = oldname.replace('MPIExec', 'MPI')
461 config[newname].update(deprecated)
461 config[newname].update(deprecated)
462 self.log.warn("WARNING: %s name has been deprecated, use %s", oldname, newname)
462 self.log.warn("WARNING: %s name has been deprecated, use %s", oldname, newname)
463
463
464 super(MPILauncher, self).__init__(*args, **kwargs)
464 super(MPILauncher, self).__init__(*args, **kwargs)
465
465
466 def find_args(self):
466 def find_args(self):
467 """Build self.args using all the fields."""
467 """Build self.args using all the fields."""
468 return self.mpi_cmd + ['-n', str(self.n)] + self.mpi_args + \
468 return self.mpi_cmd + ['-n', str(self.n)] + self.mpi_args + \
469 self.program + self.program_args
469 self.program + self.program_args
470
470
471 def start(self, n):
471 def start(self, n):
472 """Start n instances of the program using mpiexec."""
472 """Start n instances of the program using mpiexec."""
473 self.n = n
473 self.n = n
474 return super(MPILauncher, self).start()
474 return super(MPILauncher, self).start()
475
475
476
476
477 class MPIControllerLauncher(MPILauncher, ControllerMixin):
477 class MPIControllerLauncher(MPILauncher, ControllerMixin):
478 """Launch a controller using mpiexec."""
478 """Launch a controller using mpiexec."""
479
479
480 # alias back to *non-configurable* program[_args] for use in find_args()
480 # alias back to *non-configurable* program[_args] for use in find_args()
481 # this way all Controller/EngineSetLaunchers have the same form, rather
481 # this way all Controller/EngineSetLaunchers have the same form, rather
482 # than *some* having `program_args` and others `controller_args`
482 # than *some* having `program_args` and others `controller_args`
483 @property
483 @property
484 def program(self):
484 def program(self):
485 return self.controller_cmd
485 return self.controller_cmd
486
486
487 @property
487 @property
488 def program_args(self):
488 def program_args(self):
489 return self.cluster_args + self.controller_args
489 return self.cluster_args + self.controller_args
490
490
491 def start(self):
491 def start(self):
492 """Start the controller by profile_dir."""
492 """Start the controller by profile_dir."""
493 return super(MPIControllerLauncher, self).start(1)
493 return super(MPIControllerLauncher, self).start(1)
494
494
495
495
496 class MPIEngineSetLauncher(MPILauncher, EngineMixin):
496 class MPIEngineSetLauncher(MPILauncher, EngineMixin):
497 """Launch engines using mpiexec"""
497 """Launch engines using mpiexec"""
498
498
499 # alias back to *non-configurable* program[_args] for use in find_args()
499 # alias back to *non-configurable* program[_args] for use in find_args()
500 # this way all Controller/EngineSetLaunchers have the same form, rather
500 # this way all Controller/EngineSetLaunchers have the same form, rather
501 # than *some* having `program_args` and others `controller_args`
501 # than *some* having `program_args` and others `controller_args`
502 @property
502 @property
503 def program(self):
503 def program(self):
504 return self.engine_cmd
504 return self.engine_cmd
505
505
506 @property
506 @property
507 def program_args(self):
507 def program_args(self):
508 return self.cluster_args + self.engine_args
508 return self.cluster_args + self.engine_args
509
509
510 def start(self, n):
510 def start(self, n):
511 """Start n engines by profile or profile_dir."""
511 """Start n engines by profile or profile_dir."""
512 self.n = n
512 self.n = n
513 return super(MPIEngineSetLauncher, self).start(n)
513 return super(MPIEngineSetLauncher, self).start(n)
514
514
515 # deprecated MPIExec names
515 # deprecated MPIExec names
516 class DeprecatedMPILauncher(object):
516 class DeprecatedMPILauncher(object):
517 def warn(self):
517 def warn(self):
518 oldname = self.__class__.__name__
518 oldname = self.__class__.__name__
519 newname = oldname.replace('MPIExec', 'MPI')
519 newname = oldname.replace('MPIExec', 'MPI')
520 self.log.warn("WARNING: %s name is deprecated, use %s", oldname, newname)
520 self.log.warn("WARNING: %s name is deprecated, use %s", oldname, newname)
521
521
522 class MPIExecLauncher(MPILauncher, DeprecatedMPILauncher):
522 class MPIExecLauncher(MPILauncher, DeprecatedMPILauncher):
523 """Deprecated, use MPILauncher"""
523 """Deprecated, use MPILauncher"""
524 def __init__(self, *args, **kwargs):
524 def __init__(self, *args, **kwargs):
525 super(MPIExecLauncher, self).__init__(*args, **kwargs)
525 super(MPIExecLauncher, self).__init__(*args, **kwargs)
526 self.warn()
526 self.warn()
527
527
528 class MPIExecControllerLauncher(MPIControllerLauncher, DeprecatedMPILauncher):
528 class MPIExecControllerLauncher(MPIControllerLauncher, DeprecatedMPILauncher):
529 """Deprecated, use MPIControllerLauncher"""
529 """Deprecated, use MPIControllerLauncher"""
530 def __init__(self, *args, **kwargs):
530 def __init__(self, *args, **kwargs):
531 super(MPIExecControllerLauncher, self).__init__(*args, **kwargs)
531 super(MPIExecControllerLauncher, self).__init__(*args, **kwargs)
532 self.warn()
532 self.warn()
533
533
534 class MPIExecEngineSetLauncher(MPIEngineSetLauncher, DeprecatedMPILauncher):
534 class MPIExecEngineSetLauncher(MPIEngineSetLauncher, DeprecatedMPILauncher):
535 """Deprecated, use MPIEngineSetLauncher"""
535 """Deprecated, use MPIEngineSetLauncher"""
536 def __init__(self, *args, **kwargs):
536 def __init__(self, *args, **kwargs):
537 super(MPIExecEngineSetLauncher, self).__init__(*args, **kwargs)
537 super(MPIExecEngineSetLauncher, self).__init__(*args, **kwargs)
538 self.warn()
538 self.warn()
539
539
540
540
541 #-----------------------------------------------------------------------------
541 #-----------------------------------------------------------------------------
542 # SSH launchers
542 # SSH launchers
543 #-----------------------------------------------------------------------------
543 #-----------------------------------------------------------------------------
544
544
545 # TODO: Get SSH Launcher back to level of sshx in 0.10.2
545 # TODO: Get SSH Launcher back to level of sshx in 0.10.2
546
546
547 class SSHLauncher(LocalProcessLauncher):
547 class SSHLauncher(LocalProcessLauncher):
548 """A minimal launcher for ssh.
548 """A minimal launcher for ssh.
549
549
550 To be useful this will probably have to be extended to use the ``sshx``
550 To be useful this will probably have to be extended to use the ``sshx``
551 idea for environment variables. There could be other things this needs
551 idea for environment variables. There could be other things this needs
552 as well.
552 as well.
553 """
553 """
554
554
555 ssh_cmd = List(['ssh'], config=True,
555 ssh_cmd = List(['ssh'], config=True,
556 help="command for starting ssh")
556 help="command for starting ssh")
557 ssh_args = List(['-tt'], config=True,
557 ssh_args = List(['-tt'], config=True,
558 help="args to pass to ssh")
558 help="args to pass to ssh")
559 scp_cmd = List(['scp'], config=True,
559 scp_cmd = List(['scp'], config=True,
560 help="command for sending files")
560 help="command for sending files")
561 program = List(['date'],
561 program = List(['date'],
562 help="Program to launch via ssh")
562 help="Program to launch via ssh")
563 program_args = List([],
563 program_args = List([],
564 help="args to pass to remote program")
564 help="args to pass to remote program")
565 hostname = Unicode('', config=True,
565 hostname = Unicode('', config=True,
566 help="hostname on which to launch the program")
566 help="hostname on which to launch the program")
567 user = Unicode('', config=True,
567 user = Unicode('', config=True,
568 help="username for ssh")
568 help="username for ssh")
569 location = Unicode('', config=True,
569 location = Unicode('', config=True,
570 help="user@hostname location for ssh in one setting")
570 help="user@hostname location for ssh in one setting")
571 to_fetch = List([], config=True,
571 to_fetch = List([], config=True,
572 help="List of (remote, local) files to fetch after starting")
572 help="List of (remote, local) files to fetch after starting")
573 to_send = List([], config=True,
573 to_send = List([], config=True,
574 help="List of (local, remote) files to send before starting")
574 help="List of (local, remote) files to send before starting")
575
575
576 def _hostname_changed(self, name, old, new):
576 def _hostname_changed(self, name, old, new):
577 if self.user:
577 if self.user:
578 self.location = u'%s@%s' % (self.user, new)
578 self.location = u'%s@%s' % (self.user, new)
579 else:
579 else:
580 self.location = new
580 self.location = new
581
581
582 def _user_changed(self, name, old, new):
582 def _user_changed(self, name, old, new):
583 self.location = u'%s@%s' % (new, self.hostname)
583 self.location = u'%s@%s' % (new, self.hostname)
584
584
585 def find_args(self):
585 def find_args(self):
586 return self.ssh_cmd + self.ssh_args + [self.location] + \
586 return self.ssh_cmd + self.ssh_args + [self.location] + \
587 list(map(pipes.quote, self.program + self.program_args))
587 list(map(pipes.quote, self.program + self.program_args))
588
588
589 def _send_file(self, local, remote):
589 def _send_file(self, local, remote):
590 """send a single file"""
590 """send a single file"""
591 remote = "%s:%s" % (self.location, remote)
591 remote = "%s:%s" % (self.location, remote)
592 for i in range(10):
592 for i in range(10):
593 if not os.path.exists(local):
593 if not os.path.exists(local):
594 self.log.debug("waiting for %s" % local)
594 self.log.debug("waiting for %s" % local)
595 time.sleep(1)
595 time.sleep(1)
596 else:
596 else:
597 break
597 break
598 self.log.info("sending %s to %s", local, remote)
598 self.log.info("sending %s to %s", local, remote)
599 check_output(self.scp_cmd + [local, remote])
599 check_output(self.scp_cmd + [local, remote])
600
600
601 def send_files(self):
601 def send_files(self):
602 """send our files (called before start)"""
602 """send our files (called before start)"""
603 if not self.to_send:
603 if not self.to_send:
604 return
604 return
605 for local_file, remote_file in self.to_send:
605 for local_file, remote_file in self.to_send:
606 self._send_file(local_file, remote_file)
606 self._send_file(local_file, remote_file)
607
607
608 def _fetch_file(self, remote, local):
608 def _fetch_file(self, remote, local):
609 """fetch a single file"""
609 """fetch a single file"""
610 full_remote = "%s:%s" % (self.location, remote)
610 full_remote = "%s:%s" % (self.location, remote)
611 self.log.info("fetching %s from %s", local, full_remote)
611 self.log.info("fetching %s from %s", local, full_remote)
612 for i in range(10):
612 for i in range(10):
613 # wait up to 10s for remote file to exist
613 # wait up to 10s for remote file to exist
614 check = check_output(self.ssh_cmd + self.ssh_args + \
614 check = check_output(self.ssh_cmd + self.ssh_args + \
615 [self.location, 'test -e', remote, "&& echo 'yes' || echo 'no'"])
615 [self.location, 'test -e', remote, "&& echo 'yes' || echo 'no'"])
616 check = check.strip()
616 check = check.strip()
617 if check == 'no':
617 if check == 'no':
618 time.sleep(1)
618 time.sleep(1)
619 elif check == 'yes':
619 elif check == 'yes':
620 break
620 break
621 check_output(self.scp_cmd + [full_remote, local])
621 check_output(self.scp_cmd + [full_remote, local])
622
622
623 def fetch_files(self):
623 def fetch_files(self):
624 """fetch remote files (called after start)"""
624 """fetch remote files (called after start)"""
625 if not self.to_fetch:
625 if not self.to_fetch:
626 return
626 return
627 for remote_file, local_file in self.to_fetch:
627 for remote_file, local_file in self.to_fetch:
628 self._fetch_file(remote_file, local_file)
628 self._fetch_file(remote_file, local_file)
629
629
630 def start(self, hostname=None, user=None):
630 def start(self, hostname=None, user=None):
631 if hostname is not None:
631 if hostname is not None:
632 self.hostname = hostname
632 self.hostname = hostname
633 if user is not None:
633 if user is not None:
634 self.user = user
634 self.user = user
635
635
636 self.send_files()
636 self.send_files()
637 super(SSHLauncher, self).start()
637 super(SSHLauncher, self).start()
638 self.fetch_files()
638 self.fetch_files()
639
639
640 def signal(self, sig):
640 def signal(self, sig):
641 if self.state == 'running':
641 if self.state == 'running':
642 # send escaped ssh connection-closer
642 # send escaped ssh connection-closer
643 self.process.stdin.write('~.')
643 self.process.stdin.write('~.')
644 self.process.stdin.flush()
644 self.process.stdin.flush()
645
645
646 class SSHClusterLauncher(SSHLauncher):
646 class SSHClusterLauncher(SSHLauncher):
647
647
648 remote_profile_dir = Unicode('', config=True,
648 remote_profile_dir = Unicode('', config=True,
649 help="""The remote profile_dir to use.
649 help="""The remote profile_dir to use.
650
650
651 If not specified, use calling profile, stripping out possible leading homedir.
651 If not specified, use calling profile, stripping out possible leading homedir.
652 """)
652 """)
653
653
654 def _remote_profile_dir_default(self):
654 def _profile_dir_changed(self, name, old, new):
655 """turns /home/you/.ipython/profile_foo into .ipython/profile_foo
655 if not self.remote_profile_dir:
656 """
656 # trigger remote_profile_dir_default logic again,
657 # in case it was already triggered before profile_dir was set
658 self.remote_profile_dir = self._strip_home(new)
659
660 @staticmethod
661 def _strip_home(path):
662 """turns /home/you/.ipython/profile_foo into .ipython/profile_foo"""
657 home = get_home_dir()
663 home = get_home_dir()
658 if not home.endswith('/'):
664 if not home.endswith('/'):
659 home = home+'/'
665 home = home+'/'
660
666
661 if self.profile_dir.startswith(home):
667 if path.startswith(home):
662 return self.profile_dir[len(home):]
668 return path[len(home):]
663 else:
669 else:
664 return self.profile_dir
670 return path
671
672 def _remote_profile_dir_default(self):
673 return self._strip_home(self.profile_dir)
665
674
666 def _cluster_id_changed(self, name, old, new):
675 def _cluster_id_changed(self, name, old, new):
667 if new:
676 if new:
668 raise ValueError("cluster id not supported by SSH launchers")
677 raise ValueError("cluster id not supported by SSH launchers")
669
678
670 @property
679 @property
671 def cluster_args(self):
680 def cluster_args(self):
672 return ['--profile-dir', self.remote_profile_dir]
681 return ['--profile-dir', self.remote_profile_dir]
673
682
674 class SSHControllerLauncher(SSHClusterLauncher, ControllerMixin):
683 class SSHControllerLauncher(SSHClusterLauncher, ControllerMixin):
675
684
676 # alias back to *non-configurable* program[_args] for use in find_args()
685 # alias back to *non-configurable* program[_args] for use in find_args()
677 # this way all Controller/EngineSetLaunchers have the same form, rather
686 # this way all Controller/EngineSetLaunchers have the same form, rather
678 # than *some* having `program_args` and others `controller_args`
687 # than *some* having `program_args` and others `controller_args`
679
688
680 def _controller_cmd_default(self):
689 def _controller_cmd_default(self):
681 return ['ipcontroller']
690 return ['ipcontroller']
682
691
683 @property
692 @property
684 def program(self):
693 def program(self):
685 return self.controller_cmd
694 return self.controller_cmd
686
695
687 @property
696 @property
688 def program_args(self):
697 def program_args(self):
689 return self.cluster_args + self.controller_args
698 return self.cluster_args + self.controller_args
690
699
691 def _to_fetch_default(self):
700 def _to_fetch_default(self):
692 return [
701 return [
693 (os.path.join(self.remote_profile_dir, 'security', cf),
702 (os.path.join(self.remote_profile_dir, 'security', cf),
694 os.path.join(self.profile_dir, 'security', cf),)
703 os.path.join(self.profile_dir, 'security', cf),)
695 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
704 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
696 ]
705 ]
697
706
698 class SSHEngineLauncher(SSHClusterLauncher, EngineMixin):
707 class SSHEngineLauncher(SSHClusterLauncher, EngineMixin):
699
708
700 # alias back to *non-configurable* program[_args] for use in find_args()
709 # alias back to *non-configurable* program[_args] for use in find_args()
701 # this way all Controller/EngineSetLaunchers have the same form, rather
710 # this way all Controller/EngineSetLaunchers have the same form, rather
702 # than *some* having `program_args` and others `controller_args`
711 # than *some* having `program_args` and others `controller_args`
703
712
704 def _engine_cmd_default(self):
713 def _engine_cmd_default(self):
705 return ['ipengine']
714 return ['ipengine']
706
715
707 @property
716 @property
708 def program(self):
717 def program(self):
709 return self.engine_cmd
718 return self.engine_cmd
710
719
711 @property
720 @property
712 def program_args(self):
721 def program_args(self):
713 return self.cluster_args + self.engine_args
722 return self.cluster_args + self.engine_args
714
723
715 def _to_send_default(self):
724 def _to_send_default(self):
716 return [
725 return [
717 (os.path.join(self.profile_dir, 'security', cf),
726 (os.path.join(self.profile_dir, 'security', cf),
718 os.path.join(self.remote_profile_dir, 'security', cf))
727 os.path.join(self.remote_profile_dir, 'security', cf))
719 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
728 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
720 ]
729 ]
721
730
722
731
723 class SSHEngineSetLauncher(LocalEngineSetLauncher):
732 class SSHEngineSetLauncher(LocalEngineSetLauncher):
724 launcher_class = SSHEngineLauncher
733 launcher_class = SSHEngineLauncher
725 engines = Dict(config=True,
734 engines = Dict(config=True,
726 help="""dict of engines to launch. This is a dict by hostname of ints,
735 help="""dict of engines to launch. This is a dict by hostname of ints,
727 corresponding to the number of engines to start on that host.""")
736 corresponding to the number of engines to start on that host.""")
728
737
729 def _engine_cmd_default(self):
738 def _engine_cmd_default(self):
730 return ['ipengine']
739 return ['ipengine']
731
740
732 @property
741 @property
733 def engine_count(self):
742 def engine_count(self):
734 """determine engine count from `engines` dict"""
743 """determine engine count from `engines` dict"""
735 count = 0
744 count = 0
736 for n in self.engines.itervalues():
745 for n in self.engines.itervalues():
737 if isinstance(n, (tuple,list)):
746 if isinstance(n, (tuple,list)):
738 n,args = n
747 n,args = n
739 count += n
748 count += n
740 return count
749 return count
741
750
742 def start(self, n):
751 def start(self, n):
743 """Start engines by profile or profile_dir.
752 """Start engines by profile or profile_dir.
744 `n` is ignored, and the `engines` config property is used instead.
753 `n` is ignored, and the `engines` config property is used instead.
745 """
754 """
746
755
747 dlist = []
756 dlist = []
748 for host, n in self.engines.iteritems():
757 for host, n in self.engines.iteritems():
749 if isinstance(n, (tuple, list)):
758 if isinstance(n, (tuple, list)):
750 n, args = n
759 n, args = n
751 else:
760 else:
752 args = copy.deepcopy(self.engine_args)
761 args = copy.deepcopy(self.engine_args)
753
762
754 if '@' in host:
763 if '@' in host:
755 user,host = host.split('@',1)
764 user,host = host.split('@',1)
756 else:
765 else:
757 user=None
766 user=None
758 for i in range(n):
767 for i in range(n):
759 if i > 0:
768 if i > 0:
760 time.sleep(self.delay)
769 time.sleep(self.delay)
761 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
770 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
762 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
771 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
763 )
772 )
764 if i > 0:
773 if i > 0:
765 # only send files for the first engine on each host
774 # only send files for the first engine on each host
766 el.to_send = []
775 el.to_send = []
767
776
768 # Copy the engine args over to each engine launcher.
777 # Copy the engine args over to each engine launcher.
769 el.engine_cmd = self.engine_cmd
778 el.engine_cmd = self.engine_cmd
770 el.engine_args = args
779 el.engine_args = args
771 el.on_stop(self._notice_engine_stopped)
780 el.on_stop(self._notice_engine_stopped)
772 d = el.start(user=user, hostname=host)
781 d = el.start(user=user, hostname=host)
773 self.launchers[ "%s/%i" % (host,i) ] = el
782 self.launchers[ "%s/%i" % (host,i) ] = el
774 dlist.append(d)
783 dlist.append(d)
775 self.notify_start(dlist)
784 self.notify_start(dlist)
776 return dlist
785 return dlist
777
786
778
787
779 class SSHProxyEngineSetLauncher(SSHClusterLauncher):
788 class SSHProxyEngineSetLauncher(SSHClusterLauncher):
780 """Launcher for calling
789 """Launcher for calling
781 `ipcluster engines` on a remote machine.
790 `ipcluster engines` on a remote machine.
782
791
783 Requires that remote profile is already configured.
792 Requires that remote profile is already configured.
784 """
793 """
785
794
786 n = Integer()
795 n = Integer()
787 ipcluster_cmd = List(['ipcluster'], config=True)
796 ipcluster_cmd = List(['ipcluster'], config=True)
788
797
789 @property
798 @property
790 def program(self):
799 def program(self):
791 return self.ipcluster_cmd + ['engines']
800 return self.ipcluster_cmd + ['engines']
792
801
793 @property
802 @property
794 def program_args(self):
803 def program_args(self):
795 return ['-n', str(self.n), '--profile-dir', self.remote_profile_dir]
804 return ['-n', str(self.n), '--profile-dir', self.remote_profile_dir]
796
805
797 def _to_send_default(self):
806 def _to_send_default(self):
798 return [
807 return [
799 (os.path.join(self.profile_dir, 'security', cf),
808 (os.path.join(self.profile_dir, 'security', cf),
800 os.path.join(self.remote_profile_dir, 'security', cf))
809 os.path.join(self.remote_profile_dir, 'security', cf))
801 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
810 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
802 ]
811 ]
803
812
804 def start(self, n):
813 def start(self, n):
805 self.n = n
814 self.n = n
806 super(SSHProxyEngineSetLauncher, self).start()
815 super(SSHProxyEngineSetLauncher, self).start()
807
816
808
817
809 #-----------------------------------------------------------------------------
818 #-----------------------------------------------------------------------------
810 # Windows HPC Server 2008 scheduler launchers
819 # Windows HPC Server 2008 scheduler launchers
811 #-----------------------------------------------------------------------------
820 #-----------------------------------------------------------------------------
812
821
813
822
814 # This is only used on Windows.
823 # This is only used on Windows.
815 def find_job_cmd():
824 def find_job_cmd():
816 if WINDOWS:
825 if WINDOWS:
817 try:
826 try:
818 return find_cmd('job')
827 return find_cmd('job')
819 except (FindCmdError, ImportError):
828 except (FindCmdError, ImportError):
820 # ImportError will be raised if win32api is not installed
829 # ImportError will be raised if win32api is not installed
821 return 'job'
830 return 'job'
822 else:
831 else:
823 return 'job'
832 return 'job'
824
833
825
834
826 class WindowsHPCLauncher(BaseLauncher):
835 class WindowsHPCLauncher(BaseLauncher):
827
836
828 job_id_regexp = CRegExp(r'\d+', config=True,
837 job_id_regexp = CRegExp(r'\d+', config=True,
829 help="""A regular expression used to get the job id from the output of the
838 help="""A regular expression used to get the job id from the output of the
830 submit_command. """
839 submit_command. """
831 )
840 )
832 job_file_name = Unicode(u'ipython_job.xml', config=True,
841 job_file_name = Unicode(u'ipython_job.xml', config=True,
833 help="The filename of the instantiated job script.")
842 help="The filename of the instantiated job script.")
834 # The full path to the instantiated job script. This gets made dynamically
843 # The full path to the instantiated job script. This gets made dynamically
835 # by combining the work_dir with the job_file_name.
844 # by combining the work_dir with the job_file_name.
836 job_file = Unicode(u'')
845 job_file = Unicode(u'')
837 scheduler = Unicode('', config=True,
846 scheduler = Unicode('', config=True,
838 help="The hostname of the scheduler to submit the job to.")
847 help="The hostname of the scheduler to submit the job to.")
839 job_cmd = Unicode(find_job_cmd(), config=True,
848 job_cmd = Unicode(find_job_cmd(), config=True,
840 help="The command for submitting jobs.")
849 help="The command for submitting jobs.")
841
850
842 def __init__(self, work_dir=u'.', config=None, **kwargs):
851 def __init__(self, work_dir=u'.', config=None, **kwargs):
843 super(WindowsHPCLauncher, self).__init__(
852 super(WindowsHPCLauncher, self).__init__(
844 work_dir=work_dir, config=config, **kwargs
853 work_dir=work_dir, config=config, **kwargs
845 )
854 )
846
855
847 @property
856 @property
848 def job_file(self):
857 def job_file(self):
849 return os.path.join(self.work_dir, self.job_file_name)
858 return os.path.join(self.work_dir, self.job_file_name)
850
859
851 def write_job_file(self, n):
860 def write_job_file(self, n):
852 raise NotImplementedError("Implement write_job_file in a subclass.")
861 raise NotImplementedError("Implement write_job_file in a subclass.")
853
862
854 def find_args(self):
863 def find_args(self):
855 return [u'job.exe']
864 return [u'job.exe']
856
865
857 def parse_job_id(self, output):
866 def parse_job_id(self, output):
858 """Take the output of the submit command and return the job id."""
867 """Take the output of the submit command and return the job id."""
859 m = self.job_id_regexp.search(output)
868 m = self.job_id_regexp.search(output)
860 if m is not None:
869 if m is not None:
861 job_id = m.group()
870 job_id = m.group()
862 else:
871 else:
863 raise LauncherError("Job id couldn't be determined: %s" % output)
872 raise LauncherError("Job id couldn't be determined: %s" % output)
864 self.job_id = job_id
873 self.job_id = job_id
865 self.log.info('Job started with id: %r', job_id)
874 self.log.info('Job started with id: %r', job_id)
866 return job_id
875 return job_id
867
876
868 def start(self, n):
877 def start(self, n):
869 """Start n copies of the process using the Win HPC job scheduler."""
878 """Start n copies of the process using the Win HPC job scheduler."""
870 self.write_job_file(n)
879 self.write_job_file(n)
871 args = [
880 args = [
872 'submit',
881 'submit',
873 '/jobfile:%s' % self.job_file,
882 '/jobfile:%s' % self.job_file,
874 '/scheduler:%s' % self.scheduler
883 '/scheduler:%s' % self.scheduler
875 ]
884 ]
876 self.log.debug("Starting Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
885 self.log.debug("Starting Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
877
886
878 output = check_output([self.job_cmd]+args,
887 output = check_output([self.job_cmd]+args,
879 env=os.environ,
888 env=os.environ,
880 cwd=self.work_dir,
889 cwd=self.work_dir,
881 stderr=STDOUT
890 stderr=STDOUT
882 )
891 )
883 job_id = self.parse_job_id(output)
892 job_id = self.parse_job_id(output)
884 self.notify_start(job_id)
893 self.notify_start(job_id)
885 return job_id
894 return job_id
886
895
887 def stop(self):
896 def stop(self):
888 args = [
897 args = [
889 'cancel',
898 'cancel',
890 self.job_id,
899 self.job_id,
891 '/scheduler:%s' % self.scheduler
900 '/scheduler:%s' % self.scheduler
892 ]
901 ]
893 self.log.info("Stopping Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
902 self.log.info("Stopping Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
894 try:
903 try:
895 output = check_output([self.job_cmd]+args,
904 output = check_output([self.job_cmd]+args,
896 env=os.environ,
905 env=os.environ,
897 cwd=self.work_dir,
906 cwd=self.work_dir,
898 stderr=STDOUT
907 stderr=STDOUT
899 )
908 )
900 except:
909 except:
901 output = 'The job already appears to be stoppped: %r' % self.job_id
910 output = 'The job already appears to be stoppped: %r' % self.job_id
902 self.notify_stop(dict(job_id=self.job_id, output=output)) # Pass the output of the kill cmd
911 self.notify_stop(dict(job_id=self.job_id, output=output)) # Pass the output of the kill cmd
903 return output
912 return output
904
913
905
914
906 class WindowsHPCControllerLauncher(WindowsHPCLauncher, ClusterAppMixin):
915 class WindowsHPCControllerLauncher(WindowsHPCLauncher, ClusterAppMixin):
907
916
908 job_file_name = Unicode(u'ipcontroller_job.xml', config=True,
917 job_file_name = Unicode(u'ipcontroller_job.xml', config=True,
909 help="WinHPC xml job file.")
918 help="WinHPC xml job file.")
910 controller_args = List([], config=False,
919 controller_args = List([], config=False,
911 help="extra args to pass to ipcontroller")
920 help="extra args to pass to ipcontroller")
912
921
913 def write_job_file(self, n):
922 def write_job_file(self, n):
914 job = IPControllerJob(config=self.config)
923 job = IPControllerJob(config=self.config)
915
924
916 t = IPControllerTask(config=self.config)
925 t = IPControllerTask(config=self.config)
917 # The tasks work directory is *not* the actual work directory of
926 # The tasks work directory is *not* the actual work directory of
918 # the controller. It is used as the base path for the stdout/stderr
927 # the controller. It is used as the base path for the stdout/stderr
919 # files that the scheduler redirects to.
928 # files that the scheduler redirects to.
920 t.work_directory = self.profile_dir
929 t.work_directory = self.profile_dir
921 # Add the profile_dir and from self.start().
930 # Add the profile_dir and from self.start().
922 t.controller_args.extend(self.cluster_args)
931 t.controller_args.extend(self.cluster_args)
923 t.controller_args.extend(self.controller_args)
932 t.controller_args.extend(self.controller_args)
924 job.add_task(t)
933 job.add_task(t)
925
934
926 self.log.debug("Writing job description file: %s", self.job_file)
935 self.log.debug("Writing job description file: %s", self.job_file)
927 job.write(self.job_file)
936 job.write(self.job_file)
928
937
929 @property
938 @property
930 def job_file(self):
939 def job_file(self):
931 return os.path.join(self.profile_dir, self.job_file_name)
940 return os.path.join(self.profile_dir, self.job_file_name)
932
941
933 def start(self):
942 def start(self):
934 """Start the controller by profile_dir."""
943 """Start the controller by profile_dir."""
935 return super(WindowsHPCControllerLauncher, self).start(1)
944 return super(WindowsHPCControllerLauncher, self).start(1)
936
945
937
946
938 class WindowsHPCEngineSetLauncher(WindowsHPCLauncher, ClusterAppMixin):
947 class WindowsHPCEngineSetLauncher(WindowsHPCLauncher, ClusterAppMixin):
939
948
940 job_file_name = Unicode(u'ipengineset_job.xml', config=True,
949 job_file_name = Unicode(u'ipengineset_job.xml', config=True,
941 help="jobfile for ipengines job")
950 help="jobfile for ipengines job")
942 engine_args = List([], config=False,
951 engine_args = List([], config=False,
943 help="extra args to pas to ipengine")
952 help="extra args to pas to ipengine")
944
953
945 def write_job_file(self, n):
954 def write_job_file(self, n):
946 job = IPEngineSetJob(config=self.config)
955 job = IPEngineSetJob(config=self.config)
947
956
948 for i in range(n):
957 for i in range(n):
949 t = IPEngineTask(config=self.config)
958 t = IPEngineTask(config=self.config)
950 # The tasks work directory is *not* the actual work directory of
959 # The tasks work directory is *not* the actual work directory of
951 # the engine. It is used as the base path for the stdout/stderr
960 # the engine. It is used as the base path for the stdout/stderr
952 # files that the scheduler redirects to.
961 # files that the scheduler redirects to.
953 t.work_directory = self.profile_dir
962 t.work_directory = self.profile_dir
954 # Add the profile_dir and from self.start().
963 # Add the profile_dir and from self.start().
955 t.engine_args.extend(self.cluster_args)
964 t.engine_args.extend(self.cluster_args)
956 t.engine_args.extend(self.engine_args)
965 t.engine_args.extend(self.engine_args)
957 job.add_task(t)
966 job.add_task(t)
958
967
959 self.log.debug("Writing job description file: %s", self.job_file)
968 self.log.debug("Writing job description file: %s", self.job_file)
960 job.write(self.job_file)
969 job.write(self.job_file)
961
970
962 @property
971 @property
963 def job_file(self):
972 def job_file(self):
964 return os.path.join(self.profile_dir, self.job_file_name)
973 return os.path.join(self.profile_dir, self.job_file_name)
965
974
966 def start(self, n):
975 def start(self, n):
967 """Start the controller by profile_dir."""
976 """Start the controller by profile_dir."""
968 return super(WindowsHPCEngineSetLauncher, self).start(n)
977 return super(WindowsHPCEngineSetLauncher, self).start(n)
969
978
970
979
971 #-----------------------------------------------------------------------------
980 #-----------------------------------------------------------------------------
972 # Batch (PBS) system launchers
981 # Batch (PBS) system launchers
973 #-----------------------------------------------------------------------------
982 #-----------------------------------------------------------------------------
974
983
975 class BatchClusterAppMixin(ClusterAppMixin):
984 class BatchClusterAppMixin(ClusterAppMixin):
976 """ClusterApp mixin that updates the self.context dict, rather than cl-args."""
985 """ClusterApp mixin that updates the self.context dict, rather than cl-args."""
977 def _profile_dir_changed(self, name, old, new):
986 def _profile_dir_changed(self, name, old, new):
978 self.context[name] = new
987 self.context[name] = new
979 _cluster_id_changed = _profile_dir_changed
988 _cluster_id_changed = _profile_dir_changed
980
989
981 def _profile_dir_default(self):
990 def _profile_dir_default(self):
982 self.context['profile_dir'] = ''
991 self.context['profile_dir'] = ''
983 return ''
992 return ''
984 def _cluster_id_default(self):
993 def _cluster_id_default(self):
985 self.context['cluster_id'] = ''
994 self.context['cluster_id'] = ''
986 return ''
995 return ''
987
996
988
997
989 class BatchSystemLauncher(BaseLauncher):
998 class BatchSystemLauncher(BaseLauncher):
990 """Launch an external process using a batch system.
999 """Launch an external process using a batch system.
991
1000
992 This class is designed to work with UNIX batch systems like PBS, LSF,
1001 This class is designed to work with UNIX batch systems like PBS, LSF,
993 GridEngine, etc. The overall model is that there are different commands
1002 GridEngine, etc. The overall model is that there are different commands
994 like qsub, qdel, etc. that handle the starting and stopping of the process.
1003 like qsub, qdel, etc. that handle the starting and stopping of the process.
995
1004
996 This class also has the notion of a batch script. The ``batch_template``
1005 This class also has the notion of a batch script. The ``batch_template``
997 attribute can be set to a string that is a template for the batch script.
1006 attribute can be set to a string that is a template for the batch script.
998 This template is instantiated using string formatting. Thus the template can
1007 This template is instantiated using string formatting. Thus the template can
999 use {n} fot the number of instances. Subclasses can add additional variables
1008 use {n} fot the number of instances. Subclasses can add additional variables
1000 to the template dict.
1009 to the template dict.
1001 """
1010 """
1002
1011
1003 # Subclasses must fill these in. See PBSEngineSet
1012 # Subclasses must fill these in. See PBSEngineSet
1004 submit_command = List([''], config=True,
1013 submit_command = List([''], config=True,
1005 help="The name of the command line program used to submit jobs.")
1014 help="The name of the command line program used to submit jobs.")
1006 delete_command = List([''], config=True,
1015 delete_command = List([''], config=True,
1007 help="The name of the command line program used to delete jobs.")
1016 help="The name of the command line program used to delete jobs.")
1008 job_id_regexp = CRegExp('', config=True,
1017 job_id_regexp = CRegExp('', config=True,
1009 help="""A regular expression used to get the job id from the output of the
1018 help="""A regular expression used to get the job id from the output of the
1010 submit_command.""")
1019 submit_command.""")
1011 batch_template = Unicode('', config=True,
1020 batch_template = Unicode('', config=True,
1012 help="The string that is the batch script template itself.")
1021 help="The string that is the batch script template itself.")
1013 batch_template_file = Unicode(u'', config=True,
1022 batch_template_file = Unicode(u'', config=True,
1014 help="The file that contains the batch template.")
1023 help="The file that contains the batch template.")
1015 batch_file_name = Unicode(u'batch_script', config=True,
1024 batch_file_name = Unicode(u'batch_script', config=True,
1016 help="The filename of the instantiated batch script.")
1025 help="The filename of the instantiated batch script.")
1017 queue = Unicode(u'', config=True,
1026 queue = Unicode(u'', config=True,
1018 help="The PBS Queue.")
1027 help="The PBS Queue.")
1019
1028
1020 def _queue_changed(self, name, old, new):
1029 def _queue_changed(self, name, old, new):
1021 self.context[name] = new
1030 self.context[name] = new
1022
1031
1023 n = Integer(1)
1032 n = Integer(1)
1024 _n_changed = _queue_changed
1033 _n_changed = _queue_changed
1025
1034
1026 # not configurable, override in subclasses
1035 # not configurable, override in subclasses
1027 # PBS Job Array regex
1036 # PBS Job Array regex
1028 job_array_regexp = CRegExp('')
1037 job_array_regexp = CRegExp('')
1029 job_array_template = Unicode('')
1038 job_array_template = Unicode('')
1030 # PBS Queue regex
1039 # PBS Queue regex
1031 queue_regexp = CRegExp('')
1040 queue_regexp = CRegExp('')
1032 queue_template = Unicode('')
1041 queue_template = Unicode('')
1033 # The default batch template, override in subclasses
1042 # The default batch template, override in subclasses
1034 default_template = Unicode('')
1043 default_template = Unicode('')
1035 # The full path to the instantiated batch script.
1044 # The full path to the instantiated batch script.
1036 batch_file = Unicode(u'')
1045 batch_file = Unicode(u'')
1037 # the format dict used with batch_template:
1046 # the format dict used with batch_template:
1038 context = Dict()
1047 context = Dict()
1039 def _context_default(self):
1048 def _context_default(self):
1040 """load the default context with the default values for the basic keys
1049 """load the default context with the default values for the basic keys
1041
1050
1042 because the _trait_changed methods only load the context if they
1051 because the _trait_changed methods only load the context if they
1043 are set to something other than the default value.
1052 are set to something other than the default value.
1044 """
1053 """
1045 return dict(n=1, queue=u'', profile_dir=u'', cluster_id=u'')
1054 return dict(n=1, queue=u'', profile_dir=u'', cluster_id=u'')
1046
1055
1047 # the Formatter instance for rendering the templates:
1056 # the Formatter instance for rendering the templates:
1048 formatter = Instance(EvalFormatter, (), {})
1057 formatter = Instance(EvalFormatter, (), {})
1049
1058
1050
1059
1051 def find_args(self):
1060 def find_args(self):
1052 return self.submit_command + [self.batch_file]
1061 return self.submit_command + [self.batch_file]
1053
1062
1054 def __init__(self, work_dir=u'.', config=None, **kwargs):
1063 def __init__(self, work_dir=u'.', config=None, **kwargs):
1055 super(BatchSystemLauncher, self).__init__(
1064 super(BatchSystemLauncher, self).__init__(
1056 work_dir=work_dir, config=config, **kwargs
1065 work_dir=work_dir, config=config, **kwargs
1057 )
1066 )
1058 self.batch_file = os.path.join(self.work_dir, self.batch_file_name)
1067 self.batch_file = os.path.join(self.work_dir, self.batch_file_name)
1059
1068
1060 def parse_job_id(self, output):
1069 def parse_job_id(self, output):
1061 """Take the output of the submit command and return the job id."""
1070 """Take the output of the submit command and return the job id."""
1062 m = self.job_id_regexp.search(output)
1071 m = self.job_id_regexp.search(output)
1063 if m is not None:
1072 if m is not None:
1064 job_id = m.group()
1073 job_id = m.group()
1065 else:
1074 else:
1066 raise LauncherError("Job id couldn't be determined: %s" % output)
1075 raise LauncherError("Job id couldn't be determined: %s" % output)
1067 self.job_id = job_id
1076 self.job_id = job_id
1068 self.log.info('Job submitted with job id: %r', job_id)
1077 self.log.info('Job submitted with job id: %r', job_id)
1069 return job_id
1078 return job_id
1070
1079
1071 def write_batch_script(self, n):
1080 def write_batch_script(self, n):
1072 """Instantiate and write the batch script to the work_dir."""
1081 """Instantiate and write the batch script to the work_dir."""
1073 self.n = n
1082 self.n = n
1074 # first priority is batch_template if set
1083 # first priority is batch_template if set
1075 if self.batch_template_file and not self.batch_template:
1084 if self.batch_template_file and not self.batch_template:
1076 # second priority is batch_template_file
1085 # second priority is batch_template_file
1077 with open(self.batch_template_file) as f:
1086 with open(self.batch_template_file) as f:
1078 self.batch_template = f.read()
1087 self.batch_template = f.read()
1079 if not self.batch_template:
1088 if not self.batch_template:
1080 # third (last) priority is default_template
1089 # third (last) priority is default_template
1081 self.batch_template = self.default_template
1090 self.batch_template = self.default_template
1082
1091
1083 # add jobarray or queue lines to user-specified template
1092 # add jobarray or queue lines to user-specified template
1084 # note that this is *only* when user did not specify a template.
1093 # note that this is *only* when user did not specify a template.
1085 # print self.job_array_regexp.search(self.batch_template)
1094 # print self.job_array_regexp.search(self.batch_template)
1086 if not self.job_array_regexp.search(self.batch_template):
1095 if not self.job_array_regexp.search(self.batch_template):
1087 self.log.debug("adding job array settings to batch script")
1096 self.log.debug("adding job array settings to batch script")
1088 firstline, rest = self.batch_template.split('\n',1)
1097 firstline, rest = self.batch_template.split('\n',1)
1089 self.batch_template = u'\n'.join([firstline, self.job_array_template, rest])
1098 self.batch_template = u'\n'.join([firstline, self.job_array_template, rest])
1090
1099
1091 # print self.queue_regexp.search(self.batch_template)
1100 # print self.queue_regexp.search(self.batch_template)
1092 if self.queue and not self.queue_regexp.search(self.batch_template):
1101 if self.queue and not self.queue_regexp.search(self.batch_template):
1093 self.log.debug("adding PBS queue settings to batch script")
1102 self.log.debug("adding PBS queue settings to batch script")
1094 firstline, rest = self.batch_template.split('\n',1)
1103 firstline, rest = self.batch_template.split('\n',1)
1095 self.batch_template = u'\n'.join([firstline, self.queue_template, rest])
1104 self.batch_template = u'\n'.join([firstline, self.queue_template, rest])
1096
1105
1097 script_as_string = self.formatter.format(self.batch_template, **self.context)
1106 script_as_string = self.formatter.format(self.batch_template, **self.context)
1098 self.log.debug('Writing batch script: %s', self.batch_file)
1107 self.log.debug('Writing batch script: %s', self.batch_file)
1099
1108
1100 with open(self.batch_file, 'w') as f:
1109 with open(self.batch_file, 'w') as f:
1101 f.write(script_as_string)
1110 f.write(script_as_string)
1102 os.chmod(self.batch_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
1111 os.chmod(self.batch_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
1103
1112
1104 def start(self, n):
1113 def start(self, n):
1105 """Start n copies of the process using a batch system."""
1114 """Start n copies of the process using a batch system."""
1106 self.log.debug("Starting %s: %r", self.__class__.__name__, self.args)
1115 self.log.debug("Starting %s: %r", self.__class__.__name__, self.args)
1107 # Here we save profile_dir in the context so they
1116 # Here we save profile_dir in the context so they
1108 # can be used in the batch script template as {profile_dir}
1117 # can be used in the batch script template as {profile_dir}
1109 self.write_batch_script(n)
1118 self.write_batch_script(n)
1110 output = check_output(self.args, env=os.environ)
1119 output = check_output(self.args, env=os.environ)
1111
1120
1112 job_id = self.parse_job_id(output)
1121 job_id = self.parse_job_id(output)
1113 self.notify_start(job_id)
1122 self.notify_start(job_id)
1114 return job_id
1123 return job_id
1115
1124
1116 def stop(self):
1125 def stop(self):
1117 output = check_output(self.delete_command+[self.job_id], env=os.environ)
1126 output = check_output(self.delete_command+[self.job_id], env=os.environ)
1118 self.notify_stop(dict(job_id=self.job_id, output=output)) # Pass the output of the kill cmd
1127 self.notify_stop(dict(job_id=self.job_id, output=output)) # Pass the output of the kill cmd
1119 return output
1128 return output
1120
1129
1121
1130
1122 class PBSLauncher(BatchSystemLauncher):
1131 class PBSLauncher(BatchSystemLauncher):
1123 """A BatchSystemLauncher subclass for PBS."""
1132 """A BatchSystemLauncher subclass for PBS."""
1124
1133
1125 submit_command = List(['qsub'], config=True,
1134 submit_command = List(['qsub'], config=True,
1126 help="The PBS submit command ['qsub']")
1135 help="The PBS submit command ['qsub']")
1127 delete_command = List(['qdel'], config=True,
1136 delete_command = List(['qdel'], config=True,
1128 help="The PBS delete command ['qsub']")
1137 help="The PBS delete command ['qsub']")
1129 job_id_regexp = CRegExp(r'\d+', config=True,
1138 job_id_regexp = CRegExp(r'\d+', config=True,
1130 help="Regular expresion for identifying the job ID [r'\d+']")
1139 help="Regular expresion for identifying the job ID [r'\d+']")
1131
1140
1132 batch_file = Unicode(u'')
1141 batch_file = Unicode(u'')
1133 job_array_regexp = CRegExp('#PBS\W+-t\W+[\w\d\-\$]+')
1142 job_array_regexp = CRegExp('#PBS\W+-t\W+[\w\d\-\$]+')
1134 job_array_template = Unicode('#PBS -t 1-{n}')
1143 job_array_template = Unicode('#PBS -t 1-{n}')
1135 queue_regexp = CRegExp('#PBS\W+-q\W+\$?\w+')
1144 queue_regexp = CRegExp('#PBS\W+-q\W+\$?\w+')
1136 queue_template = Unicode('#PBS -q {queue}')
1145 queue_template = Unicode('#PBS -q {queue}')
1137
1146
1138
1147
1139 class PBSControllerLauncher(PBSLauncher, BatchClusterAppMixin):
1148 class PBSControllerLauncher(PBSLauncher, BatchClusterAppMixin):
1140 """Launch a controller using PBS."""
1149 """Launch a controller using PBS."""
1141
1150
1142 batch_file_name = Unicode(u'pbs_controller', config=True,
1151 batch_file_name = Unicode(u'pbs_controller', config=True,
1143 help="batch file name for the controller job.")
1152 help="batch file name for the controller job.")
1144 default_template= Unicode("""#!/bin/sh
1153 default_template= Unicode("""#!/bin/sh
1145 #PBS -V
1154 #PBS -V
1146 #PBS -N ipcontroller
1155 #PBS -N ipcontroller
1147 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1156 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1148 """%(' '.join(map(pipes.quote, ipcontroller_cmd_argv))))
1157 """%(' '.join(map(pipes.quote, ipcontroller_cmd_argv))))
1149
1158
1150
1159
1151 def start(self):
1160 def start(self):
1152 """Start the controller by profile or profile_dir."""
1161 """Start the controller by profile or profile_dir."""
1153 return super(PBSControllerLauncher, self).start(1)
1162 return super(PBSControllerLauncher, self).start(1)
1154
1163
1155
1164
1156 class PBSEngineSetLauncher(PBSLauncher, BatchClusterAppMixin):
1165 class PBSEngineSetLauncher(PBSLauncher, BatchClusterAppMixin):
1157 """Launch Engines using PBS"""
1166 """Launch Engines using PBS"""
1158 batch_file_name = Unicode(u'pbs_engines', config=True,
1167 batch_file_name = Unicode(u'pbs_engines', config=True,
1159 help="batch file name for the engine(s) job.")
1168 help="batch file name for the engine(s) job.")
1160 default_template= Unicode(u"""#!/bin/sh
1169 default_template= Unicode(u"""#!/bin/sh
1161 #PBS -V
1170 #PBS -V
1162 #PBS -N ipengine
1171 #PBS -N ipengine
1163 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1172 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1164 """%(' '.join(map(pipes.quote,ipengine_cmd_argv))))
1173 """%(' '.join(map(pipes.quote,ipengine_cmd_argv))))
1165
1174
1166 def start(self, n):
1175 def start(self, n):
1167 """Start n engines by profile or profile_dir."""
1176 """Start n engines by profile or profile_dir."""
1168 return super(PBSEngineSetLauncher, self).start(n)
1177 return super(PBSEngineSetLauncher, self).start(n)
1169
1178
1170 #SGE is very similar to PBS
1179 #SGE is very similar to PBS
1171
1180
1172 class SGELauncher(PBSLauncher):
1181 class SGELauncher(PBSLauncher):
1173 """Sun GridEngine is a PBS clone with slightly different syntax"""
1182 """Sun GridEngine is a PBS clone with slightly different syntax"""
1174 job_array_regexp = CRegExp('#\$\W+\-t')
1183 job_array_regexp = CRegExp('#\$\W+\-t')
1175 job_array_template = Unicode('#$ -t 1-{n}')
1184 job_array_template = Unicode('#$ -t 1-{n}')
1176 queue_regexp = CRegExp('#\$\W+-q\W+\$?\w+')
1185 queue_regexp = CRegExp('#\$\W+-q\W+\$?\w+')
1177 queue_template = Unicode('#$ -q {queue}')
1186 queue_template = Unicode('#$ -q {queue}')
1178
1187
1179 class SGEControllerLauncher(SGELauncher, BatchClusterAppMixin):
1188 class SGEControllerLauncher(SGELauncher, BatchClusterAppMixin):
1180 """Launch a controller using SGE."""
1189 """Launch a controller using SGE."""
1181
1190
1182 batch_file_name = Unicode(u'sge_controller', config=True,
1191 batch_file_name = Unicode(u'sge_controller', config=True,
1183 help="batch file name for the ipontroller job.")
1192 help="batch file name for the ipontroller job.")
1184 default_template= Unicode(u"""#$ -V
1193 default_template= Unicode(u"""#$ -V
1185 #$ -S /bin/sh
1194 #$ -S /bin/sh
1186 #$ -N ipcontroller
1195 #$ -N ipcontroller
1187 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1196 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1188 """%(' '.join(map(pipes.quote, ipcontroller_cmd_argv))))
1197 """%(' '.join(map(pipes.quote, ipcontroller_cmd_argv))))
1189
1198
1190 def start(self):
1199 def start(self):
1191 """Start the controller by profile or profile_dir."""
1200 """Start the controller by profile or profile_dir."""
1192 return super(SGEControllerLauncher, self).start(1)
1201 return super(SGEControllerLauncher, self).start(1)
1193
1202
1194 class SGEEngineSetLauncher(SGELauncher, BatchClusterAppMixin):
1203 class SGEEngineSetLauncher(SGELauncher, BatchClusterAppMixin):
1195 """Launch Engines with SGE"""
1204 """Launch Engines with SGE"""
1196 batch_file_name = Unicode(u'sge_engines', config=True,
1205 batch_file_name = Unicode(u'sge_engines', config=True,
1197 help="batch file name for the engine(s) job.")
1206 help="batch file name for the engine(s) job.")
1198 default_template = Unicode("""#$ -V
1207 default_template = Unicode("""#$ -V
1199 #$ -S /bin/sh
1208 #$ -S /bin/sh
1200 #$ -N ipengine
1209 #$ -N ipengine
1201 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1210 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1202 """%(' '.join(map(pipes.quote, ipengine_cmd_argv))))
1211 """%(' '.join(map(pipes.quote, ipengine_cmd_argv))))
1203
1212
1204 def start(self, n):
1213 def start(self, n):
1205 """Start n engines by profile or profile_dir."""
1214 """Start n engines by profile or profile_dir."""
1206 return super(SGEEngineSetLauncher, self).start(n)
1215 return super(SGEEngineSetLauncher, self).start(n)
1207
1216
1208
1217
1209 # LSF launchers
1218 # LSF launchers
1210
1219
1211 class LSFLauncher(BatchSystemLauncher):
1220 class LSFLauncher(BatchSystemLauncher):
1212 """A BatchSystemLauncher subclass for LSF."""
1221 """A BatchSystemLauncher subclass for LSF."""
1213
1222
1214 submit_command = List(['bsub'], config=True,
1223 submit_command = List(['bsub'], config=True,
1215 help="The PBS submit command ['bsub']")
1224 help="The PBS submit command ['bsub']")
1216 delete_command = List(['bkill'], config=True,
1225 delete_command = List(['bkill'], config=True,
1217 help="The PBS delete command ['bkill']")
1226 help="The PBS delete command ['bkill']")
1218 job_id_regexp = CRegExp(r'\d+', config=True,
1227 job_id_regexp = CRegExp(r'\d+', config=True,
1219 help="Regular expresion for identifying the job ID [r'\d+']")
1228 help="Regular expresion for identifying the job ID [r'\d+']")
1220
1229
1221 batch_file = Unicode(u'')
1230 batch_file = Unicode(u'')
1222 job_array_regexp = CRegExp('#BSUB[ \t]-J+\w+\[\d+-\d+\]')
1231 job_array_regexp = CRegExp('#BSUB[ \t]-J+\w+\[\d+-\d+\]')
1223 job_array_template = Unicode('#BSUB -J ipengine[1-{n}]')
1232 job_array_template = Unicode('#BSUB -J ipengine[1-{n}]')
1224 queue_regexp = CRegExp('#BSUB[ \t]+-q[ \t]+\w+')
1233 queue_regexp = CRegExp('#BSUB[ \t]+-q[ \t]+\w+')
1225 queue_template = Unicode('#BSUB -q {queue}')
1234 queue_template = Unicode('#BSUB -q {queue}')
1226
1235
1227 def start(self, n):
1236 def start(self, n):
1228 """Start n copies of the process using LSF batch system.
1237 """Start n copies of the process using LSF batch system.
1229 This cant inherit from the base class because bsub expects
1238 This cant inherit from the base class because bsub expects
1230 to be piped a shell script in order to honor the #BSUB directives :
1239 to be piped a shell script in order to honor the #BSUB directives :
1231 bsub < script
1240 bsub < script
1232 """
1241 """
1233 # Here we save profile_dir in the context so they
1242 # Here we save profile_dir in the context so they
1234 # can be used in the batch script template as {profile_dir}
1243 # can be used in the batch script template as {profile_dir}
1235 self.write_batch_script(n)
1244 self.write_batch_script(n)
1236 #output = check_output(self.args, env=os.environ)
1245 #output = check_output(self.args, env=os.environ)
1237 piped_cmd = self.args[0]+'<\"'+self.args[1]+'\"'
1246 piped_cmd = self.args[0]+'<\"'+self.args[1]+'\"'
1238 self.log.debug("Starting %s: %s", self.__class__.__name__, piped_cmd)
1247 self.log.debug("Starting %s: %s", self.__class__.__name__, piped_cmd)
1239 p = Popen(piped_cmd, shell=True,env=os.environ,stdout=PIPE)
1248 p = Popen(piped_cmd, shell=True,env=os.environ,stdout=PIPE)
1240 output,err = p.communicate()
1249 output,err = p.communicate()
1241 job_id = self.parse_job_id(output)
1250 job_id = self.parse_job_id(output)
1242 self.notify_start(job_id)
1251 self.notify_start(job_id)
1243 return job_id
1252 return job_id
1244
1253
1245
1254
1246 class LSFControllerLauncher(LSFLauncher, BatchClusterAppMixin):
1255 class LSFControllerLauncher(LSFLauncher, BatchClusterAppMixin):
1247 """Launch a controller using LSF."""
1256 """Launch a controller using LSF."""
1248
1257
1249 batch_file_name = Unicode(u'lsf_controller', config=True,
1258 batch_file_name = Unicode(u'lsf_controller', config=True,
1250 help="batch file name for the controller job.")
1259 help="batch file name for the controller job.")
1251 default_template= Unicode("""#!/bin/sh
1260 default_template= Unicode("""#!/bin/sh
1252 #BSUB -J ipcontroller
1261 #BSUB -J ipcontroller
1253 #BSUB -oo ipcontroller.o.%%J
1262 #BSUB -oo ipcontroller.o.%%J
1254 #BSUB -eo ipcontroller.e.%%J
1263 #BSUB -eo ipcontroller.e.%%J
1255 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1264 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1256 """%(' '.join(map(pipes.quote,ipcontroller_cmd_argv))))
1265 """%(' '.join(map(pipes.quote,ipcontroller_cmd_argv))))
1257
1266
1258 def start(self):
1267 def start(self):
1259 """Start the controller by profile or profile_dir."""
1268 """Start the controller by profile or profile_dir."""
1260 return super(LSFControllerLauncher, self).start(1)
1269 return super(LSFControllerLauncher, self).start(1)
1261
1270
1262
1271
1263 class LSFEngineSetLauncher(LSFLauncher, BatchClusterAppMixin):
1272 class LSFEngineSetLauncher(LSFLauncher, BatchClusterAppMixin):
1264 """Launch Engines using LSF"""
1273 """Launch Engines using LSF"""
1265 batch_file_name = Unicode(u'lsf_engines', config=True,
1274 batch_file_name = Unicode(u'lsf_engines', config=True,
1266 help="batch file name for the engine(s) job.")
1275 help="batch file name for the engine(s) job.")
1267 default_template= Unicode(u"""#!/bin/sh
1276 default_template= Unicode(u"""#!/bin/sh
1268 #BSUB -oo ipengine.o.%%J
1277 #BSUB -oo ipengine.o.%%J
1269 #BSUB -eo ipengine.e.%%J
1278 #BSUB -eo ipengine.e.%%J
1270 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1279 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1271 """%(' '.join(map(pipes.quote, ipengine_cmd_argv))))
1280 """%(' '.join(map(pipes.quote, ipengine_cmd_argv))))
1272
1281
1273 def start(self, n):
1282 def start(self, n):
1274 """Start n engines by profile or profile_dir."""
1283 """Start n engines by profile or profile_dir."""
1275 return super(LSFEngineSetLauncher, self).start(n)
1284 return super(LSFEngineSetLauncher, self).start(n)
1276
1285
1277
1286
1278 #-----------------------------------------------------------------------------
1287 #-----------------------------------------------------------------------------
1279 # A launcher for ipcluster itself!
1288 # A launcher for ipcluster itself!
1280 #-----------------------------------------------------------------------------
1289 #-----------------------------------------------------------------------------
1281
1290
1282
1291
1283 class IPClusterLauncher(LocalProcessLauncher):
1292 class IPClusterLauncher(LocalProcessLauncher):
1284 """Launch the ipcluster program in an external process."""
1293 """Launch the ipcluster program in an external process."""
1285
1294
1286 ipcluster_cmd = List(ipcluster_cmd_argv, config=True,
1295 ipcluster_cmd = List(ipcluster_cmd_argv, config=True,
1287 help="Popen command for ipcluster")
1296 help="Popen command for ipcluster")
1288 ipcluster_args = List(
1297 ipcluster_args = List(
1289 ['--clean-logs=True', '--log-to-file', '--log-level=%i'%logging.INFO], config=True,
1298 ['--clean-logs=True', '--log-to-file', '--log-level=%i'%logging.INFO], config=True,
1290 help="Command line arguments to pass to ipcluster.")
1299 help="Command line arguments to pass to ipcluster.")
1291 ipcluster_subcommand = Unicode('start')
1300 ipcluster_subcommand = Unicode('start')
1292 profile = Unicode('default')
1301 profile = Unicode('default')
1293 n = Integer(2)
1302 n = Integer(2)
1294
1303
1295 def find_args(self):
1304 def find_args(self):
1296 return self.ipcluster_cmd + [self.ipcluster_subcommand] + \
1305 return self.ipcluster_cmd + [self.ipcluster_subcommand] + \
1297 ['--n=%i'%self.n, '--profile=%s'%self.profile] + \
1306 ['--n=%i'%self.n, '--profile=%s'%self.profile] + \
1298 self.ipcluster_args
1307 self.ipcluster_args
1299
1308
1300 def start(self):
1309 def start(self):
1301 return super(IPClusterLauncher, self).start()
1310 return super(IPClusterLauncher, self).start()
1302
1311
1303 #-----------------------------------------------------------------------------
1312 #-----------------------------------------------------------------------------
1304 # Collections of launchers
1313 # Collections of launchers
1305 #-----------------------------------------------------------------------------
1314 #-----------------------------------------------------------------------------
1306
1315
1307 local_launchers = [
1316 local_launchers = [
1308 LocalControllerLauncher,
1317 LocalControllerLauncher,
1309 LocalEngineLauncher,
1318 LocalEngineLauncher,
1310 LocalEngineSetLauncher,
1319 LocalEngineSetLauncher,
1311 ]
1320 ]
1312 mpi_launchers = [
1321 mpi_launchers = [
1313 MPILauncher,
1322 MPILauncher,
1314 MPIControllerLauncher,
1323 MPIControllerLauncher,
1315 MPIEngineSetLauncher,
1324 MPIEngineSetLauncher,
1316 ]
1325 ]
1317 ssh_launchers = [
1326 ssh_launchers = [
1318 SSHLauncher,
1327 SSHLauncher,
1319 SSHControllerLauncher,
1328 SSHControllerLauncher,
1320 SSHEngineLauncher,
1329 SSHEngineLauncher,
1321 SSHEngineSetLauncher,
1330 SSHEngineSetLauncher,
1322 ]
1331 ]
1323 winhpc_launchers = [
1332 winhpc_launchers = [
1324 WindowsHPCLauncher,
1333 WindowsHPCLauncher,
1325 WindowsHPCControllerLauncher,
1334 WindowsHPCControllerLauncher,
1326 WindowsHPCEngineSetLauncher,
1335 WindowsHPCEngineSetLauncher,
1327 ]
1336 ]
1328 pbs_launchers = [
1337 pbs_launchers = [
1329 PBSLauncher,
1338 PBSLauncher,
1330 PBSControllerLauncher,
1339 PBSControllerLauncher,
1331 PBSEngineSetLauncher,
1340 PBSEngineSetLauncher,
1332 ]
1341 ]
1333 sge_launchers = [
1342 sge_launchers = [
1334 SGELauncher,
1343 SGELauncher,
1335 SGEControllerLauncher,
1344 SGEControllerLauncher,
1336 SGEEngineSetLauncher,
1345 SGEEngineSetLauncher,
1337 ]
1346 ]
1338 lsf_launchers = [
1347 lsf_launchers = [
1339 LSFLauncher,
1348 LSFLauncher,
1340 LSFControllerLauncher,
1349 LSFControllerLauncher,
1341 LSFEngineSetLauncher,
1350 LSFEngineSetLauncher,
1342 ]
1351 ]
1343 all_launchers = local_launchers + mpi_launchers + ssh_launchers + winhpc_launchers\
1352 all_launchers = local_launchers + mpi_launchers + ssh_launchers + winhpc_launchers\
1344 + pbs_launchers + sge_launchers + lsf_launchers
1353 + pbs_launchers + sge_launchers + lsf_launchers
1345
1354
@@ -1,491 +1,494 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for path handling.
3 Utilities for path handling.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import os
17 import os
18 import sys
18 import sys
19 import tempfile
19 import tempfile
20 import warnings
20 import warnings
21 from hashlib import md5
21 from hashlib import md5
22 import glob
22 import glob
23
23
24 import IPython
24 import IPython
25 from IPython.testing.skipdoctest import skip_doctest
25 from IPython.testing.skipdoctest import skip_doctest
26 from IPython.utils.process import system
26 from IPython.utils.process import system
27 from IPython.utils.importstring import import_item
27 from IPython.utils.importstring import import_item
28 from IPython.utils import py3compat
28 from IPython.utils import py3compat
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Code
30 # Code
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 fs_encoding = sys.getfilesystemencoding()
33 fs_encoding = sys.getfilesystemencoding()
34
34
35 def _get_long_path_name(path):
35 def _get_long_path_name(path):
36 """Dummy no-op."""
36 """Dummy no-op."""
37 return path
37 return path
38
38
39 def _writable_dir(path):
39 def _writable_dir(path):
40 """Whether `path` is a directory, to which the user has write access."""
40 """Whether `path` is a directory, to which the user has write access."""
41 return os.path.isdir(path) and os.access(path, os.W_OK)
41 return os.path.isdir(path) and os.access(path, os.W_OK)
42
42
43 if sys.platform == 'win32':
43 if sys.platform == 'win32':
44 @skip_doctest
44 @skip_doctest
45 def _get_long_path_name(path):
45 def _get_long_path_name(path):
46 """Get a long path name (expand ~) on Windows using ctypes.
46 """Get a long path name (expand ~) on Windows using ctypes.
47
47
48 Examples
48 Examples
49 --------
49 --------
50
50
51 >>> get_long_path_name('c:\\docume~1')
51 >>> get_long_path_name('c:\\docume~1')
52 u'c:\\\\Documents and Settings'
52 u'c:\\\\Documents and Settings'
53
53
54 """
54 """
55 try:
55 try:
56 import ctypes
56 import ctypes
57 except ImportError:
57 except ImportError:
58 raise ImportError('you need to have ctypes installed for this to work')
58 raise ImportError('you need to have ctypes installed for this to work')
59 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
59 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
60 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
60 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
61 ctypes.c_uint ]
61 ctypes.c_uint ]
62
62
63 buf = ctypes.create_unicode_buffer(260)
63 buf = ctypes.create_unicode_buffer(260)
64 rv = _GetLongPathName(path, buf, 260)
64 rv = _GetLongPathName(path, buf, 260)
65 if rv == 0 or rv > 260:
65 if rv == 0 or rv > 260:
66 return path
66 return path
67 else:
67 else:
68 return buf.value
68 return buf.value
69
69
70
70
71 def get_long_path_name(path):
71 def get_long_path_name(path):
72 """Expand a path into its long form.
72 """Expand a path into its long form.
73
73
74 On Windows this expands any ~ in the paths. On other platforms, it is
74 On Windows this expands any ~ in the paths. On other platforms, it is
75 a null operation.
75 a null operation.
76 """
76 """
77 return _get_long_path_name(path)
77 return _get_long_path_name(path)
78
78
79
79
80 def unquote_filename(name, win32=(sys.platform=='win32')):
80 def unquote_filename(name, win32=(sys.platform=='win32')):
81 """ On Windows, remove leading and trailing quotes from filenames.
81 """ On Windows, remove leading and trailing quotes from filenames.
82 """
82 """
83 if win32:
83 if win32:
84 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
84 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
85 name = name[1:-1]
85 name = name[1:-1]
86 return name
86 return name
87
87
88
88
89 def get_py_filename(name, force_win32=None):
89 def get_py_filename(name, force_win32=None):
90 """Return a valid python filename in the current directory.
90 """Return a valid python filename in the current directory.
91
91
92 If the given name is not a file, it adds '.py' and searches again.
92 If the given name is not a file, it adds '.py' and searches again.
93 Raises IOError with an informative message if the file isn't found.
93 Raises IOError with an informative message if the file isn't found.
94
94
95 On Windows, apply Windows semantics to the filename. In particular, remove
95 On Windows, apply Windows semantics to the filename. In particular, remove
96 any quoting that has been applied to it. This option can be forced for
96 any quoting that has been applied to it. This option can be forced for
97 testing purposes.
97 testing purposes.
98 """
98 """
99
99
100 name = os.path.expanduser(name)
100 name = os.path.expanduser(name)
101 if force_win32 is None:
101 if force_win32 is None:
102 win32 = (sys.platform == 'win32')
102 win32 = (sys.platform == 'win32')
103 else:
103 else:
104 win32 = force_win32
104 win32 = force_win32
105 name = unquote_filename(name, win32=win32)
105 name = unquote_filename(name, win32=win32)
106 if not os.path.isfile(name) and not name.endswith('.py'):
106 if not os.path.isfile(name) and not name.endswith('.py'):
107 name += '.py'
107 name += '.py'
108 if os.path.isfile(name):
108 if os.path.isfile(name):
109 return name
109 return name
110 else:
110 else:
111 raise IOError('File `%r` not found.' % name)
111 raise IOError('File `%r` not found.' % name)
112
112
113
113
114 def filefind(filename, path_dirs=None):
114 def filefind(filename, path_dirs=None):
115 """Find a file by looking through a sequence of paths.
115 """Find a file by looking through a sequence of paths.
116
116
117 This iterates through a sequence of paths looking for a file and returns
117 This iterates through a sequence of paths looking for a file and returns
118 the full, absolute path of the first occurence of the file. If no set of
118 the full, absolute path of the first occurence of the file. If no set of
119 path dirs is given, the filename is tested as is, after running through
119 path dirs is given, the filename is tested as is, after running through
120 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
120 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
121
121
122 filefind('myfile.txt')
122 filefind('myfile.txt')
123
123
124 will find the file in the current working dir, but::
124 will find the file in the current working dir, but::
125
125
126 filefind('~/myfile.txt')
126 filefind('~/myfile.txt')
127
127
128 Will find the file in the users home directory. This function does not
128 Will find the file in the users home directory. This function does not
129 automatically try any paths, such as the cwd or the user's home directory.
129 automatically try any paths, such as the cwd or the user's home directory.
130
130
131 Parameters
131 Parameters
132 ----------
132 ----------
133 filename : str
133 filename : str
134 The filename to look for.
134 The filename to look for.
135 path_dirs : str, None or sequence of str
135 path_dirs : str, None or sequence of str
136 The sequence of paths to look for the file in. If None, the filename
136 The sequence of paths to look for the file in. If None, the filename
137 need to be absolute or be in the cwd. If a string, the string is
137 need to be absolute or be in the cwd. If a string, the string is
138 put into a sequence and the searched. If a sequence, walk through
138 put into a sequence and the searched. If a sequence, walk through
139 each element and join with ``filename``, calling :func:`expandvars`
139 each element and join with ``filename``, calling :func:`expandvars`
140 and :func:`expanduser` before testing for existence.
140 and :func:`expanduser` before testing for existence.
141
141
142 Returns
142 Returns
143 -------
143 -------
144 Raises :exc:`IOError` or returns absolute path to file.
144 Raises :exc:`IOError` or returns absolute path to file.
145 """
145 """
146
146
147 # If paths are quoted, abspath gets confused, strip them...
147 # If paths are quoted, abspath gets confused, strip them...
148 filename = filename.strip('"').strip("'")
148 filename = filename.strip('"').strip("'")
149 # If the input is an absolute path, just check it exists
149 # If the input is an absolute path, just check it exists
150 if os.path.isabs(filename) and os.path.isfile(filename):
150 if os.path.isabs(filename) and os.path.isfile(filename):
151 return filename
151 return filename
152
152
153 if path_dirs is None:
153 if path_dirs is None:
154 path_dirs = ("",)
154 path_dirs = ("",)
155 elif isinstance(path_dirs, basestring):
155 elif isinstance(path_dirs, basestring):
156 path_dirs = (path_dirs,)
156 path_dirs = (path_dirs,)
157
157
158 for path in path_dirs:
158 for path in path_dirs:
159 if path == '.': path = os.getcwdu()
159 if path == '.': path = os.getcwdu()
160 testname = expand_path(os.path.join(path, filename))
160 testname = expand_path(os.path.join(path, filename))
161 if os.path.isfile(testname):
161 if os.path.isfile(testname):
162 return os.path.abspath(testname)
162 return os.path.abspath(testname)
163
163
164 raise IOError("File %r does not exist in any of the search paths: %r" %
164 raise IOError("File %r does not exist in any of the search paths: %r" %
165 (filename, path_dirs) )
165 (filename, path_dirs) )
166
166
167
167
168 class HomeDirError(Exception):
168 class HomeDirError(Exception):
169 pass
169 pass
170
170
171
171
172 def get_home_dir(require_writable=False):
172 def get_home_dir(require_writable=False):
173 """Return the 'home' directory, as a unicode string.
173 """Return the 'home' directory, as a unicode string.
174
174
175 * First, check for frozen env in case of py2exe
175 * First, check for frozen env in case of py2exe
176 * Otherwise, defer to os.path.expanduser('~')
176 * Otherwise, defer to os.path.expanduser('~')
177
177
178 See stdlib docs for how this is determined.
178 See stdlib docs for how this is determined.
179 $HOME is first priority on *ALL* platforms.
179 $HOME is first priority on *ALL* platforms.
180
180
181 Parameters
181 Parameters
182 ----------
182 ----------
183
183
184 require_writable : bool [default: False]
184 require_writable : bool [default: False]
185 if True:
185 if True:
186 guarantees the return value is a writable directory, otherwise
186 guarantees the return value is a writable directory, otherwise
187 raises HomeDirError
187 raises HomeDirError
188 if False:
188 if False:
189 The path is resolved, but it is not guaranteed to exist or be writable.
189 The path is resolved, but it is not guaranteed to exist or be writable.
190 """
190 """
191
191
192 # first, check py2exe distribution root directory for _ipython.
192 # first, check py2exe distribution root directory for _ipython.
193 # This overrides all. Normally does not exist.
193 # This overrides all. Normally does not exist.
194
194
195 if hasattr(sys, "frozen"): #Is frozen by py2exe
195 if hasattr(sys, "frozen"): #Is frozen by py2exe
196 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
196 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
197 root, rest = IPython.__file__.lower().split('library.zip')
197 root, rest = IPython.__file__.lower().split('library.zip')
198 else:
198 else:
199 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
199 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
200 root=os.path.abspath(root).rstrip('\\')
200 root=os.path.abspath(root).rstrip('\\')
201 if _writable_dir(os.path.join(root, '_ipython')):
201 if _writable_dir(os.path.join(root, '_ipython')):
202 os.environ["IPYKITROOT"] = root
202 os.environ["IPYKITROOT"] = root
203 return py3compat.cast_unicode(root, fs_encoding)
203 return py3compat.cast_unicode(root, fs_encoding)
204
204
205 homedir = os.path.expanduser('~')
205 homedir = os.path.expanduser('~')
206 # Next line will make things work even when /home/ is a symlink to
206 # Next line will make things work even when /home/ is a symlink to
207 # /usr/home as it is on FreeBSD, for example
207 # /usr/home as it is on FreeBSD, for example
208 homedir = os.path.realpath(homedir)
208 homedir = os.path.realpath(homedir)
209
209
210 if not _writable_dir(homedir) and os.name == 'nt':
210 if not _writable_dir(homedir) and os.name == 'nt':
211 # expanduser failed, use the registry to get the 'My Documents' folder.
211 # expanduser failed, use the registry to get the 'My Documents' folder.
212 try:
212 try:
213 import _winreg as wreg
213 import _winreg as wreg
214 key = wreg.OpenKey(
214 key = wreg.OpenKey(
215 wreg.HKEY_CURRENT_USER,
215 wreg.HKEY_CURRENT_USER,
216 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
216 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
217 )
217 )
218 homedir = wreg.QueryValueEx(key,'Personal')[0]
218 homedir = wreg.QueryValueEx(key,'Personal')[0]
219 key.Close()
219 key.Close()
220 except:
220 except:
221 pass
221 pass
222
222
223 if (not require_writable) or _writable_dir(homedir):
223 if (not require_writable) or _writable_dir(homedir):
224 return py3compat.cast_unicode(homedir, fs_encoding)
224 return py3compat.cast_unicode(homedir, fs_encoding)
225 else:
225 else:
226 raise HomeDirError('%s is not a writable dir, '
226 raise HomeDirError('%s is not a writable dir, '
227 'set $HOME environment variable to override' % homedir)
227 'set $HOME environment variable to override' % homedir)
228
228
229 def get_xdg_dir():
229 def get_xdg_dir():
230 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
230 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
231
231
232 This is only for non-OS X posix (Linux,Unix,etc.) systems.
232 This is only for non-OS X posix (Linux,Unix,etc.) systems.
233 """
233 """
234
234
235 env = os.environ
235 env = os.environ
236
236
237 if os.name == 'posix' and sys.platform != 'darwin':
237 if os.name == 'posix' and sys.platform != 'darwin':
238 # Linux, Unix, AIX, etc.
238 # Linux, Unix, AIX, etc.
239 # use ~/.config if empty OR not set
239 # use ~/.config if empty OR not set
240 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
240 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
241 if xdg and _writable_dir(xdg):
241 if xdg and _writable_dir(xdg):
242 return py3compat.cast_unicode(xdg, fs_encoding)
242 return py3compat.cast_unicode(xdg, fs_encoding)
243
243
244 return None
244 return None
245
245
246
246
247 def get_ipython_dir():
247 def get_ipython_dir():
248 """Get the IPython directory for this platform and user.
248 """Get the IPython directory for this platform and user.
249
249
250 This uses the logic in `get_home_dir` to find the home directory
250 This uses the logic in `get_home_dir` to find the home directory
251 and then adds .ipython to the end of the path.
251 and then adds .ipython to the end of the path.
252 """
252 """
253
253
254 env = os.environ
254 env = os.environ
255 pjoin = os.path.join
255 pjoin = os.path.join
256
256
257
257
258 ipdir_def = '.ipython'
258 ipdir_def = '.ipython'
259 xdg_def = 'ipython'
259 xdg_def = 'ipython'
260
260
261 home_dir = get_home_dir()
261 home_dir = get_home_dir()
262 xdg_dir = get_xdg_dir()
262 xdg_dir = get_xdg_dir()
263
263
264 # import pdb; pdb.set_trace() # dbg
264 # import pdb; pdb.set_trace() # dbg
265 if 'IPYTHON_DIR' in env:
265 if 'IPYTHON_DIR' in env:
266 warnings.warn('The environment variable IPYTHON_DIR is deprecated. '
266 warnings.warn('The environment variable IPYTHON_DIR is deprecated. '
267 'Please use IPYTHONDIR instead.')
267 'Please use IPYTHONDIR instead.')
268 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
268 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
269 if ipdir is None:
269 if ipdir is None:
270 # not set explicitly, use XDG_CONFIG_HOME or HOME
270 # not set explicitly, use XDG_CONFIG_HOME or HOME
271 home_ipdir = pjoin(home_dir, ipdir_def)
271 home_ipdir = pjoin(home_dir, ipdir_def)
272 if xdg_dir:
272 if xdg_dir:
273 # use XDG, as long as the user isn't already
273 # use XDG, as long as the user isn't already
274 # using $HOME/.ipython and *not* XDG/ipython
274 # using $HOME/.ipython and *not* XDG/ipython
275
275
276 xdg_ipdir = pjoin(xdg_dir, xdg_def)
276 xdg_ipdir = pjoin(xdg_dir, xdg_def)
277
277
278 if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir):
278 if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir):
279 ipdir = xdg_ipdir
279 ipdir = xdg_ipdir
280
280
281 if ipdir is None:
281 if ipdir is None:
282 # not using XDG
282 # not using XDG
283 ipdir = home_ipdir
283 ipdir = home_ipdir
284
284
285 ipdir = os.path.normpath(os.path.expanduser(ipdir))
285 ipdir = os.path.normpath(os.path.expanduser(ipdir))
286
286
287 if os.path.exists(ipdir) and not _writable_dir(ipdir):
287 if os.path.exists(ipdir) and not _writable_dir(ipdir):
288 # ipdir exists, but is not writable
288 # ipdir exists, but is not writable
289 warnings.warn("IPython dir '%s' is not a writable location,"
289 warnings.warn("IPython dir '%s' is not a writable location,"
290 " using a temp directory."%ipdir)
290 " using a temp directory."%ipdir)
291 ipdir = tempfile.mkdtemp()
291 ipdir = tempfile.mkdtemp()
292 elif not os.path.exists(ipdir):
292 elif not os.path.exists(ipdir):
293 parent = ipdir.rsplit(os.path.sep, 1)[0]
293 parent = ipdir.rsplit(os.path.sep, 1)[0]
294 if not _writable_dir(parent):
294 if not _writable_dir(parent):
295 # ipdir does not exist and parent isn't writable
295 # ipdir does not exist and parent isn't writable
296 warnings.warn("IPython parent '%s' is not a writable location,"
296 warnings.warn("IPython parent '%s' is not a writable location,"
297 " using a temp directory."%parent)
297 " using a temp directory."%parent)
298 ipdir = tempfile.mkdtemp()
298 ipdir = tempfile.mkdtemp()
299
299
300 return py3compat.cast_unicode(ipdir, fs_encoding)
300 return py3compat.cast_unicode(ipdir, fs_encoding)
301
301
302
302
303 def get_ipython_package_dir():
303 def get_ipython_package_dir():
304 """Get the base directory where IPython itself is installed."""
304 """Get the base directory where IPython itself is installed."""
305 ipdir = os.path.dirname(IPython.__file__)
305 ipdir = os.path.dirname(IPython.__file__)
306 return py3compat.cast_unicode(ipdir, fs_encoding)
306 return py3compat.cast_unicode(ipdir, fs_encoding)
307
307
308
308
309 def get_ipython_module_path(module_str):
309 def get_ipython_module_path(module_str):
310 """Find the path to an IPython module in this version of IPython.
310 """Find the path to an IPython module in this version of IPython.
311
311
312 This will always find the version of the module that is in this importable
312 This will always find the version of the module that is in this importable
313 IPython package. This will always return the path to the ``.py``
313 IPython package. This will always return the path to the ``.py``
314 version of the module.
314 version of the module.
315 """
315 """
316 if module_str == 'IPython':
316 if module_str == 'IPython':
317 return os.path.join(get_ipython_package_dir(), '__init__.py')
317 return os.path.join(get_ipython_package_dir(), '__init__.py')
318 mod = import_item(module_str)
318 mod = import_item(module_str)
319 the_path = mod.__file__.replace('.pyc', '.py')
319 the_path = mod.__file__.replace('.pyc', '.py')
320 the_path = the_path.replace('.pyo', '.py')
320 the_path = the_path.replace('.pyo', '.py')
321 return py3compat.cast_unicode(the_path, fs_encoding)
321 return py3compat.cast_unicode(the_path, fs_encoding)
322
322
323 def locate_profile(profile='default'):
323 def locate_profile(profile='default'):
324 """Find the path to the folder associated with a given profile.
324 """Find the path to the folder associated with a given profile.
325
325
326 I.e. find $IPYTHONDIR/profile_whatever.
326 I.e. find $IPYTHONDIR/profile_whatever.
327 """
327 """
328 from IPython.core.profiledir import ProfileDir, ProfileDirError
328 from IPython.core.profiledir import ProfileDir, ProfileDirError
329 try:
329 try:
330 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
330 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
331 except ProfileDirError:
331 except ProfileDirError:
332 # IOError makes more sense when people are expecting a path
332 # IOError makes more sense when people are expecting a path
333 raise IOError("Couldn't find profile %r" % profile)
333 raise IOError("Couldn't find profile %r" % profile)
334 return pd.location
334 return pd.location
335
335
336 def expand_path(s):
336 def expand_path(s):
337 """Expand $VARS and ~names in a string, like a shell
337 """Expand $VARS and ~names in a string, like a shell
338
338
339 :Examples:
339 :Examples:
340
340
341 In [2]: os.environ['FOO']='test'
341 In [2]: os.environ['FOO']='test'
342
342
343 In [3]: expand_path('variable FOO is $FOO')
343 In [3]: expand_path('variable FOO is $FOO')
344 Out[3]: 'variable FOO is test'
344 Out[3]: 'variable FOO is test'
345 """
345 """
346 # This is a pretty subtle hack. When expand user is given a UNC path
346 # This is a pretty subtle hack. When expand user is given a UNC path
347 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
347 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
348 # the $ to get (\\server\share\%username%). I think it considered $
348 # the $ to get (\\server\share\%username%). I think it considered $
349 # alone an empty var. But, we need the $ to remains there (it indicates
349 # alone an empty var. But, we need the $ to remains there (it indicates
350 # a hidden share).
350 # a hidden share).
351 if os.name=='nt':
351 if os.name=='nt':
352 s = s.replace('$\\', 'IPYTHON_TEMP')
352 s = s.replace('$\\', 'IPYTHON_TEMP')
353 s = os.path.expandvars(os.path.expanduser(s))
353 s = os.path.expandvars(os.path.expanduser(s))
354 if os.name=='nt':
354 if os.name=='nt':
355 s = s.replace('IPYTHON_TEMP', '$\\')
355 s = s.replace('IPYTHON_TEMP', '$\\')
356 return s
356 return s
357
357
358
358
359 def unescape_glob(string):
359 def unescape_glob(string):
360 """Unescape glob pattern in `string`."""
360 """Unescape glob pattern in `string`."""
361 def unescape(s):
361 def unescape(s):
362 for pattern in '*[]!?':
362 for pattern in '*[]!?':
363 s = s.replace(r'\{0}'.format(pattern), pattern)
363 s = s.replace(r'\{0}'.format(pattern), pattern)
364 return s
364 return s
365 return '\\'.join(map(unescape, string.split('\\\\')))
365 return '\\'.join(map(unescape, string.split('\\\\')))
366
366
367
367
368 def shellglob(args):
368 def shellglob(args):
369 """
369 """
370 Do glob expansion for each element in `args` and return a flattened list.
370 Do glob expansion for each element in `args` and return a flattened list.
371
371
372 Unmatched glob pattern will remain as-is in the returned list.
372 Unmatched glob pattern will remain as-is in the returned list.
373
373
374 """
374 """
375 expanded = []
375 expanded = []
376 # Do not unescape backslash in Windows as it is interpreted as
377 # path separator:
378 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
376 for a in args:
379 for a in args:
377 expanded.extend(glob.glob(a) or [unescape_glob(a)])
380 expanded.extend(glob.glob(a) or [unescape(a)])
378 return expanded
381 return expanded
379
382
380
383
381 def target_outdated(target,deps):
384 def target_outdated(target,deps):
382 """Determine whether a target is out of date.
385 """Determine whether a target is out of date.
383
386
384 target_outdated(target,deps) -> 1/0
387 target_outdated(target,deps) -> 1/0
385
388
386 deps: list of filenames which MUST exist.
389 deps: list of filenames which MUST exist.
387 target: single filename which may or may not exist.
390 target: single filename which may or may not exist.
388
391
389 If target doesn't exist or is older than any file listed in deps, return
392 If target doesn't exist or is older than any file listed in deps, return
390 true, otherwise return false.
393 true, otherwise return false.
391 """
394 """
392 try:
395 try:
393 target_time = os.path.getmtime(target)
396 target_time = os.path.getmtime(target)
394 except os.error:
397 except os.error:
395 return 1
398 return 1
396 for dep in deps:
399 for dep in deps:
397 dep_time = os.path.getmtime(dep)
400 dep_time = os.path.getmtime(dep)
398 if dep_time > target_time:
401 if dep_time > target_time:
399 #print "For target",target,"Dep failed:",dep # dbg
402 #print "For target",target,"Dep failed:",dep # dbg
400 #print "times (dep,tar):",dep_time,target_time # dbg
403 #print "times (dep,tar):",dep_time,target_time # dbg
401 return 1
404 return 1
402 return 0
405 return 0
403
406
404
407
405 def target_update(target,deps,cmd):
408 def target_update(target,deps,cmd):
406 """Update a target with a given command given a list of dependencies.
409 """Update a target with a given command given a list of dependencies.
407
410
408 target_update(target,deps,cmd) -> runs cmd if target is outdated.
411 target_update(target,deps,cmd) -> runs cmd if target is outdated.
409
412
410 This is just a wrapper around target_outdated() which calls the given
413 This is just a wrapper around target_outdated() which calls the given
411 command if target is outdated."""
414 command if target is outdated."""
412
415
413 if target_outdated(target,deps):
416 if target_outdated(target,deps):
414 system(cmd)
417 system(cmd)
415
418
416 def filehash(path):
419 def filehash(path):
417 """Make an MD5 hash of a file, ignoring any differences in line
420 """Make an MD5 hash of a file, ignoring any differences in line
418 ending characters."""
421 ending characters."""
419 with open(path, "rU") as f:
422 with open(path, "rU") as f:
420 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
423 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
421
424
422 # If the config is unmodified from the default, we'll just delete it.
425 # If the config is unmodified from the default, we'll just delete it.
423 # These are consistent for 0.10.x, thankfully. We're not going to worry about
426 # These are consistent for 0.10.x, thankfully. We're not going to worry about
424 # older versions.
427 # older versions.
425 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
428 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
426 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
429 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
427
430
428 def check_for_old_config(ipython_dir=None):
431 def check_for_old_config(ipython_dir=None):
429 """Check for old config files, and present a warning if they exist.
432 """Check for old config files, and present a warning if they exist.
430
433
431 A link to the docs of the new config is included in the message.
434 A link to the docs of the new config is included in the message.
432
435
433 This should mitigate confusion with the transition to the new
436 This should mitigate confusion with the transition to the new
434 config system in 0.11.
437 config system in 0.11.
435 """
438 """
436 if ipython_dir is None:
439 if ipython_dir is None:
437 ipython_dir = get_ipython_dir()
440 ipython_dir = get_ipython_dir()
438
441
439 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
442 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
440 warned = False
443 warned = False
441 for cfg in old_configs:
444 for cfg in old_configs:
442 f = os.path.join(ipython_dir, cfg)
445 f = os.path.join(ipython_dir, cfg)
443 if os.path.exists(f):
446 if os.path.exists(f):
444 if filehash(f) == old_config_md5.get(cfg, ''):
447 if filehash(f) == old_config_md5.get(cfg, ''):
445 os.unlink(f)
448 os.unlink(f)
446 else:
449 else:
447 warnings.warn("Found old IPython config file %r (modified by user)"%f)
450 warnings.warn("Found old IPython config file %r (modified by user)"%f)
448 warned = True
451 warned = True
449
452
450 if warned:
453 if warned:
451 warnings.warn("""
454 warnings.warn("""
452 The IPython configuration system has changed as of 0.11, and these files will
455 The IPython configuration system has changed as of 0.11, and these files will
453 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
456 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
454 of the new config system.
457 of the new config system.
455 To start configuring IPython, do `ipython profile create`, and edit
458 To start configuring IPython, do `ipython profile create`, and edit
456 `ipython_config.py` in <ipython_dir>/profile_default.
459 `ipython_config.py` in <ipython_dir>/profile_default.
457 If you need to leave the old config files in place for an older version of
460 If you need to leave the old config files in place for an older version of
458 IPython and want to suppress this warning message, set
461 IPython and want to suppress this warning message, set
459 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
462 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
460
463
461 def get_security_file(filename, profile='default'):
464 def get_security_file(filename, profile='default'):
462 """Return the absolute path of a security file given by filename and profile
465 """Return the absolute path of a security file given by filename and profile
463
466
464 This allows users and developers to find security files without
467 This allows users and developers to find security files without
465 knowledge of the IPython directory structure. The search path
468 knowledge of the IPython directory structure. The search path
466 will be ['.', profile.security_dir]
469 will be ['.', profile.security_dir]
467
470
468 Parameters
471 Parameters
469 ----------
472 ----------
470
473
471 filename : str
474 filename : str
472 The file to be found. If it is passed as an absolute path, it will
475 The file to be found. If it is passed as an absolute path, it will
473 simply be returned.
476 simply be returned.
474 profile : str [default: 'default']
477 profile : str [default: 'default']
475 The name of the profile to search. Leaving this unspecified
478 The name of the profile to search. Leaving this unspecified
476 The file to be found. If it is passed as an absolute path, fname will
479 The file to be found. If it is passed as an absolute path, fname will
477 simply be returned.
480 simply be returned.
478
481
479 Returns
482 Returns
480 -------
483 -------
481 Raises :exc:`IOError` if file not found or returns absolute path to file.
484 Raises :exc:`IOError` if file not found or returns absolute path to file.
482 """
485 """
483 # import here, because profiledir also imports from utils.path
486 # import here, because profiledir also imports from utils.path
484 from IPython.core.profiledir import ProfileDir
487 from IPython.core.profiledir import ProfileDir
485 try:
488 try:
486 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
489 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
487 except Exception:
490 except Exception:
488 # will raise ProfileDirError if no such profile
491 # will raise ProfileDirError if no such profile
489 raise IOError("Profile %r not found")
492 raise IOError("Profile %r not found")
490 return filefind(filename, ['.', pd.security_dir])
493 return filefind(filename, ['.', pd.security_dir])
491
494
@@ -1,124 +1,124 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """ Imports and provides the 'correct' version of readline for the platform.
2 """ Imports and provides the 'correct' version of readline for the platform.
3
3
4 Readline is used throughout IPython as::
4 Readline is used throughout IPython as::
5
5
6 import IPython.utils.rlineimpl as readline
6 import IPython.utils.rlineimpl as readline
7
7
8 In addition to normal readline stuff, this module provides have_readline
8 In addition to normal readline stuff, this module provides have_readline
9 boolean and _outputfile variable used in IPython.utils.
9 boolean and _outputfile variable used in IPython.utils.
10 """
10 """
11
11
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15 import time
15 import time
16 import warnings
16 import warnings
17
17
18 from subprocess import Popen, PIPE
18 from subprocess import Popen, PIPE
19
19
20 if sys.platform == 'darwin':
20 if sys.platform == 'darwin':
21 # dirty trick, to skip the system readline, because pip-installed readline
21 # dirty trick, to skip the system readline, because pip-installed readline
22 # will never be found on OSX, since lib-dynload always comes ahead of site-packages
22 # will never be found on OSX, since lib-dynload always comes ahead of site-packages
23 from distutils import sysconfig
23 from distutils import sysconfig
24 lib_dynload = sysconfig.get_config_var('DESTSHARED')
24 lib_dynload = sysconfig.get_config_var('DESTSHARED')
25 del sysconfig
25 del sysconfig
26 try:
26 try:
27 dynload_idx = sys.path.index(lib_dynload)
27 dynload_idx = sys.path.index(lib_dynload)
28 except ValueError:
28 except ValueError:
29 dynload_idx = None
29 dynload_idx = None
30 else:
30 else:
31 sys.path.pop(dynload_idx)
31 sys.path.pop(dynload_idx)
32 try:
32 try:
33 from readline import *
33 from readline import *
34 import readline as _rl
34 import readline as _rl
35 have_readline = True
35 have_readline = True
36 except ImportError:
36 except ImportError:
37 try:
37 try:
38 from pyreadline import *
38 from pyreadline import *
39 import pyreadline as _rl
39 import pyreadline as _rl
40 have_readline = True
40 have_readline = True
41 except ImportError:
41 except ImportError:
42 have_readline = False
42 have_readline = False
43
43
44 if sys.platform == 'darwin':
44 if sys.platform == 'darwin':
45 # dirty trick, part II:
45 # dirty trick, part II:
46 if dynload_idx is not None:
46 if dynload_idx is not None:
47 # restore path
47 # restore path
48 sys.path.insert(dynload_idx, lib_dynload)
48 sys.path.insert(dynload_idx, lib_dynload)
49 if not have_readline:
49 if not have_readline:
50 # *only* have system readline, try import again
50 # *only* have system readline, try import again
51 try:
51 try:
52 from readline import *
52 from readline import *
53 import readline as _rl
53 import readline as _rl
54 have_readline = True
54 have_readline = True
55 except ImportError:
55 except ImportError:
56 have_readline = False
56 have_readline = False
57 else:
57 else:
58 # if we want to warn about EPD / Fink having bad readline
58 # if we want to warn about EPD / Fink having bad readline
59 # we would do it here
59 # we would do it here
60 pass
60 pass
61 # cleanup dirty trick vars
61 # cleanup dirty trick vars
62 del dynload_idx, lib_dynload
62 del dynload_idx, lib_dynload
63
63
64 if have_readline and hasattr(_rl, 'rlmain'):
64 if have_readline and hasattr(_rl, 'rlmain'):
65 # patch add_history to allow for strings in pyreadline <= 1.5:
65 # patch add_history to allow for strings in pyreadline <= 1.5:
66 # fix copied from pyreadline 1.6
66 # fix copied from pyreadline 1.6
67 import pyreadline
67 import pyreadline
68 if pyreadline.release.version <= '1.5':
68 if pyreadline.release.version <= '1.5':
69 def add_history(line):
69 def add_history(line):
70 """add a line to the history buffer."""
70 """add a line to the history buffer."""
71 from pyreadline import lineobj
71 from pyreadline import lineobj
72 if not isinstance(line, lineobj.TextLine):
72 if not isinstance(line, lineobj.TextLine):
73 line = lineobj.TextLine(line)
73 line = lineobj.TextLine(line)
74 return _rl.add_history(line)
74 return _rl.add_history(line)
75
75
76 if sys.platform == 'win32' and have_readline:
76 if (sys.platform == 'win32' or sys.platform == 'cli') and have_readline:
77 try:
77 try:
78 _outputfile=_rl.GetOutputFile()
78 _outputfile=_rl.GetOutputFile()
79 except AttributeError:
79 except AttributeError:
80 warnings.warn("Failed GetOutputFile")
80 warnings.warn("Failed GetOutputFile")
81 have_readline = False
81 have_readline = False
82
82
83 # Test to see if libedit is being used instead of GNU readline.
83 # Test to see if libedit is being used instead of GNU readline.
84 # Thanks to Boyd Waters for the original patch.
84 # Thanks to Boyd Waters for the original patch.
85 uses_libedit = False
85 uses_libedit = False
86
86
87 if have_readline:
87 if have_readline:
88 # Official Python docs state that 'libedit' is in the docstring for libedit readline:
88 # Official Python docs state that 'libedit' is in the docstring for libedit readline:
89 uses_libedit = _rl.__doc__ and 'libedit' in _rl.__doc__
89 uses_libedit = _rl.__doc__ and 'libedit' in _rl.__doc__
90 # Note that many non-System Pythons also do not use proper readline,
90 # Note that many non-System Pythons also do not use proper readline,
91 # but do not report libedit at all, nor are they linked dynamically against libedit.
91 # but do not report libedit at all, nor are they linked dynamically against libedit.
92 # known culprits of this include: EPD, Fink
92 # known culprits of this include: EPD, Fink
93 # There is not much we can do to detect this, until we find a specific failure
93 # There is not much we can do to detect this, until we find a specific failure
94 # case, rather than relying on the readline module to self-identify as broken.
94 # case, rather than relying on the readline module to self-identify as broken.
95
95
96 if uses_libedit and sys.platform == 'darwin':
96 if uses_libedit and sys.platform == 'darwin':
97 _rl.parse_and_bind("bind ^I rl_complete")
97 _rl.parse_and_bind("bind ^I rl_complete")
98 warnings.warn('\n'.join(['', "*"*78,
98 warnings.warn('\n'.join(['', "*"*78,
99 "libedit detected - readline will not be well behaved, including but not limited to:",
99 "libedit detected - readline will not be well behaved, including but not limited to:",
100 " * crashes on tab completion",
100 " * crashes on tab completion",
101 " * incorrect history navigation",
101 " * incorrect history navigation",
102 " * corrupting long-lines",
102 " * corrupting long-lines",
103 " * failure to wrap or indent lines properly",
103 " * failure to wrap or indent lines properly",
104 "It is highly recommended that you install readline, which is easy_installable:",
104 "It is highly recommended that you install readline, which is easy_installable:",
105 " easy_install readline",
105 " easy_install readline",
106 "Note that `pip install readline` generally DOES NOT WORK, because",
106 "Note that `pip install readline` generally DOES NOT WORK, because",
107 "it installs to site-packages, which come *after* lib-dynload in sys.path,",
107 "it installs to site-packages, which come *after* lib-dynload in sys.path,",
108 "where readline is located. It must be `easy_install readline`, or to a custom",
108 "where readline is located. It must be `easy_install readline`, or to a custom",
109 "location on your PYTHONPATH (even --user comes after lib-dyload).",
109 "location on your PYTHONPATH (even --user comes after lib-dyload).",
110 "*"*78]),
110 "*"*78]),
111 RuntimeWarning)
111 RuntimeWarning)
112
112
113 # the clear_history() function was only introduced in Python 2.4 and is
113 # the clear_history() function was only introduced in Python 2.4 and is
114 # actually optional in the readline API, so we must explicitly check for its
114 # actually optional in the readline API, so we must explicitly check for its
115 # existence. Some known platforms actually don't have it. This thread:
115 # existence. Some known platforms actually don't have it. This thread:
116 # http://mail.python.org/pipermail/python-dev/2003-August/037845.html
116 # http://mail.python.org/pipermail/python-dev/2003-August/037845.html
117 # has the original discussion.
117 # has the original discussion.
118
118
119 if have_readline:
119 if have_readline:
120 try:
120 try:
121 _rl.clear_history
121 _rl.clear_history
122 except AttributeError:
122 except AttributeError:
123 def clear_history(): pass
123 def clear_history(): pass
124 _rl.clear_history = clear_history
124 _rl.clear_history = clear_history
@@ -1,492 +1,524 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.utils.path.py"""
2 """Tests for IPython.utils.path.py"""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008-2011 The IPython Development Team
5 # Copyright (C) 2008-2011 The IPython Development Team
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 from __future__ import with_statement
15 from __future__ import with_statement
16
16
17 import os
17 import os
18 import shutil
18 import shutil
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 from io import StringIO
21 from io import StringIO
22 from contextlib import contextmanager
22
23
23 from os.path import join, abspath, split
24 from os.path import join, abspath, split
24
25
25 import nose.tools as nt
26 import nose.tools as nt
26
27
27 from nose import with_setup
28 from nose import with_setup
28
29
29 import IPython
30 import IPython
30 from IPython.testing import decorators as dec
31 from IPython.testing import decorators as dec
31 from IPython.testing.decorators import skip_if_not_win32, skip_win32
32 from IPython.testing.decorators import skip_if_not_win32, skip_win32
32 from IPython.testing.tools import make_tempfile, AssertPrints
33 from IPython.testing.tools import make_tempfile, AssertPrints
33 from IPython.utils import path, io
34 from IPython.utils import path, io
34 from IPython.utils import py3compat
35 from IPython.utils import py3compat
35 from IPython.utils.tempdir import TemporaryDirectory
36 from IPython.utils.tempdir import TemporaryDirectory
36
37
37 # Platform-dependent imports
38 # Platform-dependent imports
38 try:
39 try:
39 import _winreg as wreg
40 import _winreg as wreg
40 except ImportError:
41 except ImportError:
41 #Fake _winreg module on none windows platforms
42 #Fake _winreg module on none windows platforms
42 import types
43 import types
43 wr_name = "winreg" if py3compat.PY3 else "_winreg"
44 wr_name = "winreg" if py3compat.PY3 else "_winreg"
44 sys.modules[wr_name] = types.ModuleType(wr_name)
45 sys.modules[wr_name] = types.ModuleType(wr_name)
45 import _winreg as wreg
46 import _winreg as wreg
46 #Add entries that needs to be stubbed by the testing code
47 #Add entries that needs to be stubbed by the testing code
47 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
48 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
48
49
49 try:
50 try:
50 reload
51 reload
51 except NameError: # Python 3
52 except NameError: # Python 3
52 from imp import reload
53 from imp import reload
53
54
54 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
55 # Globals
56 # Globals
56 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
57 env = os.environ
58 env = os.environ
58 TEST_FILE_PATH = split(abspath(__file__))[0]
59 TEST_FILE_PATH = split(abspath(__file__))[0]
59 TMP_TEST_DIR = tempfile.mkdtemp()
60 TMP_TEST_DIR = tempfile.mkdtemp()
60 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
61 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
61 XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
62 XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
62 IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
63 IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
63 #
64 #
64 # Setup/teardown functions/decorators
65 # Setup/teardown functions/decorators
65 #
66 #
66
67
67 def setup():
68 def setup():
68 """Setup testenvironment for the module:
69 """Setup testenvironment for the module:
69
70
70 - Adds dummy home dir tree
71 - Adds dummy home dir tree
71 """
72 """
72 # Do not mask exceptions here. In particular, catching WindowsError is a
73 # Do not mask exceptions here. In particular, catching WindowsError is a
73 # problem because that exception is only defined on Windows...
74 # problem because that exception is only defined on Windows...
74 os.makedirs(IP_TEST_DIR)
75 os.makedirs(IP_TEST_DIR)
75 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
76 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
76
77
77
78
78 def teardown():
79 def teardown():
79 """Teardown testenvironment for the module:
80 """Teardown testenvironment for the module:
80
81
81 - Remove dummy home dir tree
82 - Remove dummy home dir tree
82 """
83 """
83 # Note: we remove the parent test dir, which is the root of all test
84 # Note: we remove the parent test dir, which is the root of all test
84 # subdirs we may have created. Use shutil instead of os.removedirs, so
85 # subdirs we may have created. Use shutil instead of os.removedirs, so
85 # that non-empty directories are all recursively removed.
86 # that non-empty directories are all recursively removed.
86 shutil.rmtree(TMP_TEST_DIR)
87 shutil.rmtree(TMP_TEST_DIR)
87
88
88
89
89 def setup_environment():
90 def setup_environment():
90 """Setup testenvironment for some functions that are tested
91 """Setup testenvironment for some functions that are tested
91 in this module. In particular this functions stores attributes
92 in this module. In particular this functions stores attributes
92 and other things that we need to stub in some test functions.
93 and other things that we need to stub in some test functions.
93 This needs to be done on a function level and not module level because
94 This needs to be done on a function level and not module level because
94 each testfunction needs a pristine environment.
95 each testfunction needs a pristine environment.
95 """
96 """
96 global oldstuff, platformstuff
97 global oldstuff, platformstuff
97 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
98 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
98
99
99 if os.name == 'nt':
100 if os.name == 'nt':
100 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
101 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
101
102
102
103
103 def teardown_environment():
104 def teardown_environment():
104 """Restore things that were remebered by the setup_environment function
105 """Restore things that were remebered by the setup_environment function
105 """
106 """
106 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
107 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
107 os.chdir(old_wd)
108 os.chdir(old_wd)
108 reload(path)
109 reload(path)
109
110
110 for key in env.keys():
111 for key in env.keys():
111 if key not in oldenv:
112 if key not in oldenv:
112 del env[key]
113 del env[key]
113 env.update(oldenv)
114 env.update(oldenv)
114 if hasattr(sys, 'frozen'):
115 if hasattr(sys, 'frozen'):
115 del sys.frozen
116 del sys.frozen
116 if os.name == 'nt':
117 if os.name == 'nt':
117 (wreg.OpenKey, wreg.QueryValueEx,) = platformstuff
118 (wreg.OpenKey, wreg.QueryValueEx,) = platformstuff
118
119
119 # Build decorator that uses the setup_environment/setup_environment
120 # Build decorator that uses the setup_environment/setup_environment
120 with_environment = with_setup(setup_environment, teardown_environment)
121 with_environment = with_setup(setup_environment, teardown_environment)
121
122
122 @skip_if_not_win32
123 @skip_if_not_win32
123 @with_environment
124 @with_environment
124 def test_get_home_dir_1():
125 def test_get_home_dir_1():
125 """Testcase for py2exe logic, un-compressed lib
126 """Testcase for py2exe logic, un-compressed lib
126 """
127 """
127 sys.frozen = True
128 sys.frozen = True
128
129
129 #fake filename for IPython.__init__
130 #fake filename for IPython.__init__
130 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
131 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
131
132
132 home_dir = path.get_home_dir()
133 home_dir = path.get_home_dir()
133 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
134 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
134
135
135
136
136 @skip_if_not_win32
137 @skip_if_not_win32
137 @with_environment
138 @with_environment
138 def test_get_home_dir_2():
139 def test_get_home_dir_2():
139 """Testcase for py2exe logic, compressed lib
140 """Testcase for py2exe logic, compressed lib
140 """
141 """
141 sys.frozen = True
142 sys.frozen = True
142 #fake filename for IPython.__init__
143 #fake filename for IPython.__init__
143 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
144 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
144
145
145 home_dir = path.get_home_dir(True)
146 home_dir = path.get_home_dir(True)
146 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
147 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
147
148
148
149
149 @with_environment
150 @with_environment
150 def test_get_home_dir_3():
151 def test_get_home_dir_3():
151 """get_home_dir() uses $HOME if set"""
152 """get_home_dir() uses $HOME if set"""
152 env["HOME"] = HOME_TEST_DIR
153 env["HOME"] = HOME_TEST_DIR
153 home_dir = path.get_home_dir(True)
154 home_dir = path.get_home_dir(True)
154 # get_home_dir expands symlinks
155 # get_home_dir expands symlinks
155 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
156 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
156
157
157
158
158 @with_environment
159 @with_environment
159 def test_get_home_dir_4():
160 def test_get_home_dir_4():
160 """get_home_dir() still works if $HOME is not set"""
161 """get_home_dir() still works if $HOME is not set"""
161
162
162 if 'HOME' in env: del env['HOME']
163 if 'HOME' in env: del env['HOME']
163 # this should still succeed, but we don't care what the answer is
164 # this should still succeed, but we don't care what the answer is
164 home = path.get_home_dir(False)
165 home = path.get_home_dir(False)
165
166
166 @with_environment
167 @with_environment
167 def test_get_home_dir_5():
168 def test_get_home_dir_5():
168 """raise HomeDirError if $HOME is specified, but not a writable dir"""
169 """raise HomeDirError if $HOME is specified, but not a writable dir"""
169 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
170 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
170 # set os.name = posix, to prevent My Documents fallback on Windows
171 # set os.name = posix, to prevent My Documents fallback on Windows
171 os.name = 'posix'
172 os.name = 'posix'
172 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
173 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
173
174
174
175
175 # Should we stub wreg fully so we can run the test on all platforms?
176 # Should we stub wreg fully so we can run the test on all platforms?
176 @skip_if_not_win32
177 @skip_if_not_win32
177 @with_environment
178 @with_environment
178 def test_get_home_dir_8():
179 def test_get_home_dir_8():
179 """Using registry hack for 'My Documents', os=='nt'
180 """Using registry hack for 'My Documents', os=='nt'
180
181
181 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
182 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
182 """
183 """
183 os.name = 'nt'
184 os.name = 'nt'
184 # Remove from stub environment all keys that may be set
185 # Remove from stub environment all keys that may be set
185 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
186 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
186 env.pop(key, None)
187 env.pop(key, None)
187
188
188 #Stub windows registry functions
189 #Stub windows registry functions
189 def OpenKey(x, y):
190 def OpenKey(x, y):
190 class key:
191 class key:
191 def Close(self):
192 def Close(self):
192 pass
193 pass
193 return key()
194 return key()
194 def QueryValueEx(x, y):
195 def QueryValueEx(x, y):
195 return [abspath(HOME_TEST_DIR)]
196 return [abspath(HOME_TEST_DIR)]
196
197
197 wreg.OpenKey = OpenKey
198 wreg.OpenKey = OpenKey
198 wreg.QueryValueEx = QueryValueEx
199 wreg.QueryValueEx = QueryValueEx
199
200
200 home_dir = path.get_home_dir()
201 home_dir = path.get_home_dir()
201 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
202 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
202
203
203
204
204 @with_environment
205 @with_environment
205 def test_get_ipython_dir_1():
206 def test_get_ipython_dir_1():
206 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
207 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
207 env_ipdir = os.path.join("someplace", ".ipython")
208 env_ipdir = os.path.join("someplace", ".ipython")
208 path._writable_dir = lambda path: True
209 path._writable_dir = lambda path: True
209 env['IPYTHONDIR'] = env_ipdir
210 env['IPYTHONDIR'] = env_ipdir
210 ipdir = path.get_ipython_dir()
211 ipdir = path.get_ipython_dir()
211 nt.assert_equal(ipdir, env_ipdir)
212 nt.assert_equal(ipdir, env_ipdir)
212
213
213
214
214 @with_environment
215 @with_environment
215 def test_get_ipython_dir_2():
216 def test_get_ipython_dir_2():
216 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
217 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
217 path.get_home_dir = lambda : "someplace"
218 path.get_home_dir = lambda : "someplace"
218 path.get_xdg_dir = lambda : None
219 path.get_xdg_dir = lambda : None
219 path._writable_dir = lambda path: True
220 path._writable_dir = lambda path: True
220 os.name = "posix"
221 os.name = "posix"
221 env.pop('IPYTHON_DIR', None)
222 env.pop('IPYTHON_DIR', None)
222 env.pop('IPYTHONDIR', None)
223 env.pop('IPYTHONDIR', None)
223 env.pop('XDG_CONFIG_HOME', None)
224 env.pop('XDG_CONFIG_HOME', None)
224 ipdir = path.get_ipython_dir()
225 ipdir = path.get_ipython_dir()
225 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
226 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
226
227
227 @with_environment
228 @with_environment
228 def test_get_ipython_dir_3():
229 def test_get_ipython_dir_3():
229 """test_get_ipython_dir_3, use XDG if defined, and .ipython doesn't exist."""
230 """test_get_ipython_dir_3, use XDG if defined, and .ipython doesn't exist."""
230 path.get_home_dir = lambda : "someplace"
231 path.get_home_dir = lambda : "someplace"
231 path._writable_dir = lambda path: True
232 path._writable_dir = lambda path: True
232 os.name = "posix"
233 os.name = "posix"
233 env.pop('IPYTHON_DIR', None)
234 env.pop('IPYTHON_DIR', None)
234 env.pop('IPYTHONDIR', None)
235 env.pop('IPYTHONDIR', None)
235 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
236 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
236 ipdir = path.get_ipython_dir()
237 ipdir = path.get_ipython_dir()
237 if sys.platform == "darwin":
238 if sys.platform == "darwin":
238 expected = os.path.join("someplace", ".ipython")
239 expected = os.path.join("someplace", ".ipython")
239 else:
240 else:
240 expected = os.path.join(XDG_TEST_DIR, "ipython")
241 expected = os.path.join(XDG_TEST_DIR, "ipython")
241 nt.assert_equal(ipdir, expected)
242 nt.assert_equal(ipdir, expected)
242
243
243 @with_environment
244 @with_environment
244 def test_get_ipython_dir_4():
245 def test_get_ipython_dir_4():
245 """test_get_ipython_dir_4, use XDG if both exist."""
246 """test_get_ipython_dir_4, use XDG if both exist."""
246 path.get_home_dir = lambda : HOME_TEST_DIR
247 path.get_home_dir = lambda : HOME_TEST_DIR
247 os.name = "posix"
248 os.name = "posix"
248 env.pop('IPYTHON_DIR', None)
249 env.pop('IPYTHON_DIR', None)
249 env.pop('IPYTHONDIR', None)
250 env.pop('IPYTHONDIR', None)
250 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
251 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
251 ipdir = path.get_ipython_dir()
252 ipdir = path.get_ipython_dir()
252 if sys.platform == "darwin":
253 if sys.platform == "darwin":
253 expected = os.path.join(HOME_TEST_DIR, ".ipython")
254 expected = os.path.join(HOME_TEST_DIR, ".ipython")
254 else:
255 else:
255 expected = os.path.join(XDG_TEST_DIR, "ipython")
256 expected = os.path.join(XDG_TEST_DIR, "ipython")
256 nt.assert_equal(ipdir, expected)
257 nt.assert_equal(ipdir, expected)
257
258
258 @with_environment
259 @with_environment
259 def test_get_ipython_dir_5():
260 def test_get_ipython_dir_5():
260 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
261 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
261 path.get_home_dir = lambda : HOME_TEST_DIR
262 path.get_home_dir = lambda : HOME_TEST_DIR
262 os.name = "posix"
263 os.name = "posix"
263 env.pop('IPYTHON_DIR', None)
264 env.pop('IPYTHON_DIR', None)
264 env.pop('IPYTHONDIR', None)
265 env.pop('IPYTHONDIR', None)
265 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
266 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
266 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
267 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
267 ipdir = path.get_ipython_dir()
268 ipdir = path.get_ipython_dir()
268 nt.assert_equal(ipdir, IP_TEST_DIR)
269 nt.assert_equal(ipdir, IP_TEST_DIR)
269
270
270 @with_environment
271 @with_environment
271 def test_get_ipython_dir_6():
272 def test_get_ipython_dir_6():
272 """test_get_ipython_dir_6, use XDG if defined and neither exist."""
273 """test_get_ipython_dir_6, use XDG if defined and neither exist."""
273 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
274 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
274 os.mkdir(xdg)
275 os.mkdir(xdg)
275 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
276 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
276 path.get_home_dir = lambda : HOME_TEST_DIR
277 path.get_home_dir = lambda : HOME_TEST_DIR
277 path.get_xdg_dir = lambda : xdg
278 path.get_xdg_dir = lambda : xdg
278 os.name = "posix"
279 os.name = "posix"
279 env.pop('IPYTHON_DIR', None)
280 env.pop('IPYTHON_DIR', None)
280 env.pop('IPYTHONDIR', None)
281 env.pop('IPYTHONDIR', None)
281 env.pop('XDG_CONFIG_HOME', None)
282 env.pop('XDG_CONFIG_HOME', None)
282 xdg_ipdir = os.path.join(xdg, "ipython")
283 xdg_ipdir = os.path.join(xdg, "ipython")
283 ipdir = path.get_ipython_dir()
284 ipdir = path.get_ipython_dir()
284 nt.assert_equal(ipdir, xdg_ipdir)
285 nt.assert_equal(ipdir, xdg_ipdir)
285
286
286 @with_environment
287 @with_environment
287 def test_get_ipython_dir_7():
288 def test_get_ipython_dir_7():
288 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
289 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
289 path._writable_dir = lambda path: True
290 path._writable_dir = lambda path: True
290 home_dir = os.path.normpath(os.path.expanduser('~'))
291 home_dir = os.path.normpath(os.path.expanduser('~'))
291 env['IPYTHONDIR'] = os.path.join('~', 'somewhere')
292 env['IPYTHONDIR'] = os.path.join('~', 'somewhere')
292 ipdir = path.get_ipython_dir()
293 ipdir = path.get_ipython_dir()
293 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
294 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
294
295
295
296
296 @with_environment
297 @with_environment
297 def test_get_xdg_dir_0():
298 def test_get_xdg_dir_0():
298 """test_get_xdg_dir_0, check xdg_dir"""
299 """test_get_xdg_dir_0, check xdg_dir"""
299 reload(path)
300 reload(path)
300 path._writable_dir = lambda path: True
301 path._writable_dir = lambda path: True
301 path.get_home_dir = lambda : 'somewhere'
302 path.get_home_dir = lambda : 'somewhere'
302 os.name = "posix"
303 os.name = "posix"
303 sys.platform = "linux2"
304 sys.platform = "linux2"
304 env.pop('IPYTHON_DIR', None)
305 env.pop('IPYTHON_DIR', None)
305 env.pop('IPYTHONDIR', None)
306 env.pop('IPYTHONDIR', None)
306 env.pop('XDG_CONFIG_HOME', None)
307 env.pop('XDG_CONFIG_HOME', None)
307
308
308 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
309 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
309
310
310
311
311 @with_environment
312 @with_environment
312 def test_get_xdg_dir_1():
313 def test_get_xdg_dir_1():
313 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
314 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
314 reload(path)
315 reload(path)
315 path.get_home_dir = lambda : HOME_TEST_DIR
316 path.get_home_dir = lambda : HOME_TEST_DIR
316 os.name = "posix"
317 os.name = "posix"
317 sys.platform = "linux2"
318 sys.platform = "linux2"
318 env.pop('IPYTHON_DIR', None)
319 env.pop('IPYTHON_DIR', None)
319 env.pop('IPYTHONDIR', None)
320 env.pop('IPYTHONDIR', None)
320 env.pop('XDG_CONFIG_HOME', None)
321 env.pop('XDG_CONFIG_HOME', None)
321 nt.assert_equal(path.get_xdg_dir(), None)
322 nt.assert_equal(path.get_xdg_dir(), None)
322
323
323 @with_environment
324 @with_environment
324 def test_get_xdg_dir_2():
325 def test_get_xdg_dir_2():
325 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
326 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
326 reload(path)
327 reload(path)
327 path.get_home_dir = lambda : HOME_TEST_DIR
328 path.get_home_dir = lambda : HOME_TEST_DIR
328 os.name = "posix"
329 os.name = "posix"
329 sys.platform = "linux2"
330 sys.platform = "linux2"
330 env.pop('IPYTHON_DIR', None)
331 env.pop('IPYTHON_DIR', None)
331 env.pop('IPYTHONDIR', None)
332 env.pop('IPYTHONDIR', None)
332 env.pop('XDG_CONFIG_HOME', None)
333 env.pop('XDG_CONFIG_HOME', None)
333 cfgdir=os.path.join(path.get_home_dir(), '.config')
334 cfgdir=os.path.join(path.get_home_dir(), '.config')
334 if not os.path.exists(cfgdir):
335 if not os.path.exists(cfgdir):
335 os.makedirs(cfgdir)
336 os.makedirs(cfgdir)
336
337
337 nt.assert_equal(path.get_xdg_dir(), cfgdir)
338 nt.assert_equal(path.get_xdg_dir(), cfgdir)
338
339
339 @with_environment
340 @with_environment
340 def test_get_xdg_dir_3():
341 def test_get_xdg_dir_3():
341 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
342 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
342 reload(path)
343 reload(path)
343 path.get_home_dir = lambda : HOME_TEST_DIR
344 path.get_home_dir = lambda : HOME_TEST_DIR
344 os.name = "posix"
345 os.name = "posix"
345 sys.platform = "darwin"
346 sys.platform = "darwin"
346 env.pop('IPYTHON_DIR', None)
347 env.pop('IPYTHON_DIR', None)
347 env.pop('IPYTHONDIR', None)
348 env.pop('IPYTHONDIR', None)
348 env.pop('XDG_CONFIG_HOME', None)
349 env.pop('XDG_CONFIG_HOME', None)
349 cfgdir=os.path.join(path.get_home_dir(), '.config')
350 cfgdir=os.path.join(path.get_home_dir(), '.config')
350 if not os.path.exists(cfgdir):
351 if not os.path.exists(cfgdir):
351 os.makedirs(cfgdir)
352 os.makedirs(cfgdir)
352
353
353 nt.assert_equal(path.get_xdg_dir(), None)
354 nt.assert_equal(path.get_xdg_dir(), None)
354
355
355 def test_filefind():
356 def test_filefind():
356 """Various tests for filefind"""
357 """Various tests for filefind"""
357 f = tempfile.NamedTemporaryFile()
358 f = tempfile.NamedTemporaryFile()
358 # print 'fname:',f.name
359 # print 'fname:',f.name
359 alt_dirs = path.get_ipython_dir()
360 alt_dirs = path.get_ipython_dir()
360 t = path.filefind(f.name, alt_dirs)
361 t = path.filefind(f.name, alt_dirs)
361 # print 'found:',t
362 # print 'found:',t
362
363
363
364
364 def test_get_ipython_package_dir():
365 def test_get_ipython_package_dir():
365 ipdir = path.get_ipython_package_dir()
366 ipdir = path.get_ipython_package_dir()
366 nt.assert_true(os.path.isdir(ipdir))
367 nt.assert_true(os.path.isdir(ipdir))
367
368
368
369
369 def test_get_ipython_module_path():
370 def test_get_ipython_module_path():
370 ipapp_path = path.get_ipython_module_path('IPython.frontend.terminal.ipapp')
371 ipapp_path = path.get_ipython_module_path('IPython.frontend.terminal.ipapp')
371 nt.assert_true(os.path.isfile(ipapp_path))
372 nt.assert_true(os.path.isfile(ipapp_path))
372
373
373
374
374 @dec.skip_if_not_win32
375 @dec.skip_if_not_win32
375 def test_get_long_path_name_win32():
376 def test_get_long_path_name_win32():
376 p = path.get_long_path_name('c:\\docume~1')
377 p = path.get_long_path_name('c:\\docume~1')
377 nt.assert_equal(p,u'c:\\Documents and Settings')
378 nt.assert_equal(p,u'c:\\Documents and Settings')
378
379
379
380
380 @dec.skip_win32
381 @dec.skip_win32
381 def test_get_long_path_name():
382 def test_get_long_path_name():
382 p = path.get_long_path_name('/usr/local')
383 p = path.get_long_path_name('/usr/local')
383 nt.assert_equal(p,'/usr/local')
384 nt.assert_equal(p,'/usr/local')
384
385
385 @dec.skip_win32 # can't create not-user-writable dir on win
386 @dec.skip_win32 # can't create not-user-writable dir on win
386 @with_environment
387 @with_environment
387 def test_not_writable_ipdir():
388 def test_not_writable_ipdir():
388 tmpdir = tempfile.mkdtemp()
389 tmpdir = tempfile.mkdtemp()
389 os.name = "posix"
390 os.name = "posix"
390 env.pop('IPYTHON_DIR', None)
391 env.pop('IPYTHON_DIR', None)
391 env.pop('IPYTHONDIR', None)
392 env.pop('IPYTHONDIR', None)
392 env.pop('XDG_CONFIG_HOME', None)
393 env.pop('XDG_CONFIG_HOME', None)
393 env['HOME'] = tmpdir
394 env['HOME'] = tmpdir
394 ipdir = os.path.join(tmpdir, '.ipython')
395 ipdir = os.path.join(tmpdir, '.ipython')
395 os.mkdir(ipdir)
396 os.mkdir(ipdir)
396 os.chmod(ipdir, 600)
397 os.chmod(ipdir, 600)
397 with AssertPrints('is not a writable location', channel='stderr'):
398 with AssertPrints('is not a writable location', channel='stderr'):
398 ipdir = path.get_ipython_dir()
399 ipdir = path.get_ipython_dir()
399 env.pop('IPYTHON_DIR', None)
400 env.pop('IPYTHON_DIR', None)
400
401
401 def test_unquote_filename():
402 def test_unquote_filename():
402 for win32 in (True, False):
403 for win32 in (True, False):
403 nt.assert_equal(path.unquote_filename('foo.py', win32=win32), 'foo.py')
404 nt.assert_equal(path.unquote_filename('foo.py', win32=win32), 'foo.py')
404 nt.assert_equal(path.unquote_filename('foo bar.py', win32=win32), 'foo bar.py')
405 nt.assert_equal(path.unquote_filename('foo bar.py', win32=win32), 'foo bar.py')
405 nt.assert_equal(path.unquote_filename('"foo.py"', win32=True), 'foo.py')
406 nt.assert_equal(path.unquote_filename('"foo.py"', win32=True), 'foo.py')
406 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=True), 'foo bar.py')
407 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=True), 'foo bar.py')
407 nt.assert_equal(path.unquote_filename("'foo.py'", win32=True), 'foo.py')
408 nt.assert_equal(path.unquote_filename("'foo.py'", win32=True), 'foo.py')
408 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=True), 'foo bar.py')
409 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=True), 'foo bar.py')
409 nt.assert_equal(path.unquote_filename('"foo.py"', win32=False), '"foo.py"')
410 nt.assert_equal(path.unquote_filename('"foo.py"', win32=False), '"foo.py"')
410 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=False), '"foo bar.py"')
411 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=False), '"foo bar.py"')
411 nt.assert_equal(path.unquote_filename("'foo.py'", win32=False), "'foo.py'")
412 nt.assert_equal(path.unquote_filename("'foo.py'", win32=False), "'foo.py'")
412 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=False), "'foo bar.py'")
413 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=False), "'foo bar.py'")
413
414
414 @with_environment
415 @with_environment
415 def test_get_py_filename():
416 def test_get_py_filename():
416 os.chdir(TMP_TEST_DIR)
417 os.chdir(TMP_TEST_DIR)
417 for win32 in (True, False):
418 for win32 in (True, False):
418 with make_tempfile('foo.py'):
419 with make_tempfile('foo.py'):
419 nt.assert_equal(path.get_py_filename('foo.py', force_win32=win32), 'foo.py')
420 nt.assert_equal(path.get_py_filename('foo.py', force_win32=win32), 'foo.py')
420 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo.py')
421 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo.py')
421 with make_tempfile('foo'):
422 with make_tempfile('foo'):
422 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo')
423 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo')
423 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
424 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
424 nt.assert_raises(IOError, path.get_py_filename, 'foo', force_win32=win32)
425 nt.assert_raises(IOError, path.get_py_filename, 'foo', force_win32=win32)
425 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
426 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
426 true_fn = 'foo with spaces.py'
427 true_fn = 'foo with spaces.py'
427 with make_tempfile(true_fn):
428 with make_tempfile(true_fn):
428 nt.assert_equal(path.get_py_filename('foo with spaces', force_win32=win32), true_fn)
429 nt.assert_equal(path.get_py_filename('foo with spaces', force_win32=win32), true_fn)
429 nt.assert_equal(path.get_py_filename('foo with spaces.py', force_win32=win32), true_fn)
430 nt.assert_equal(path.get_py_filename('foo with spaces.py', force_win32=win32), true_fn)
430 if win32:
431 if win32:
431 nt.assert_equal(path.get_py_filename('"foo with spaces.py"', force_win32=True), true_fn)
432 nt.assert_equal(path.get_py_filename('"foo with spaces.py"', force_win32=True), true_fn)
432 nt.assert_equal(path.get_py_filename("'foo with spaces.py'", force_win32=True), true_fn)
433 nt.assert_equal(path.get_py_filename("'foo with spaces.py'", force_win32=True), true_fn)
433 else:
434 else:
434 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"', force_win32=False)
435 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"', force_win32=False)
435 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'", force_win32=False)
436 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'", force_win32=False)
436
437
437 def test_unicode_in_filename():
438 def test_unicode_in_filename():
438 """When a file doesn't exist, the exception raised should be safe to call
439 """When a file doesn't exist, the exception raised should be safe to call
439 str() on - i.e. in Python 2 it must only have ASCII characters.
440 str() on - i.e. in Python 2 it must only have ASCII characters.
440
441
441 https://github.com/ipython/ipython/issues/875
442 https://github.com/ipython/ipython/issues/875
442 """
443 """
443 try:
444 try:
444 # these calls should not throw unicode encode exceptions
445 # these calls should not throw unicode encode exceptions
445 path.get_py_filename(u'fooéè.py', force_win32=False)
446 path.get_py_filename(u'fooéè.py', force_win32=False)
446 except IOError as ex:
447 except IOError as ex:
447 str(ex)
448 str(ex)
448
449
449
450
450 def test_shellglob():
451 class TestShellGlob(object):
451 """Test glob expansion for %run magic."""
452 filenames_start_with_a = map('a{0}'.format, range(3))
453 filenames_end_with_b = map('{0}b'.format, range(3))
454 filenames = filenames_start_with_a + filenames_end_with_b
455
452
456 with TemporaryDirectory() as td:
453 @classmethod
457 save = os.getcwdu()
454 def setUpClass(cls):
458 try:
455 cls.filenames_start_with_a = map('a{0}'.format, range(3))
459 os.chdir(td)
456 cls.filenames_end_with_b = map('{0}b'.format, range(3))
457 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
458 cls.tempdir = TemporaryDirectory()
459 td = cls.tempdir.name
460
460
461 with cls.in_tempdir():
461 # Create empty files
462 # Create empty files
462 for fname in filenames:
463 for fname in cls.filenames:
463 open(os.path.join(td, fname), 'w').close()
464 open(os.path.join(td, fname), 'w').close()
464
465
465 def assert_match(patterns, matches):
466 @classmethod
466 # glob returns unordered list. that's why sorted is required.
467 def tearDownClass(cls):
467 nt.assert_equals(sorted(path.shellglob(patterns)),
468 cls.tempdir.cleanup()
468 sorted(matches))
469
469
470 @classmethod
470 assert_match(['*'], filenames)
471 @contextmanager
471 assert_match(['a*'], filenames_start_with_a)
472 def in_tempdir(cls):
472 assert_match(['*c'], ['*c'])
473 save = os.getcwdu()
473 assert_match(['*', 'a*', '*b', '*c'],
474 try:
474 filenames
475 os.chdir(cls.tempdir.name)
475 + filenames_start_with_a
476 yield
476 + filenames_end_with_b
477 + ['*c'])
478
479 assert_match([r'\*'], ['*'])
480 assert_match([r'a\*', 'a*'], ['a*'] + filenames_start_with_a)
481 assert_match(['a[012]'], filenames_start_with_a)
482 assert_match([r'a\[012]'], ['a[012]'])
483 finally:
477 finally:
484 os.chdir(save)
478 os.chdir(save)
485
479
480 def check_match(self, patterns, matches):
481 with self.in_tempdir():
482 # glob returns unordered list. that's why sorted is required.
483 nt.assert_equals(sorted(path.shellglob(patterns)),
484 sorted(matches))
485
486 def common_cases(self):
487 return [
488 (['*'], self.filenames),
489 (['a*'], self.filenames_start_with_a),
490 (['*c'], ['*c']),
491 (['*', 'a*', '*b', '*c'], self.filenames
492 + self.filenames_start_with_a
493 + self.filenames_end_with_b
494 + ['*c']),
495 (['a[012]'], self.filenames_start_with_a),
496 ]
497
498 @skip_win32
499 def test_match_posix(self):
500 for (patterns, matches) in self.common_cases() + [
501 ([r'\*'], ['*']),
502 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
503 ([r'a\[012]'], ['a[012]']),
504 ]:
505 yield (self.check_match, patterns, matches)
506
507 @skip_if_not_win32
508 def test_match_windows(self):
509 for (patterns, matches) in self.common_cases() + [
510 # In windows, backslash is interpreted as path
511 # separator. Therefore, you can't escape glob
512 # using it.
513 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
514 ([r'a\[012]'], [r'a\[012]']),
515 ]:
516 yield (self.check_match, patterns, matches)
517
486
518
487 def test_unescape_glob():
519 def test_unescape_glob():
488 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
520 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
489 nt.assert_equals(path.unescape_glob(r'\\*'), r'\*')
521 nt.assert_equals(path.unescape_glob(r'\\*'), r'\*')
490 nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*')
522 nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*')
491 nt.assert_equals(path.unescape_glob(r'\\a'), r'\a')
523 nt.assert_equals(path.unescape_glob(r'\\a'), r'\a')
492 nt.assert_equals(path.unescape_glob(r'\a'), r'\a')
524 nt.assert_equals(path.unescape_glob(r'\a'), r'\a')
@@ -1,36 +1,37 b''
1 include README.rst
1 include README.rst
2 include COPYING.txt
2 include ipython.py
3 include ipython.py
3 include setupbase.py
4 include setupbase.py
4 include setupegg.py
5 include setupegg.py
5
6
6 graft setupext
7 graft setupext
7
8
8 graft scripts
9 graft scripts
9
10
10 # Load main dir but exclude things we don't want in the distro
11 # Load main dir but exclude things we don't want in the distro
11 graft IPython
12 graft IPython
12 prune IPython/deathrow
13 prune IPython/deathrow
13 prune IPython/external/js
14 prune IPython/external/js
14 prune IPython/frontend/html/notebook/static/mathjax
15 prune IPython/frontend/html/notebook/static/mathjax
15
16
16 # Include some specific files and data resources we need
17 # Include some specific files and data resources we need
17 include IPython/.git_commit_info.ini
18 include IPython/.git_commit_info.ini
18 include IPython/frontend/qt/console/resources/icon/IPythonConsole.svg
19 include IPython/frontend/qt/console/resources/icon/IPythonConsole.svg
19
20
20 # Documentation
21 # Documentation
21 graft docs
22 graft docs
22 exclude docs/\#*
23 exclude docs/\#*
23 exclude docs/man/*.1.gz
24 exclude docs/man/*.1.gz
24
25
25 # docs subdirs we want to skip
26 # docs subdirs we want to skip
26 prune docs/attic
27 prune docs/attic
27 prune docs/build
28 prune docs/build
28 prune docs/gh-pages
29 prune docs/gh-pages
29 prune docs/dist
30 prune docs/dist
30
31
31 # Patterns to exclude from any directory
32 # Patterns to exclude from any directory
32 global-exclude *~
33 global-exclude *~
33 global-exclude *.flc
34 global-exclude *.flc
34 global-exclude *.pyc
35 global-exclude *.pyc
35 global-exclude *.pyo
36 global-exclude *.pyo
36 global-exclude .dircopy.log
37 global-exclude .dircopy.log
@@ -1,347 +1,259 b''
1 {
1 {
2 "metadata": {
2 "metadata": {
3 "name": "Typesetting Math Using MathJax"
3 "name": "Typesetting Math Using MathJax"
4 },
4 },
5 "nbformat": 3,
5 "nbformat": 3,
6 "nbformat_minor": 0,
6 "nbformat_minor": 0,
7 "worksheets": [
7 "worksheets": [
8 {
8 {
9 "cells": [
9 "cells": [
10 {
10 {
11 "cell_type": "markdown",
11 "cell_type": "markdown",
12 "metadata": {},
12 "metadata": {},
13 "source": [
13 "source": [
14 "The Markdown parser included in IPython is MathJax-aware. This means that you can freely mix in mathematical expressions using the [MathJax subset of Tex and LaTeX](http://docs.mathjax.org/en/latest/tex.html#tex-support). [Some examples from the MathJax site](http://www.mathjax.org/demos/tex-samples/) are reproduced below, as well as the Markdown+TeX source."
14 "The Markdown parser included in IPython is MathJax-aware. This means that you can freely mix in mathematical expressions using the [MathJax subset of Tex and LaTeX](http://docs.mathjax.org/en/latest/tex.html#tex-support). [Some examples from the MathJax site](http://www.mathjax.org/demos/tex-samples/) are reproduced below, as well as the Markdown+TeX source."
15 ]
15 ]
16 },
16 },
17 {
17 {
18 "cell_type": "markdown",
18 "cell_type": "markdown",
19 "metadata": {},
19 "metadata": {},
20 "source": [
20 "source": [
21 "# Motivating Examples\n",
21 "# Motivating Examples\n",
22 "\n",
22 "\n",
23 "---\n",
23 "---\n",
24 "\n",
24 "\n",
25 "## The Lorenz Equations\n",
25 "## The Lorenz Equations\n",
26 "### Source\n",
26 "### Source\n",
27 "```\\begin{aligned}\n",
27 "```\\begin{aligned}\n",
28 "\\dot{x} & = \\sigma(y-x) \\\\\n",
28 "\\dot{x} & = \\sigma(y-x) \\\\\n",
29 "\\dot{y} & = \\rho x - y - xz \\\\\n",
29 "\\dot{y} & = \\rho x - y - xz \\\\\n",
30 "\\dot{z} & = -\\beta z + xy\n",
30 "\\dot{z} & = -\\beta z + xy\n",
31 "\\end{aligned}\n",
31 "\\end{aligned}\n",
32 "```\n",
32 "```\n",
33 "### Display\n",
33 "### Display\n",
34 "\\begin{aligned}\n",
34 "\\begin{aligned}\n",
35 "\\dot{x} & = \\sigma(y-x) \\\\\n",
35 "\\dot{x} & = \\sigma(y-x) \\\\\n",
36 "\\dot{y} & = \\rho x - y - xz \\\\\n",
36 "\\dot{y} & = \\rho x - y - xz \\\\\n",
37 "\\dot{z} & = -\\beta z + xy\n",
37 "\\dot{z} & = -\\beta z + xy\n",
38 "\\end{aligned}"
38 "\\end{aligned}"
39 ]
39 ]
40 },
40 },
41 {
41 {
42 "cell_type": "markdown",
42 "cell_type": "markdown",
43 "metadata": {},
43 "metadata": {},
44 "source": [
44 "source": [
45 "## The Cauchy-Schwarz Inequality\n",
45 "## The Cauchy-Schwarz Inequality\n",
46 "### Source\n",
46 "### Source\n",
47 "```\\begin{equation*}\n",
47 "```\\begin{equation*}\n",
48 "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n",
48 "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n",
49 "\\end{equation*}\n",
49 "\\end{equation*}\n",
50 "```\n",
50 "```\n",
51 "### Display\n",
51 "### Display\n",
52 "\\begin{equation*}\n",
52 "\\begin{equation*}\n",
53 "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n",
53 "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n",
54 "\\end{equation*}"
54 "\\end{equation*}"
55 ]
55 ]
56 },
56 },
57 {
57 {
58 "cell_type": "markdown",
58 "cell_type": "markdown",
59 "metadata": {},
59 "metadata": {},
60 "source": [
60 "source": [
61 "## A Cross Product Formula\n",
61 "## A Cross Product Formula\n",
62 "### Source\n",
62 "### Source\n",
63 "```\\begin{equation*}\n",
63 "```\\begin{equation*}\n",
64 "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n",
64 "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n",
65 "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n",
65 "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n",
66 "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n",
66 "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n",
67 "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n",
67 "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n",
68 "\\end{vmatrix} \n",
68 "\\end{vmatrix} \n",
69 "\\end{equation*}\n",
69 "\\end{equation*}\n",
70 "```\n",
70 "```\n",
71 "### Display\n",
71 "### Display\n",
72 "\\begin{equation*}\n",
72 "\\begin{equation*}\n",
73 "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n",
73 "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n",
74 "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n",
74 "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n",
75 "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n",
75 "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n",
76 "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n",
76 "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n",
77 "\\end{vmatrix} \n",
77 "\\end{vmatrix} \n",
78 "\\end{equation*}"
78 "\\end{equation*}"
79 ]
79 ]
80 },
80 },
81 {
81 {
82 "cell_type": "markdown",
82 "cell_type": "markdown",
83 "metadata": {},
83 "metadata": {},
84 "source": [
84 "source": [
85 "## The probability of getting \\(k\\) heads when flipping \\(n\\) coins is\n",
85 "## The probability of getting \\(k\\) heads when flipping \\(n\\) coins is\n",
86 "### Source\n",
86 "### Source\n",
87 "```\\begin{equation*}\n",
87 "```\\begin{equation*}\n",
88 "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n",
88 "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n",
89 "\\end{equation*}\n",
89 "\\end{equation*}\n",
90 "```\n",
90 "```\n",
91 "### Display\n",
91 "### Display\n",
92 "\\begin{equation*}\n",
92 "\\begin{equation*}\n",
93 "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n",
93 "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n",
94 "\\end{equation*}"
94 "\\end{equation*}"
95 ]
95 ]
96 },
96 },
97 {
97 {
98 "cell_type": "markdown",
98 "cell_type": "markdown",
99 "metadata": {},
99 "metadata": {},
100 "source": [
100 "source": [
101 "## An Identity of Ramanujan\n",
101 "## An Identity of Ramanujan\n",
102 "### Source\n",
102 "### Source\n",
103 "```\\begin{equation*}\n",
103 "```\\begin{equation*}\n",
104 "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n",
104 "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n",
105 "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n",
105 "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n",
106 "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n",
106 "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n",
107 "\\end{equation*}\n",
107 "\\end{equation*}\n",
108 "```\n",
108 "```\n",
109 "### Display\n",
109 "### Display\n",
110 "\\begin{equation*}\n",
110 "\\begin{equation*}\n",
111 "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n",
111 "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n",
112 "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n",
112 "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n",
113 "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n",
113 "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n",
114 "\\end{equation*}"
114 "\\end{equation*}"
115 ]
115 ]
116 },
116 },
117 {
117 {
118 "cell_type": "markdown",
118 "cell_type": "markdown",
119 "metadata": {},
119 "metadata": {},
120 "source": [
120 "source": [
121 "## A Rogers-Ramanujan Identity\n",
121 "## A Rogers-Ramanujan Identity\n",
122 "### Source\n",
122 "### Source\n",
123 "```\\begin{equation*}\n",
123 "```\\begin{equation*}\n",
124 "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n",
124 "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n",
125 "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n",
125 "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n",
126 "\\quad\\quad \\text{for $|q|<1$}. \n",
126 "\\quad\\quad \\text{for $|q|<1$}. \n",
127 "\\end{equation*}\n",
127 "\\end{equation*}\n",
128 "```\n",
128 "```\n",
129 "### Display\n",
129 "### Display\n",
130 "\\begin{equation*}\n",
130 "\\begin{equation*}\n",
131 "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n",
131 "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n",
132 "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n",
132 "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n",
133 "\\quad\\quad \\text{for $|q|<1$}. \n",
133 "\\quad\\quad \\text{for $|q|<1$}. \n",
134 "\\end{equation*}"
134 "\\end{equation*}"
135 ]
135 ]
136 },
136 },
137 {
137 {
138 "cell_type": "markdown",
138 "cell_type": "markdown",
139 "metadata": {},
139 "metadata": {},
140 "source": [
140 "source": [
141 "## Maxwell's Equations\n",
141 "## Maxwell's Equations\n",
142 "### Source\n",
142 "### Source\n",
143 "```\\begin{aligned}\n",
143 "```\\begin{aligned}\n",
144 "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n",
144 "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n",
145 "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n",
145 "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n",
146 "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n",
146 "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n",
147 "\\end{aligned}\n",
147 "\\end{aligned}\n",
148 "```\n",
148 "```\n",
149 "### Display\n",
149 "### Display\n",
150 "\\begin{aligned}\n",
150 "\\begin{aligned}\n",
151 "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n",
151 "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n",
152 "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n",
152 "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n",
153 "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n",
153 "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n",
154 "\\end{aligned}"
154 "\\end{aligned}"
155 ]
155 ]
156 },
156 },
157 {
157 {
158 "cell_type": "markdown",
158 "cell_type": "markdown",
159 "metadata": {},
159 "metadata": {},
160 "source": [
160 "source": [
161 "# Equation Numbering and References\n",
161 "# Equation Numbering and References\n",
162 "\n",
162 "\n",
163 "---\n",
163 "---\n",
164 "\n",
164 "\n",
165 "These equation reference examples are adapted from an [example page in the MathJax documentation](http://cdn.mathjax.org/mathjax/latest/test/sample-eqrefs.html). Note that it's okay to reference equations across cells. Click inside this cell to see the source.\n",
165 "Equation numbering and referencing will be available in a future version of IPython."
166 "\n",
167 "## Labeled equations and references\n",
168 "\n",
169 "Here is a labeled equation:\n",
170 "\\begin{equation}\n",
171 "x+1\\over\\sqrt{1-x^2}\\label{ref1}\n",
172 "\\end{equation}\n",
173 "\n",
174 "with a reference to ref1: \\ref{ref1},\n",
175 "and another numbered one with no label:\n",
176 "\\begin{equation}\n",
177 "x+1\\over\\sqrt{1-x^2}\n",
178 "\\end{equation}"
179 ]
180 },
181 {
182 "cell_type": "markdown",
183 "metadata": {},
184 "source": [
185 "## \\nonumber and equation*\n",
186 "\n",
187 "This one uses \\nonumber:\n",
188 "\\begin{equation}\n",
189 "x+1\\over\\sqrt{1-x^2}\\nonumber\n",
190 "\\end{equation}\n",
191 "\n",
192 "Here's one with the equation* environment:\n",
193 "\\begin{equation*}\n",
194 "x+1\\over\\sqrt{1-x^2}\n",
195 "\\end{equation*}"
196 ]
197 },
198 {
199 "cell_type": "markdown",
200 "metadata": {},
201 "source": [
202 "## Forward references\n",
203 "\n",
204 "This is a forward reference [\\ref{ref2}] and another \\eqref{ref2} for the \n",
205 "following equation:\n",
206 "\n",
207 "\\begin{equation}\n",
208 "x+1\\over\\sqrt{1-x^2}\\label{ref2}\n",
209 "\\end{equation}\n",
210 "\n",
211 "More math:\n",
212 "\\begin{equation}\n",
213 "x+1\\over\\sqrt{1-x^2}\n",
214 "\\end{equation}"
215 ]
216 },
217 {
218 "cell_type": "markdown",
219 "metadata": {},
220 "source": [
221 "### References inline and in environments\n",
222 "\n",
223 "Here is a ref inside math: $\\ref{ref2}+1$ and text after it.\n",
224 "\n",
225 "\\begin{align} \n",
226 "x& = y_1-y_2+y_3-y_5+y_8-\\dots \n",
227 "&& \\text{by \\eqref{ref1}}\\\\ \n",
228 "& = y'\\circ y^* && \\text{(by \\eqref{ref3})}\\\\ \n",
229 "& = y(0) y' && \\text {by Axiom 1.} \n",
230 "\\end{align} \n",
231 "\n",
232 "### Missing references\n",
233 "Here's a bad ref [\\ref{ref4}] to a nonexistent label.\n",
234 "\n",
235 "### Numbering align environments\n",
236 "An alignment:\n",
237 "\\begin{align}\n",
238 "a&=b\\label{ref3}\\cr\n",
239 "&=c+d\n",
240 "\\end{align}\n",
241 "and a starred one:\n",
242 "\\begin{align*}\n",
243 "a&=b\\cr\n",
244 "&=c+d\n",
245 "\\end{align*}"
246 ]
166 ]
247 },
167 },
248 {
168 {
249 "cell_type": "markdown",
169 "cell_type": "markdown",
250 "metadata": {},
170 "metadata": {},
251 "source": [
171 "source": [
252 "# Inline Typesetting (Mixing Markdown and TeX)\n",
172 "# Inline Typesetting (Mixing Markdown and TeX)\n",
253 "\n",
173 "\n",
254 "---\n",
174 "---\n",
255 "\n",
175 "\n",
256 "While display equations look good for a page of samples, the ability to mix math and *formatted* **text** in a paragraph is also important.\n",
176 "While display equations look good for a page of samples, the ability to mix math and *formatted* **text** in a paragraph is also important.\n",
257 "\n",
177 "\n",
258 "## Source\n",
178 "## Source\n",
259 "``` This expression $\\sqrt{3x-1}+(1+x)^2$ is an example of a TeX inline equation in a **[Markdown-formatted](http://daringfireball.net/projects/markdown/)** sentence. \n",
179 "``` This expression $\\sqrt{3x-1}+(1+x)^2$ is an example of a TeX inline equation in a **[Markdown-formatted](http://daringfireball.net/projects/markdown/)** sentence. \n",
260 "```\n",
180 "```\n",
261 "## Display\n",
181 "## Display\n",
262 "This expression $\\sqrt{3x-1}+(1+x)^2$ is an example of a TeX inline equation in a **[Markdown-formatted](http://daringfireball.net/projects/markdown/)** sentence. "
182 "This expression $\\sqrt{3x-1}+(1+x)^2$ is an example of a TeX inline equation in a **[Markdown-formatted](http://daringfireball.net/projects/markdown/)** sentence. "
263 ]
183 ]
264 },
184 },
265 {
185 {
266 "cell_type": "markdown",
186 "cell_type": "markdown",
267 "metadata": {},
187 "metadata": {},
268 "source": [
188 "source": [
269 "# Other Syntax\n",
189 "# Other Syntax\n",
270 "\n",
190 "\n",
271 "---\n",
191 "---\n",
272 "\n",
192 "\n",
273 "You will notice in other places on the web that `$$` are needed explicitly to begin and end MathJax typesetting. This is **not** required if you will be using TeX environments, but the IPython notebook will accept this syntax on legacy notebooks. \n",
193 "You will notice in other places on the web that `$$` are needed explicitly to begin and end MathJax typesetting. This is **not** required if you will be using TeX environments, but the IPython notebook will accept this syntax on legacy notebooks. \n",
274 "\n",
194 "\n",
275 "### Source\n",
195 "### Source\n",
276 "```$$\n",
196 "```$$\n",
277 "\\begin{array}{c}\n",
197 "\\begin{array}{c}\n",
278 "y_1 \\\\\\\n",
198 "y_1 \\\\\\\n",
279 "y_2 \\mathtt{t}_i \\\\\\\n",
199 "y_2 \\mathtt{t}_i \\\\\\\n",
280 "z_{3,4}\n",
200 "z_{3,4}\n",
281 "\\end{array}\n",
201 "\\end{array}\n",
282 "$$\n",
202 "$$\n",
283 "```\n",
203 "```\n",
284 "\n",
204 "\n",
285 "```\n",
205 "```\n",
286 "$$\n",
206 "$$\n",
287 "\\begin{array}{c}\n",
207 "\\begin{array}{c}\n",
288 "y_1 \\cr\n",
208 "y_1 \\cr\n",
289 "y_2 \\mathtt{t}_i \\cr\n",
209 "y_2 \\mathtt{t}_i \\cr\n",
290 "y_{3}\n",
210 "y_{3}\n",
291 "\\end{array}\n",
211 "\\end{array}\n",
292 "$$\n",
212 "$$\n",
293 "```\n",
213 "```\n",
294 "\n",
214 "\n",
295 "```\n",
215 "```\n",
296 "$$\\begin{eqnarray} \n",
216 "$$\\begin{eqnarray} \n",
297 "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n",
217 "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n",
298 "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n",
218 "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n",
299 "\\end{eqnarray}$$\n",
219 "\\end{eqnarray}$$\n",
300 "```\n",
220 "```\n",
301 "\n",
221 "\n",
302 "```\n",
222 "```\n",
303 "$$\n",
223 "$$\n",
304 "x=4\n",
224 "x=4\n",
305 "$$\n",
225 "$$\n",
306 "```\n",
226 "```\n",
307 "\n",
227 "\n",
308 "### Display\n",
228 "### Display\n",
309 "$$\n",
229 "$$\n",
310 "\\begin{array}{c}\n",
230 "\\begin{array}{c}\n",
311 "y_1 \\\\\\\n",
231 "y_1 \\\\\\\n",
312 "y_2 \\mathtt{t}_i \\\\\\\n",
232 "y_2 \\mathtt{t}_i \\\\\\\n",
313 "z_{3,4}\n",
233 "z_{3,4}\n",
314 "\\end{array}\n",
234 "\\end{array}\n",
315 "$$\n",
235 "$$\n",
316 "\n",
236 "\n",
317 "$$\n",
237 "$$\n",
318 "\\begin{array}{c}\n",
238 "\\begin{array}{c}\n",
319 "y_1 \\cr\n",
239 "y_1 \\cr\n",
320 "y_2 \\mathtt{t}_i \\cr\n",
240 "y_2 \\mathtt{t}_i \\cr\n",
321 "y_{3}\n",
241 "y_{3}\n",
322 "\\end{array}\n",
242 "\\end{array}\n",
323 "$$\n",
243 "$$\n",
324 "\n",
244 "\n",
325 "$$\\begin{eqnarray} \n",
245 "$$\\begin{eqnarray} \n",
326 "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n",
246 "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n",
327 "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n",
247 "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n",
328 "\\end{eqnarray}$$\n",
248 "\\end{eqnarray}$$\n",
329 "\n",
249 "\n",
330 "$$\n",
250 "$$\n",
331 "x=4\n",
251 "x=4\n",
332 "$$"
252 "$$"
333 ]
253 ]
334 },
335 {
336 "cell_type": "code",
337 "collapsed": false,
338 "input": [],
339 "language": "python",
340 "metadata": {},
341 "outputs": []
342 }
254 }
343 ],
255 ],
344 "metadata": {}
256 "metadata": {}
345 }
257 }
346 ]
258 ]
347 } No newline at end of file
259 }
@@ -1,1150 +1,1150 b''
1 =================
1 =================
2 IPython reference
2 IPython reference
3 =================
3 =================
4
4
5 .. _command_line_options:
5 .. _command_line_options:
6
6
7 Command-line usage
7 Command-line usage
8 ==================
8 ==================
9
9
10 You start IPython with the command::
10 You start IPython with the command::
11
11
12 $ ipython [options] files
12 $ ipython [options] files
13
13
14 .. note::
14 .. note::
15
15
16 For IPython on Python 3, use ``ipython3`` in place of ``ipython``.
16 For IPython on Python 3, use ``ipython3`` in place of ``ipython``.
17
17
18 If invoked with no options, it executes all the files listed in sequence
18 If invoked with no options, it executes all the files listed in sequence
19 and drops you into the interpreter while still acknowledging any options
19 and drops you into the interpreter while still acknowledging any options
20 you may have set in your ipython_config.py. This behavior is different from
20 you may have set in your ipython_config.py. This behavior is different from
21 standard Python, which when called as python -i will only execute one
21 standard Python, which when called as python -i will only execute one
22 file and ignore your configuration setup.
22 file and ignore your configuration setup.
23
23
24 Please note that some of the configuration options are not available at
24 Please note that some of the configuration options are not available at
25 the command line, simply because they are not practical here. Look into
25 the command line, simply because they are not practical here. Look into
26 your configuration files for details on those. There are separate configuration
26 your configuration files for details on those. There are separate configuration
27 files for each profile, and the files look like "ipython_config.py" or
27 files for each profile, and the files look like "ipython_config.py" or
28 "ipython_config_<frontendname>.py". Profile directories look like
28 "ipython_config_<frontendname>.py". Profile directories look like
29 "profile_profilename" and are typically installed in the IPYTHONDIR directory.
29 "profile_profilename" and are typically installed in the IPYTHONDIR directory.
30 For Linux users, this will be $HOME/.config/ipython, and for other users it
30 For Linux users, this will be $HOME/.config/ipython, and for other users it
31 will be $HOME/.ipython. For Windows users, $HOME resolves to C:\\Documents and
31 will be $HOME/.ipython. For Windows users, $HOME resolves to C:\\Documents and
32 Settings\\YourUserName in most instances.
32 Settings\\YourUserName in most instances.
33
33
34
34
35 Eventloop integration
35 Eventloop integration
36 ---------------------
36 ---------------------
37
37
38 Previously IPython had command line options for controlling GUI event loop
38 Previously IPython had command line options for controlling GUI event loop
39 integration (-gthread, -qthread, -q4thread, -wthread, -pylab). As of IPython
39 integration (-gthread, -qthread, -q4thread, -wthread, -pylab). As of IPython
40 version 0.11, these have been removed. Please see the new ``%gui``
40 version 0.11, these have been removed. Please see the new ``%gui``
41 magic command or :ref:`this section <gui_support>` for details on the new
41 magic command or :ref:`this section <gui_support>` for details on the new
42 interface, or specify the gui at the commandline::
42 interface, or specify the gui at the commandline::
43
43
44 $ ipython --gui=qt
44 $ ipython --gui=qt
45
45
46
46
47 Command-line Options
47 Command-line Options
48 --------------------
48 --------------------
49
49
50 To see the options IPython accepts, use ``ipython --help`` (and you probably
50 To see the options IPython accepts, use ``ipython --help`` (and you probably
51 should run the output through a pager such as ``ipython --help | less`` for
51 should run the output through a pager such as ``ipython --help | less`` for
52 more convenient reading). This shows all the options that have a single-word
52 more convenient reading). This shows all the options that have a single-word
53 alias to control them, but IPython lets you configure all of its objects from
53 alias to control them, but IPython lets you configure all of its objects from
54 the command-line by passing the full class name and a corresponding value; type
54 the command-line by passing the full class name and a corresponding value; type
55 ``ipython --help-all`` to see this full list. For example::
55 ``ipython --help-all`` to see this full list. For example::
56
56
57 ipython --pylab qt
57 ipython --pylab qt
58
58
59 is equivalent to::
59 is equivalent to::
60
60
61 ipython --TerminalIPythonApp.pylab='qt'
61 ipython --TerminalIPythonApp.pylab='qt'
62
62
63 Note that in the second form, you *must* use the equal sign, as the expression
63 Note that in the second form, you *must* use the equal sign, as the expression
64 is evaluated as an actual Python assignment. While in the above example the
64 is evaluated as an actual Python assignment. While in the above example the
65 short form is more convenient, only the most common options have a short form,
65 short form is more convenient, only the most common options have a short form,
66 while any configurable variable in IPython can be set at the command-line by
66 while any configurable variable in IPython can be set at the command-line by
67 using the long form. This long form is the same syntax used in the
67 using the long form. This long form is the same syntax used in the
68 configuration files, if you want to set these options permanently.
68 configuration files, if you want to set these options permanently.
69
69
70
70
71 Interactive use
71 Interactive use
72 ===============
72 ===============
73
73
74 IPython is meant to work as a drop-in replacement for the standard interactive
74 IPython is meant to work as a drop-in replacement for the standard interactive
75 interpreter. As such, any code which is valid python should execute normally
75 interpreter. As such, any code which is valid python should execute normally
76 under IPython (cases where this is not true should be reported as bugs). It
76 under IPython (cases where this is not true should be reported as bugs). It
77 does, however, offer many features which are not available at a standard python
77 does, however, offer many features which are not available at a standard python
78 prompt. What follows is a list of these.
78 prompt. What follows is a list of these.
79
79
80
80
81 Caution for Windows users
81 Caution for Windows users
82 -------------------------
82 -------------------------
83
83
84 Windows, unfortunately, uses the '\\' character as a path separator. This is a
84 Windows, unfortunately, uses the '\\' character as a path separator. This is a
85 terrible choice, because '\\' also represents the escape character in most
85 terrible choice, because '\\' also represents the escape character in most
86 modern programming languages, including Python. For this reason, using '/'
86 modern programming languages, including Python. For this reason, using '/'
87 character is recommended if you have problems with ``\``. However, in Windows
87 character is recommended if you have problems with ``\``. However, in Windows
88 commands '/' flags options, so you can not use it for the root directory. This
88 commands '/' flags options, so you can not use it for the root directory. This
89 means that paths beginning at the root must be typed in a contrived manner
89 means that paths beginning at the root must be typed in a contrived manner
90 like: ``%copy \opt/foo/bar.txt \tmp``
90 like: ``%copy \opt/foo/bar.txt \tmp``
91
91
92 .. _magic:
92 .. _magic:
93
93
94 Magic command system
94 Magic command system
95 --------------------
95 --------------------
96
96
97 IPython will treat any line whose first character is a % as a special
97 IPython will treat any line whose first character is a % as a special
98 call to a 'magic' function. These allow you to control the behavior of
98 call to a 'magic' function. These allow you to control the behavior of
99 IPython itself, plus a lot of system-type features. They are all
99 IPython itself, plus a lot of system-type features. They are all
100 prefixed with a % character, but parameters are given without
100 prefixed with a % character, but parameters are given without
101 parentheses or quotes.
101 parentheses or quotes.
102
102
103 Lines that begin with ``%%`` signal a *cell magic*: they take as arguments not
103 Lines that begin with ``%%`` signal a *cell magic*: they take as arguments not
104 only the rest of the current line, but all lines below them as well, in the
104 only the rest of the current line, but all lines below them as well, in the
105 current execution block. Cell magics can in fact make arbitrary modifications
105 current execution block. Cell magics can in fact make arbitrary modifications
106 to the input they receive, which need not even be valid Python code at all.
106 to the input they receive, which need not even be valid Python code at all.
107 They receive the whole block as a single string.
107 They receive the whole block as a single string.
108
108
109 As a line magic example, the ``%cd`` magic works just like the OS command of
109 As a line magic example, the ``%cd`` magic works just like the OS command of
110 the same name::
110 the same name::
111
111
112 In [8]: %cd
112 In [8]: %cd
113 /home/fperez
113 /home/fperez
114
114
115 The following uses the builtin ``timeit`` in cell mode::
115 The following uses the builtin ``timeit`` in cell mode::
116
116
117 In [10]: %%timeit x = range(10000)
117 In [10]: %%timeit x = range(10000)
118 ...: min(x)
118 ...: min(x)
119 ...: max(x)
119 ...: max(x)
120 ...:
120 ...:
121 1000 loops, best of 3: 438 us per loop
121 1000 loops, best of 3: 438 us per loop
122
122
123 In this case, ``x = range(10000)`` is called as the line argument, and the
123 In this case, ``x = range(10000)`` is called as the line argument, and the
124 block with ``min(x)`` and ``max(x)`` is called as the cell body. The
124 block with ``min(x)`` and ``max(x)`` is called as the cell body. The
125 ``timeit`` magic receives both.
125 ``timeit`` magic receives both.
126
126
127 If you have 'automagic' enabled (as it by default), you don't need to type in
127 If you have 'automagic' enabled (as it by default), you don't need to type in
128 the single ``%`` explicitly for line magics; IPython will scan its internal
128 the single ``%`` explicitly for line magics; IPython will scan its internal
129 list of magic functions and call one if it exists. With automagic on you can
129 list of magic functions and call one if it exists. With automagic on you can
130 then just type ``cd mydir`` to go to directory 'mydir'::
130 then just type ``cd mydir`` to go to directory 'mydir'::
131
131
132 In [9]: cd mydir
132 In [9]: cd mydir
133 /home/fperez/mydir
133 /home/fperez/mydir
134
134
135 Note that cell magics *always* require an explicit ``%%`` prefix, automagic
135 Note that cell magics *always* require an explicit ``%%`` prefix, automagic
136 calling only works for line magics.
136 calling only works for line magics.
137
137
138 The automagic system has the lowest possible precedence in name searches, so
138 The automagic system has the lowest possible precedence in name searches, so
139 defining an identifier with the same name as an existing magic function will
139 defining an identifier with the same name as an existing magic function will
140 shadow it for automagic use. You can still access the shadowed magic function
140 shadow it for automagic use. You can still access the shadowed magic function
141 by explicitly using the ``%`` character at the beginning of the line.
141 by explicitly using the ``%`` character at the beginning of the line.
142
142
143 An example (with automagic on) should clarify all this:
143 An example (with automagic on) should clarify all this:
144
144
145 .. sourcecode:: ipython
145 .. sourcecode:: ipython
146
146
147 In [1]: cd ipython # %cd is called by automagic
147 In [1]: cd ipython # %cd is called by automagic
148 /home/fperez/ipython
148 /home/fperez/ipython
149
149
150 In [2]: cd=1 # now cd is just a variable
150 In [2]: cd=1 # now cd is just a variable
151
151
152 In [3]: cd .. # and doesn't work as a function anymore
152 In [3]: cd .. # and doesn't work as a function anymore
153 File "<ipython-input-3-9fedb3aff56c>", line 1
153 File "<ipython-input-3-9fedb3aff56c>", line 1
154 cd ..
154 cd ..
155 ^
155 ^
156 SyntaxError: invalid syntax
156 SyntaxError: invalid syntax
157
157
158
158
159 In [4]: %cd .. # but %cd always works
159 In [4]: %cd .. # but %cd always works
160 /home/fperez
160 /home/fperez
161
161
162 In [5]: del cd # if you remove the cd variable, automagic works again
162 In [5]: del cd # if you remove the cd variable, automagic works again
163
163
164 In [6]: cd ipython
164 In [6]: cd ipython
165
165
166 /home/fperez/ipython
166 /home/fperez/ipython
167
167
168 Defining your own magics
168 Defining your own magics
169 ~~~~~~~~~~~~~~~~~~~~~~~~
169 ~~~~~~~~~~~~~~~~~~~~~~~~
170
170
171 There are two main ways to define your own magic functions: from standalone
171 There are two main ways to define your own magic functions: from standalone
172 functions and by inheriting from a base class provided by IPython:
172 functions and by inheriting from a base class provided by IPython:
173 :class:`IPython.core.magic.Magics`. Below we show code you can place in a file
173 :class:`IPython.core.magic.Magics`. Below we show code you can place in a file
174 that you load from your configuration, such as any file in the ``startup``
174 that you load from your configuration, such as any file in the ``startup``
175 subdirectory of your default IPython profile.
175 subdirectory of your default IPython profile.
176
176
177 First, let us see the simplest case. The following shows how to create a line
177 First, let us see the simplest case. The following shows how to create a line
178 magic, a cell one and one that works in both modes, using just plain functions:
178 magic, a cell one and one that works in both modes, using just plain functions:
179
179
180 .. sourcecode:: python
180 .. sourcecode:: python
181
181
182 from IPython.core.magic import (register_line_magic, register_cell_magic,
182 from IPython.core.magic import (register_line_magic, register_cell_magic,
183 register_line_cell_magic)
183 register_line_cell_magic)
184
184
185 @register_line_magic
185 @register_line_magic
186 def lmagic(line):
186 def lmagic(line):
187 "my line magic"
187 "my line magic"
188 return line
188 return line
189
189
190 @register_cell_magic
190 @register_cell_magic
191 def cmagic(line, cell):
191 def cmagic(line, cell):
192 "my cell magic"
192 "my cell magic"
193 return line, cell
193 return line, cell
194
194
195 @register_line_cell_magic
195 @register_line_cell_magic
196 def lcmagic(line, cell=None):
196 def lcmagic(line, cell=None):
197 "Magic that works both as %lcmagic and as %%lcmagic"
197 "Magic that works both as %lcmagic and as %%lcmagic"
198 if cell is None:
198 if cell is None:
199 print "Called as line magic"
199 print "Called as line magic"
200 return line
200 return line
201 else:
201 else:
202 print "Called as cell magic"
202 print "Called as cell magic"
203 return line, cell
203 return line, cell
204
204
205 # We delete these to avoid name conflicts for automagic to work
205 # We delete these to avoid name conflicts for automagic to work
206 del lmagic, lcmagic
206 del lmagic, lcmagic
207
207
208
208
209 You can also create magics of all three kinds by inheriting from the
209 You can also create magics of all three kinds by inheriting from the
210 :class:`IPython.core.magic.Magics` class. This lets you create magics that can
210 :class:`IPython.core.magic.Magics` class. This lets you create magics that can
211 potentially hold state in between calls, and that have full access to the main
211 potentially hold state in between calls, and that have full access to the main
212 IPython object:
212 IPython object:
213
213
214 .. sourcecode:: python
214 .. sourcecode:: python
215
215
216 # This code can be put in any Python module, it does not require IPython
216 # This code can be put in any Python module, it does not require IPython
217 # itself to be running already. It only creates the magics subclass but
217 # itself to be running already. It only creates the magics subclass but
218 # doesn't instantiate it yet.
218 # doesn't instantiate it yet.
219 from IPython.core.magic import (Magics, magics_class, line_magic,
219 from IPython.core.magic import (Magics, magics_class, line_magic,
220 cell_magic, line_cell_magic)
220 cell_magic, line_cell_magic)
221
221
222 # The class MUST call this class decorator at creation time
222 # The class MUST call this class decorator at creation time
223 @magics_class
223 @magics_class
224 class MyMagics(Magics):
224 class MyMagics(Magics):
225
225
226 @line_magic
226 @line_magic
227 def lmagic(self, line):
227 def lmagic(self, line):
228 "my line magic"
228 "my line magic"
229 print "Full access to the main IPython object:", self.shell
229 print "Full access to the main IPython object:", self.shell
230 print "Variables in the user namespace:", self.user_ns.keys()
230 print "Variables in the user namespace:", self.user_ns.keys()
231 return line
231 return line
232
232
233 @cell_magic
233 @cell_magic
234 def cmagic(self, line, cell):
234 def cmagic(self, line, cell):
235 "my cell magic"
235 "my cell magic"
236 return line, cell
236 return line, cell
237
237
238 @line_cell_magic
238 @line_cell_magic
239 def lcmagic(self, line, cell=None):
239 def lcmagic(self, line, cell=None):
240 "Magic that works both as %lcmagic and as %%lcmagic"
240 "Magic that works both as %lcmagic and as %%lcmagic"
241 if cell is None:
241 if cell is None:
242 print "Called as line magic"
242 print "Called as line magic"
243 return line
243 return line
244 else:
244 else:
245 print "Called as cell magic"
245 print "Called as cell magic"
246 return line, cell
246 return line, cell
247
247
248
248
249 # In order to actually use these magics, you must register them with a
249 # In order to actually use these magics, you must register them with a
250 # running IPython. This code must be placed in a file that is loaded once
250 # running IPython. This code must be placed in a file that is loaded once
251 # IPython is up and running:
251 # IPython is up and running:
252 ip = get_ipython()
252 ip = get_ipython()
253 # You can register the class itself without instantiating it. IPython will
253 # You can register the class itself without instantiating it. IPython will
254 # call the default constructor on it.
254 # call the default constructor on it.
255 ip.register_magics(MyMagics)
255 ip.register_magics(MyMagics)
256
256
257 If you want to create a class with a different constructor that holds
257 If you want to create a class with a different constructor that holds
258 additional state, then you should always call the parent constructor and
258 additional state, then you should always call the parent constructor and
259 instantiate the class yourself before registration:
259 instantiate the class yourself before registration:
260
260
261 .. sourcecode:: python
261 .. sourcecode:: python
262
262
263 @magics_class
263 @magics_class
264 class StatefulMagics(Magics):
264 class StatefulMagics(Magics):
265 "Magics that hold additional state"
265 "Magics that hold additional state"
266
266
267 def __init__(self, shell, data):
267 def __init__(self, shell, data):
268 # You must call the parent constructor
268 # You must call the parent constructor
269 super(StatefulMagics, self).__init__(shell)
269 super(StatefulMagics, self).__init__(shell)
270 self.data = data
270 self.data = data
271
271
272 # etc...
272 # etc...
273
273
274 # This class must then be registered with a manually created instance,
274 # This class must then be registered with a manually created instance,
275 # since its constructor has different arguments from the default:
275 # since its constructor has different arguments from the default:
276 ip = get_ipython()
276 ip = get_ipython()
277 magics = StatefulMagics(ip, some_data)
277 magics = StatefulMagics(ip, some_data)
278 ip.register_magics(magics)
278 ip.register_magics(magics)
279
279
280
280
281 In earlier versions, IPython had an API for the creation of line magics (cell
281 In earlier versions, IPython had an API for the creation of line magics (cell
282 magics did not exist at the time) that required you to create functions with a
282 magics did not exist at the time) that required you to create functions with a
283 method-looking signature and to manually pass both the function and the name.
283 method-looking signature and to manually pass both the function and the name.
284 While this API is no longer recommended, it remains indefinitely supported for
284 While this API is no longer recommended, it remains indefinitely supported for
285 backwards compatibility purposes. With the old API, you'd create a magic as
285 backwards compatibility purposes. With the old API, you'd create a magic as
286 follows:
286 follows:
287
287
288 .. sourcecode:: python
288 .. sourcecode:: python
289
289
290 def func(self, line):
290 def func(self, line):
291 print "Line magic called with line:", line
291 print "Line magic called with line:", line
292 print "IPython object:", self.shell
292 print "IPython object:", self.shell
293
293
294 ip = get_ipython()
294 ip = get_ipython()
295 # Declare this function as the magic %mycommand
295 # Declare this function as the magic %mycommand
296 ip.define_magic('mycommand', func)
296 ip.define_magic('mycommand', func)
297
297
298 Type ``%magic`` for more information, including a list of all available magic
298 Type ``%magic`` for more information, including a list of all available magic
299 functions at any time and their docstrings. You can also type
299 functions at any time and their docstrings. You can also type
300 ``%magic_function_name?`` (see :ref:`below <dynamic_object_info>` for
300 ``%magic_function_name?`` (see :ref:`below <dynamic_object_info>` for
301 information on the '?' system) to get information about any particular magic
301 information on the '?' system) to get information about any particular magic
302 function you are interested in.
302 function you are interested in.
303
303
304 The API documentation for the :mod:`IPython.core.magic` module contains the full
304 The API documentation for the :mod:`IPython.core.magic` module contains the full
305 docstrings of all currently available magic commands.
305 docstrings of all currently available magic commands.
306
306
307
307
308 Access to the standard Python help
308 Access to the standard Python help
309 ----------------------------------
309 ----------------------------------
310
310
311 Simply type ``help()`` to access Python's standard help system. You can
311 Simply type ``help()`` to access Python's standard help system. You can
312 also type ``help(object)`` for information about a given object, or
312 also type ``help(object)`` for information about a given object, or
313 ``help('keyword')`` for information on a keyword. You may need to configure your
313 ``help('keyword')`` for information on a keyword. You may need to configure your
314 PYTHONDOCS environment variable for this feature to work correctly.
314 PYTHONDOCS environment variable for this feature to work correctly.
315
315
316 .. _dynamic_object_info:
316 .. _dynamic_object_info:
317
317
318 Dynamic object information
318 Dynamic object information
319 --------------------------
319 --------------------------
320
320
321 Typing ``?word`` or ``word?`` prints detailed information about an object. If
321 Typing ``?word`` or ``word?`` prints detailed information about an object. If
322 certain strings in the object are too long (e.g. function signatures) they get
322 certain strings in the object are too long (e.g. function signatures) they get
323 snipped in the center for brevity. This system gives access variable types and
323 snipped in the center for brevity. This system gives access variable types and
324 values, docstrings, function prototypes and other useful information.
324 values, docstrings, function prototypes and other useful information.
325
325
326 If the information will not fit in the terminal, it is displayed in a pager
326 If the information will not fit in the terminal, it is displayed in a pager
327 (``less`` if available, otherwise a basic internal pager).
327 (``less`` if available, otherwise a basic internal pager).
328
328
329 Typing ``??word`` or ``word??`` gives access to the full information, including
329 Typing ``??word`` or ``word??`` gives access to the full information, including
330 the source code where possible. Long strings are not snipped.
330 the source code where possible. Long strings are not snipped.
331
331
332 The following magic functions are particularly useful for gathering
332 The following magic functions are particularly useful for gathering
333 information about your working environment. You can get more details by
333 information about your working environment. You can get more details by
334 typing ``%magic`` or querying them individually (``%function_name?``);
334 typing ``%magic`` or querying them individually (``%function_name?``);
335 this is just a summary:
335 this is just a summary:
336
336
337 * **%pdoc <object>**: Print (or run through a pager if too long) the
337 * **%pdoc <object>**: Print (or run through a pager if too long) the
338 docstring for an object. If the given object is a class, it will
338 docstring for an object. If the given object is a class, it will
339 print both the class and the constructor docstrings.
339 print both the class and the constructor docstrings.
340 * **%pdef <object>**: Print the definition header for any callable
340 * **%pdef <object>**: Print the call signature for any callable
341 object. If the object is a class, print the constructor information.
341 object. If the object is a class, print the constructor information.
342 * **%psource <object>**: Print (or run through a pager if too long)
342 * **%psource <object>**: Print (or run through a pager if too long)
343 the source code for an object.
343 the source code for an object.
344 * **%pfile <object>**: Show the entire source file where an object was
344 * **%pfile <object>**: Show the entire source file where an object was
345 defined via a pager, opening it at the line where the object
345 defined via a pager, opening it at the line where the object
346 definition begins.
346 definition begins.
347 * **%who/%whos**: These functions give information about identifiers
347 * **%who/%whos**: These functions give information about identifiers
348 you have defined interactively (not things you loaded or defined
348 you have defined interactively (not things you loaded or defined
349 in your configuration files). %who just prints a list of
349 in your configuration files). %who just prints a list of
350 identifiers and %whos prints a table with some basic details about
350 identifiers and %whos prints a table with some basic details about
351 each identifier.
351 each identifier.
352
352
353 Note that the dynamic object information functions (?/??, ``%pdoc``,
353 Note that the dynamic object information functions (?/??, ``%pdoc``,
354 ``%pfile``, ``%pdef``, ``%psource``) work on object attributes, as well as
354 ``%pfile``, ``%pdef``, ``%psource``) work on object attributes, as well as
355 directly on variables. For example, after doing ``import os``, you can use
355 directly on variables. For example, after doing ``import os``, you can use
356 ``os.path.abspath??``.
356 ``os.path.abspath??``.
357
357
358 .. _readline:
358 .. _readline:
359
359
360 Readline-based features
360 Readline-based features
361 -----------------------
361 -----------------------
362
362
363 These features require the GNU readline library, so they won't work if your
363 These features require the GNU readline library, so they won't work if your
364 Python installation lacks readline support. We will first describe the default
364 Python installation lacks readline support. We will first describe the default
365 behavior IPython uses, and then how to change it to suit your preferences.
365 behavior IPython uses, and then how to change it to suit your preferences.
366
366
367
367
368 Command line completion
368 Command line completion
369 +++++++++++++++++++++++
369 +++++++++++++++++++++++
370
370
371 At any time, hitting TAB will complete any available python commands or
371 At any time, hitting TAB will complete any available python commands or
372 variable names, and show you a list of the possible completions if
372 variable names, and show you a list of the possible completions if
373 there's no unambiguous one. It will also complete filenames in the
373 there's no unambiguous one. It will also complete filenames in the
374 current directory if no python names match what you've typed so far.
374 current directory if no python names match what you've typed so far.
375
375
376
376
377 Search command history
377 Search command history
378 ++++++++++++++++++++++
378 ++++++++++++++++++++++
379
379
380 IPython provides two ways for searching through previous input and thus
380 IPython provides two ways for searching through previous input and thus
381 reduce the need for repetitive typing:
381 reduce the need for repetitive typing:
382
382
383 1. Start typing, and then use Ctrl-p (previous,up) and Ctrl-n
383 1. Start typing, and then use Ctrl-p (previous,up) and Ctrl-n
384 (next,down) to search through only the history items that match
384 (next,down) to search through only the history items that match
385 what you've typed so far. If you use Ctrl-p/Ctrl-n at a blank
385 what you've typed so far. If you use Ctrl-p/Ctrl-n at a blank
386 prompt, they just behave like normal arrow keys.
386 prompt, they just behave like normal arrow keys.
387 2. Hit Ctrl-r: opens a search prompt. Begin typing and the system
387 2. Hit Ctrl-r: opens a search prompt. Begin typing and the system
388 searches your history for lines that contain what you've typed so
388 searches your history for lines that contain what you've typed so
389 far, completing as much as it can.
389 far, completing as much as it can.
390
390
391
391
392 Persistent command history across sessions
392 Persistent command history across sessions
393 ++++++++++++++++++++++++++++++++++++++++++
393 ++++++++++++++++++++++++++++++++++++++++++
394
394
395 IPython will save your input history when it leaves and reload it next
395 IPython will save your input history when it leaves and reload it next
396 time you restart it. By default, the history file is named
396 time you restart it. By default, the history file is named
397 $IPYTHONDIR/profile_<name>/history.sqlite. This allows you to keep
397 $IPYTHONDIR/profile_<name>/history.sqlite. This allows you to keep
398 separate histories related to various tasks: commands related to
398 separate histories related to various tasks: commands related to
399 numerical work will not be clobbered by a system shell history, for
399 numerical work will not be clobbered by a system shell history, for
400 example.
400 example.
401
401
402
402
403 Autoindent
403 Autoindent
404 ++++++++++
404 ++++++++++
405
405
406 IPython can recognize lines ending in ':' and indent the next line,
406 IPython can recognize lines ending in ':' and indent the next line,
407 while also un-indenting automatically after 'raise' or 'return'.
407 while also un-indenting automatically after 'raise' or 'return'.
408
408
409 This feature uses the readline library, so it will honor your
409 This feature uses the readline library, so it will honor your
410 :file:`~/.inputrc` configuration (or whatever file your INPUTRC variable points
410 :file:`~/.inputrc` configuration (or whatever file your INPUTRC variable points
411 to). Adding the following lines to your :file:`.inputrc` file can make
411 to). Adding the following lines to your :file:`.inputrc` file can make
412 indenting/unindenting more convenient (M-i indents, M-u unindents)::
412 indenting/unindenting more convenient (M-i indents, M-u unindents)::
413
413
414 $if Python
414 $if Python
415 "\M-i": " "
415 "\M-i": " "
416 "\M-u": "\d\d\d\d"
416 "\M-u": "\d\d\d\d"
417 $endif
417 $endif
418
418
419 Note that there are 4 spaces between the quote marks after "M-i" above.
419 Note that there are 4 spaces between the quote marks after "M-i" above.
420
420
421 .. warning::
421 .. warning::
422
422
423 Setting the above indents will cause problems with unicode text entry in
423 Setting the above indents will cause problems with unicode text entry in
424 the terminal.
424 the terminal.
425
425
426 .. warning::
426 .. warning::
427
427
428 Autoindent is ON by default, but it can cause problems with the pasting of
428 Autoindent is ON by default, but it can cause problems with the pasting of
429 multi-line indented code (the pasted code gets re-indented on each line). A
429 multi-line indented code (the pasted code gets re-indented on each line). A
430 magic function %autoindent allows you to toggle it on/off at runtime. You
430 magic function %autoindent allows you to toggle it on/off at runtime. You
431 can also disable it permanently on in your :file:`ipython_config.py` file
431 can also disable it permanently on in your :file:`ipython_config.py` file
432 (set TerminalInteractiveShell.autoindent=False).
432 (set TerminalInteractiveShell.autoindent=False).
433
433
434 If you want to paste multiple lines in the terminal, it is recommended that
434 If you want to paste multiple lines in the terminal, it is recommended that
435 you use ``%paste``.
435 you use ``%paste``.
436
436
437
437
438 Customizing readline behavior
438 Customizing readline behavior
439 +++++++++++++++++++++++++++++
439 +++++++++++++++++++++++++++++
440
440
441 All these features are based on the GNU readline library, which has an
441 All these features are based on the GNU readline library, which has an
442 extremely customizable interface. Normally, readline is configured via a
442 extremely customizable interface. Normally, readline is configured via a
443 file which defines the behavior of the library; the details of the
443 file which defines the behavior of the library; the details of the
444 syntax for this can be found in the readline documentation available
444 syntax for this can be found in the readline documentation available
445 with your system or on the Internet. IPython doesn't read this file (if
445 with your system or on the Internet. IPython doesn't read this file (if
446 it exists) directly, but it does support passing to readline valid
446 it exists) directly, but it does support passing to readline valid
447 options via a simple interface. In brief, you can customize readline by
447 options via a simple interface. In brief, you can customize readline by
448 setting the following options in your configuration file (note
448 setting the following options in your configuration file (note
449 that these options can not be specified at the command line):
449 that these options can not be specified at the command line):
450
450
451 * **readline_parse_and_bind**: this holds a list of strings to be executed
451 * **readline_parse_and_bind**: this holds a list of strings to be executed
452 via a readline.parse_and_bind() command. The syntax for valid commands
452 via a readline.parse_and_bind() command. The syntax for valid commands
453 of this kind can be found by reading the documentation for the GNU
453 of this kind can be found by reading the documentation for the GNU
454 readline library, as these commands are of the kind which readline
454 readline library, as these commands are of the kind which readline
455 accepts in its configuration file.
455 accepts in its configuration file.
456 * **readline_remove_delims**: a string of characters to be removed
456 * **readline_remove_delims**: a string of characters to be removed
457 from the default word-delimiters list used by readline, so that
457 from the default word-delimiters list used by readline, so that
458 completions may be performed on strings which contain them. Do not
458 completions may be performed on strings which contain them. Do not
459 change the default value unless you know what you're doing.
459 change the default value unless you know what you're doing.
460
460
461 You will find the default values in your configuration file.
461 You will find the default values in your configuration file.
462
462
463
463
464 Session logging and restoring
464 Session logging and restoring
465 -----------------------------
465 -----------------------------
466
466
467 You can log all input from a session either by starting IPython with the
467 You can log all input from a session either by starting IPython with the
468 command line switch ``--logfile=foo.py`` (see :ref:`here <command_line_options>`)
468 command line switch ``--logfile=foo.py`` (see :ref:`here <command_line_options>`)
469 or by activating the logging at any moment with the magic function %logstart.
469 or by activating the logging at any moment with the magic function %logstart.
470
470
471 Log files can later be reloaded by running them as scripts and IPython
471 Log files can later be reloaded by running them as scripts and IPython
472 will attempt to 'replay' the log by executing all the lines in it, thus
472 will attempt to 'replay' the log by executing all the lines in it, thus
473 restoring the state of a previous session. This feature is not quite
473 restoring the state of a previous session. This feature is not quite
474 perfect, but can still be useful in many cases.
474 perfect, but can still be useful in many cases.
475
475
476 The log files can also be used as a way to have a permanent record of
476 The log files can also be used as a way to have a permanent record of
477 any code you wrote while experimenting. Log files are regular text files
477 any code you wrote while experimenting. Log files are regular text files
478 which you can later open in your favorite text editor to extract code or
478 which you can later open in your favorite text editor to extract code or
479 to 'clean them up' before using them to replay a session.
479 to 'clean them up' before using them to replay a session.
480
480
481 The `%logstart` function for activating logging in mid-session is used as
481 The `%logstart` function for activating logging in mid-session is used as
482 follows::
482 follows::
483
483
484 %logstart [log_name [log_mode]]
484 %logstart [log_name [log_mode]]
485
485
486 If no name is given, it defaults to a file named 'ipython_log.py' in your
486 If no name is given, it defaults to a file named 'ipython_log.py' in your
487 current working directory, in 'rotate' mode (see below).
487 current working directory, in 'rotate' mode (see below).
488
488
489 '%logstart name' saves to file 'name' in 'backup' mode. It saves your
489 '%logstart name' saves to file 'name' in 'backup' mode. It saves your
490 history up to that point and then continues logging.
490 history up to that point and then continues logging.
491
491
492 %logstart takes a second optional parameter: logging mode. This can be
492 %logstart takes a second optional parameter: logging mode. This can be
493 one of (note that the modes are given unquoted):
493 one of (note that the modes are given unquoted):
494
494
495 * [over:] overwrite existing log_name.
495 * [over:] overwrite existing log_name.
496 * [backup:] rename (if exists) to log_name~ and start log_name.
496 * [backup:] rename (if exists) to log_name~ and start log_name.
497 * [append:] well, that says it.
497 * [append:] well, that says it.
498 * [rotate:] create rotating logs log_name.1~, log_name.2~, etc.
498 * [rotate:] create rotating logs log_name.1~, log_name.2~, etc.
499
499
500 The %logoff and %logon functions allow you to temporarily stop and
500 The %logoff and %logon functions allow you to temporarily stop and
501 resume logging to a file which had previously been started with
501 resume logging to a file which had previously been started with
502 %logstart. They will fail (with an explanation) if you try to use them
502 %logstart. They will fail (with an explanation) if you try to use them
503 before logging has been started.
503 before logging has been started.
504
504
505 .. _system_shell_access:
505 .. _system_shell_access:
506
506
507 System shell access
507 System shell access
508 -------------------
508 -------------------
509
509
510 Any input line beginning with a ! character is passed verbatim (minus
510 Any input line beginning with a ! character is passed verbatim (minus
511 the !, of course) to the underlying operating system. For example,
511 the !, of course) to the underlying operating system. For example,
512 typing ``!ls`` will run 'ls' in the current directory.
512 typing ``!ls`` will run 'ls' in the current directory.
513
513
514 Manual capture of command output
514 Manual capture of command output
515 --------------------------------
515 --------------------------------
516
516
517 You can assign the result of a system command to a Python variable with the
517 You can assign the result of a system command to a Python variable with the
518 syntax ``myfiles = !ls``. This gets machine readable output from stdout
518 syntax ``myfiles = !ls``. This gets machine readable output from stdout
519 (e.g. without colours), and splits on newlines. To explicitly get this sort of
519 (e.g. without colours), and splits on newlines. To explicitly get this sort of
520 output without assigning to a variable, use two exclamation marks (``!!ls``) or
520 output without assigning to a variable, use two exclamation marks (``!!ls``) or
521 the ``%sx`` magic command.
521 the ``%sx`` magic command.
522
522
523 The captured list has some convenience features. ``myfiles.n`` or ``myfiles.s``
523 The captured list has some convenience features. ``myfiles.n`` or ``myfiles.s``
524 returns a string delimited by newlines or spaces, respectively. ``myfiles.p``
524 returns a string delimited by newlines or spaces, respectively. ``myfiles.p``
525 produces `path objects <http://pypi.python.org/pypi/path.py>`_ from the list items.
525 produces `path objects <http://pypi.python.org/pypi/path.py>`_ from the list items.
526 See :ref:`string_lists` for details.
526 See :ref:`string_lists` for details.
527
527
528 IPython also allows you to expand the value of python variables when
528 IPython also allows you to expand the value of python variables when
529 making system calls. Wrap variables or expressions in {braces}::
529 making system calls. Wrap variables or expressions in {braces}::
530
530
531 In [1]: pyvar = 'Hello world'
531 In [1]: pyvar = 'Hello world'
532 In [2]: !echo "A python variable: {pyvar}"
532 In [2]: !echo "A python variable: {pyvar}"
533 A python variable: Hello world
533 A python variable: Hello world
534 In [3]: import math
534 In [3]: import math
535 In [4]: x = 8
535 In [4]: x = 8
536 In [5]: !echo {math.factorial(x)}
536 In [5]: !echo {math.factorial(x)}
537 40320
537 40320
538
538
539 For simple cases, you can alternatively prepend $ to a variable name::
539 For simple cases, you can alternatively prepend $ to a variable name::
540
540
541 In [6]: !echo $sys.argv
541 In [6]: !echo $sys.argv
542 [/home/fperez/usr/bin/ipython]
542 [/home/fperez/usr/bin/ipython]
543 In [7]: !echo "A system variable: $$HOME" # Use $$ for literal $
543 In [7]: !echo "A system variable: $$HOME" # Use $$ for literal $
544 A system variable: /home/fperez
544 A system variable: /home/fperez
545
545
546 System command aliases
546 System command aliases
547 ----------------------
547 ----------------------
548
548
549 The %alias magic function allows you to define magic functions which are in fact
549 The %alias magic function allows you to define magic functions which are in fact
550 system shell commands. These aliases can have parameters.
550 system shell commands. These aliases can have parameters.
551
551
552 ``%alias alias_name cmd`` defines 'alias_name' as an alias for 'cmd'
552 ``%alias alias_name cmd`` defines 'alias_name' as an alias for 'cmd'
553
553
554 Then, typing ``alias_name params`` will execute the system command 'cmd
554 Then, typing ``alias_name params`` will execute the system command 'cmd
555 params' (from your underlying operating system).
555 params' (from your underlying operating system).
556
556
557 You can also define aliases with parameters using %s specifiers (one per
557 You can also define aliases with parameters using %s specifiers (one per
558 parameter). The following example defines the parts function as an
558 parameter). The following example defines the parts function as an
559 alias to the command 'echo first %s second %s' where each %s will be
559 alias to the command 'echo first %s second %s' where each %s will be
560 replaced by a positional parameter to the call to %parts::
560 replaced by a positional parameter to the call to %parts::
561
561
562 In [1]: %alias parts echo first %s second %s
562 In [1]: %alias parts echo first %s second %s
563 In [2]: parts A B
563 In [2]: parts A B
564 first A second B
564 first A second B
565 In [3]: parts A
565 In [3]: parts A
566 ERROR: Alias <parts> requires 2 arguments, 1 given.
566 ERROR: Alias <parts> requires 2 arguments, 1 given.
567
567
568 If called with no parameters, %alias prints the table of currently
568 If called with no parameters, %alias prints the table of currently
569 defined aliases.
569 defined aliases.
570
570
571 The %rehashx magic allows you to load your entire $PATH as
571 The %rehashx magic allows you to load your entire $PATH as
572 ipython aliases. See its docstring for further details.
572 ipython aliases. See its docstring for further details.
573
573
574
574
575 .. _dreload:
575 .. _dreload:
576
576
577 Recursive reload
577 Recursive reload
578 ----------------
578 ----------------
579
579
580 The :mod:`IPython.lib.deepreload` module allows you to recursively reload a
580 The :mod:`IPython.lib.deepreload` module allows you to recursively reload a
581 module: changes made to any of its dependencies will be reloaded without
581 module: changes made to any of its dependencies will be reloaded without
582 having to exit. To start using it, do::
582 having to exit. To start using it, do::
583
583
584 from IPython.lib.deepreload import reload as dreload
584 from IPython.lib.deepreload import reload as dreload
585
585
586
586
587 Verbose and colored exception traceback printouts
587 Verbose and colored exception traceback printouts
588 -------------------------------------------------
588 -------------------------------------------------
589
589
590 IPython provides the option to see very detailed exception tracebacks,
590 IPython provides the option to see very detailed exception tracebacks,
591 which can be especially useful when debugging large programs. You can
591 which can be especially useful when debugging large programs. You can
592 run any Python file with the %run function to benefit from these
592 run any Python file with the %run function to benefit from these
593 detailed tracebacks. Furthermore, both normal and verbose tracebacks can
593 detailed tracebacks. Furthermore, both normal and verbose tracebacks can
594 be colored (if your terminal supports it) which makes them much easier
594 be colored (if your terminal supports it) which makes them much easier
595 to parse visually.
595 to parse visually.
596
596
597 See the magic xmode and colors functions for details (just type %magic).
597 See the magic xmode and colors functions for details (just type %magic).
598
598
599 These features are basically a terminal version of Ka-Ping Yee's cgitb
599 These features are basically a terminal version of Ka-Ping Yee's cgitb
600 module, now part of the standard Python library.
600 module, now part of the standard Python library.
601
601
602
602
603 .. _input_caching:
603 .. _input_caching:
604
604
605 Input caching system
605 Input caching system
606 --------------------
606 --------------------
607
607
608 IPython offers numbered prompts (In/Out) with input and output caching
608 IPython offers numbered prompts (In/Out) with input and output caching
609 (also referred to as 'input history'). All input is saved and can be
609 (also referred to as 'input history'). All input is saved and can be
610 retrieved as variables (besides the usual arrow key recall), in
610 retrieved as variables (besides the usual arrow key recall), in
611 addition to the %rep magic command that brings a history entry
611 addition to the %rep magic command that brings a history entry
612 up for editing on the next command line.
612 up for editing on the next command line.
613
613
614 The following GLOBAL variables always exist (so don't overwrite them!):
614 The following GLOBAL variables always exist (so don't overwrite them!):
615
615
616 * _i, _ii, _iii: store previous, next previous and next-next previous inputs.
616 * _i, _ii, _iii: store previous, next previous and next-next previous inputs.
617 * In, _ih : a list of all inputs; _ih[n] is the input from line n. If you
617 * In, _ih : a list of all inputs; _ih[n] is the input from line n. If you
618 overwrite In with a variable of your own, you can remake the assignment to the
618 overwrite In with a variable of your own, you can remake the assignment to the
619 internal list with a simple ``In=_ih``.
619 internal list with a simple ``In=_ih``.
620
620
621 Additionally, global variables named _i<n> are dynamically created (<n>
621 Additionally, global variables named _i<n> are dynamically created (<n>
622 being the prompt counter), so ``_i<n> == _ih[<n>] == In[<n>]``.
622 being the prompt counter), so ``_i<n> == _ih[<n>] == In[<n>]``.
623
623
624 For example, what you typed at prompt 14 is available as _i14, _ih[14]
624 For example, what you typed at prompt 14 is available as _i14, _ih[14]
625 and In[14].
625 and In[14].
626
626
627 This allows you to easily cut and paste multi line interactive prompts
627 This allows you to easily cut and paste multi line interactive prompts
628 by printing them out: they print like a clean string, without prompt
628 by printing them out: they print like a clean string, without prompt
629 characters. You can also manipulate them like regular variables (they
629 characters. You can also manipulate them like regular variables (they
630 are strings), modify or exec them (typing ``exec _i9`` will re-execute the
630 are strings), modify or exec them (typing ``exec _i9`` will re-execute the
631 contents of input prompt 9.
631 contents of input prompt 9.
632
632
633 You can also re-execute multiple lines of input easily by using the
633 You can also re-execute multiple lines of input easily by using the
634 magic %rerun or %macro functions. The macro system also allows you to re-execute
634 magic %rerun or %macro functions. The macro system also allows you to re-execute
635 previous lines which include magic function calls (which require special
635 previous lines which include magic function calls (which require special
636 processing). Type %macro? for more details on the macro system.
636 processing). Type %macro? for more details on the macro system.
637
637
638 A history function %hist allows you to see any part of your input
638 A history function %hist allows you to see any part of your input
639 history by printing a range of the _i variables.
639 history by printing a range of the _i variables.
640
640
641 You can also search ('grep') through your history by typing
641 You can also search ('grep') through your history by typing
642 ``%hist -g somestring``. This is handy for searching for URLs, IP addresses,
642 ``%hist -g somestring``. This is handy for searching for URLs, IP addresses,
643 etc. You can bring history entries listed by '%hist -g' up for editing
643 etc. You can bring history entries listed by '%hist -g' up for editing
644 with the %recall command, or run them immediately with %rerun.
644 with the %recall command, or run them immediately with %rerun.
645
645
646 .. _output_caching:
646 .. _output_caching:
647
647
648 Output caching system
648 Output caching system
649 ---------------------
649 ---------------------
650
650
651 For output that is returned from actions, a system similar to the input
651 For output that is returned from actions, a system similar to the input
652 cache exists but using _ instead of _i. Only actions that produce a
652 cache exists but using _ instead of _i. Only actions that produce a
653 result (NOT assignments, for example) are cached. If you are familiar
653 result (NOT assignments, for example) are cached. If you are familiar
654 with Mathematica, IPython's _ variables behave exactly like
654 with Mathematica, IPython's _ variables behave exactly like
655 Mathematica's % variables.
655 Mathematica's % variables.
656
656
657 The following GLOBAL variables always exist (so don't overwrite them!):
657 The following GLOBAL variables always exist (so don't overwrite them!):
658
658
659 * [_] (a single underscore) : stores previous output, like Python's
659 * [_] (a single underscore) : stores previous output, like Python's
660 default interpreter.
660 default interpreter.
661 * [__] (two underscores): next previous.
661 * [__] (two underscores): next previous.
662 * [___] (three underscores): next-next previous.
662 * [___] (three underscores): next-next previous.
663
663
664 Additionally, global variables named _<n> are dynamically created (<n>
664 Additionally, global variables named _<n> are dynamically created (<n>
665 being the prompt counter), such that the result of output <n> is always
665 being the prompt counter), such that the result of output <n> is always
666 available as _<n> (don't use the angle brackets, just the number, e.g.
666 available as _<n> (don't use the angle brackets, just the number, e.g.
667 _21).
667 _21).
668
668
669 These variables are also stored in a global dictionary (not a
669 These variables are also stored in a global dictionary (not a
670 list, since it only has entries for lines which returned a result)
670 list, since it only has entries for lines which returned a result)
671 available under the names _oh and Out (similar to _ih and In). So the
671 available under the names _oh and Out (similar to _ih and In). So the
672 output from line 12 can be obtained as _12, Out[12] or _oh[12]. If you
672 output from line 12 can be obtained as _12, Out[12] or _oh[12]. If you
673 accidentally overwrite the Out variable you can recover it by typing
673 accidentally overwrite the Out variable you can recover it by typing
674 'Out=_oh' at the prompt.
674 'Out=_oh' at the prompt.
675
675
676 This system obviously can potentially put heavy memory demands on your
676 This system obviously can potentially put heavy memory demands on your
677 system, since it prevents Python's garbage collector from removing any
677 system, since it prevents Python's garbage collector from removing any
678 previously computed results. You can control how many results are kept
678 previously computed results. You can control how many results are kept
679 in memory with the option (at the command line or in your configuration
679 in memory with the option (at the command line or in your configuration
680 file) cache_size. If you set it to 0, the whole system is completely
680 file) cache_size. If you set it to 0, the whole system is completely
681 disabled and the prompts revert to the classic '>>>' of normal Python.
681 disabled and the prompts revert to the classic '>>>' of normal Python.
682
682
683
683
684 Directory history
684 Directory history
685 -----------------
685 -----------------
686
686
687 Your history of visited directories is kept in the global list _dh, and
687 Your history of visited directories is kept in the global list _dh, and
688 the magic %cd command can be used to go to any entry in that list. The
688 the magic %cd command can be used to go to any entry in that list. The
689 %dhist command allows you to view this history. Do ``cd -<TAB>`` to
689 %dhist command allows you to view this history. Do ``cd -<TAB>`` to
690 conveniently view the directory history.
690 conveniently view the directory history.
691
691
692
692
693 Automatic parentheses and quotes
693 Automatic parentheses and quotes
694 --------------------------------
694 --------------------------------
695
695
696 These features were adapted from Nathan Gray's LazyPython. They are
696 These features were adapted from Nathan Gray's LazyPython. They are
697 meant to allow less typing for common situations.
697 meant to allow less typing for common situations.
698
698
699
699
700 Automatic parentheses
700 Automatic parentheses
701 +++++++++++++++++++++
701 +++++++++++++++++++++
702
702
703 Callable objects (i.e. functions, methods, etc) can be invoked like this
703 Callable objects (i.e. functions, methods, etc) can be invoked like this
704 (notice the commas between the arguments)::
704 (notice the commas between the arguments)::
705
705
706 In [1]: callable_ob arg1, arg2, arg3
706 In [1]: callable_ob arg1, arg2, arg3
707 ------> callable_ob(arg1, arg2, arg3)
707 ------> callable_ob(arg1, arg2, arg3)
708
708
709 You can force automatic parentheses by using '/' as the first character
709 You can force automatic parentheses by using '/' as the first character
710 of a line. For example::
710 of a line. For example::
711
711
712 In [2]: /globals # becomes 'globals()'
712 In [2]: /globals # becomes 'globals()'
713
713
714 Note that the '/' MUST be the first character on the line! This won't work::
714 Note that the '/' MUST be the first character on the line! This won't work::
715
715
716 In [3]: print /globals # syntax error
716 In [3]: print /globals # syntax error
717
717
718 In most cases the automatic algorithm should work, so you should rarely
718 In most cases the automatic algorithm should work, so you should rarely
719 need to explicitly invoke /. One notable exception is if you are trying
719 need to explicitly invoke /. One notable exception is if you are trying
720 to call a function with a list of tuples as arguments (the parenthesis
720 to call a function with a list of tuples as arguments (the parenthesis
721 will confuse IPython)::
721 will confuse IPython)::
722
722
723 In [4]: zip (1,2,3),(4,5,6) # won't work
723 In [4]: zip (1,2,3),(4,5,6) # won't work
724
724
725 but this will work::
725 but this will work::
726
726
727 In [5]: /zip (1,2,3),(4,5,6)
727 In [5]: /zip (1,2,3),(4,5,6)
728 ------> zip ((1,2,3),(4,5,6))
728 ------> zip ((1,2,3),(4,5,6))
729 Out[5]: [(1, 4), (2, 5), (3, 6)]
729 Out[5]: [(1, 4), (2, 5), (3, 6)]
730
730
731 IPython tells you that it has altered your command line by displaying
731 IPython tells you that it has altered your command line by displaying
732 the new command line preceded by ->. e.g.::
732 the new command line preceded by ->. e.g.::
733
733
734 In [6]: callable list
734 In [6]: callable list
735 ------> callable(list)
735 ------> callable(list)
736
736
737
737
738 Automatic quoting
738 Automatic quoting
739 +++++++++++++++++
739 +++++++++++++++++
740
740
741 You can force automatic quoting of a function's arguments by using ','
741 You can force automatic quoting of a function's arguments by using ','
742 or ';' as the first character of a line. For example::
742 or ';' as the first character of a line. For example::
743
743
744 In [1]: ,my_function /home/me # becomes my_function("/home/me")
744 In [1]: ,my_function /home/me # becomes my_function("/home/me")
745
745
746 If you use ';' the whole argument is quoted as a single string, while ',' splits
746 If you use ';' the whole argument is quoted as a single string, while ',' splits
747 on whitespace::
747 on whitespace::
748
748
749 In [2]: ,my_function a b c # becomes my_function("a","b","c")
749 In [2]: ,my_function a b c # becomes my_function("a","b","c")
750
750
751 In [3]: ;my_function a b c # becomes my_function("a b c")
751 In [3]: ;my_function a b c # becomes my_function("a b c")
752
752
753 Note that the ',' or ';' MUST be the first character on the line! This
753 Note that the ',' or ';' MUST be the first character on the line! This
754 won't work::
754 won't work::
755
755
756 In [4]: x = ,my_function /home/me # syntax error
756 In [4]: x = ,my_function /home/me # syntax error
757
757
758 IPython as your default Python environment
758 IPython as your default Python environment
759 ==========================================
759 ==========================================
760
760
761 Python honors the environment variable PYTHONSTARTUP and will execute at
761 Python honors the environment variable PYTHONSTARTUP and will execute at
762 startup the file referenced by this variable. If you put the following code at
762 startup the file referenced by this variable. If you put the following code at
763 the end of that file, then IPython will be your working environment anytime you
763 the end of that file, then IPython will be your working environment anytime you
764 start Python::
764 start Python::
765
765
766 from IPython.frontend.terminal.ipapp import launch_new_instance
766 from IPython.frontend.terminal.ipapp import launch_new_instance
767 launch_new_instance()
767 launch_new_instance()
768 raise SystemExit
768 raise SystemExit
769
769
770 The ``raise SystemExit`` is needed to exit Python when
770 The ``raise SystemExit`` is needed to exit Python when
771 it finishes, otherwise you'll be back at the normal Python '>>>'
771 it finishes, otherwise you'll be back at the normal Python '>>>'
772 prompt.
772 prompt.
773
773
774 This is probably useful to developers who manage multiple Python
774 This is probably useful to developers who manage multiple Python
775 versions and don't want to have correspondingly multiple IPython
775 versions and don't want to have correspondingly multiple IPython
776 versions. Note that in this mode, there is no way to pass IPython any
776 versions. Note that in this mode, there is no way to pass IPython any
777 command-line options, as those are trapped first by Python itself.
777 command-line options, as those are trapped first by Python itself.
778
778
779 .. _Embedding:
779 .. _Embedding:
780
780
781 Embedding IPython
781 Embedding IPython
782 =================
782 =================
783
783
784 It is possible to start an IPython instance inside your own Python
784 It is possible to start an IPython instance inside your own Python
785 programs. This allows you to evaluate dynamically the state of your
785 programs. This allows you to evaluate dynamically the state of your
786 code, operate with your variables, analyze them, etc. Note however that
786 code, operate with your variables, analyze them, etc. Note however that
787 any changes you make to values while in the shell do not propagate back
787 any changes you make to values while in the shell do not propagate back
788 to the running code, so it is safe to modify your values because you
788 to the running code, so it is safe to modify your values because you
789 won't break your code in bizarre ways by doing so.
789 won't break your code in bizarre ways by doing so.
790
790
791 .. note::
791 .. note::
792
792
793 At present, trying to embed IPython from inside IPython causes problems. Run
793 At present, trying to embed IPython from inside IPython causes problems. Run
794 the code samples below outside IPython.
794 the code samples below outside IPython.
795
795
796 This feature allows you to easily have a fully functional python
796 This feature allows you to easily have a fully functional python
797 environment for doing object introspection anywhere in your code with a
797 environment for doing object introspection anywhere in your code with a
798 simple function call. In some cases a simple print statement is enough,
798 simple function call. In some cases a simple print statement is enough,
799 but if you need to do more detailed analysis of a code fragment this
799 but if you need to do more detailed analysis of a code fragment this
800 feature can be very valuable.
800 feature can be very valuable.
801
801
802 It can also be useful in scientific computing situations where it is
802 It can also be useful in scientific computing situations where it is
803 common to need to do some automatic, computationally intensive part and
803 common to need to do some automatic, computationally intensive part and
804 then stop to look at data, plots, etc.
804 then stop to look at data, plots, etc.
805 Opening an IPython instance will give you full access to your data and
805 Opening an IPython instance will give you full access to your data and
806 functions, and you can resume program execution once you are done with
806 functions, and you can resume program execution once you are done with
807 the interactive part (perhaps to stop again later, as many times as
807 the interactive part (perhaps to stop again later, as many times as
808 needed).
808 needed).
809
809
810 The following code snippet is the bare minimum you need to include in
810 The following code snippet is the bare minimum you need to include in
811 your Python programs for this to work (detailed examples follow later)::
811 your Python programs for this to work (detailed examples follow later)::
812
812
813 from IPython import embed
813 from IPython import embed
814
814
815 embed() # this call anywhere in your program will start IPython
815 embed() # this call anywhere in your program will start IPython
816
816
817 .. note::
817 .. note::
818
818
819 As of 0.13, you can embed an IPython *kernel*, for use with qtconsole,
819 As of 0.13, you can embed an IPython *kernel*, for use with qtconsole,
820 etc. via ``IPython.embed_kernel()`` instead of ``IPython.embed()``.
820 etc. via ``IPython.embed_kernel()`` instead of ``IPython.embed()``.
821 It should function just the same as regular embed, but you connect
821 It should function just the same as regular embed, but you connect
822 an external frontend rather than IPython starting up in the local
822 an external frontend rather than IPython starting up in the local
823 terminal.
823 terminal.
824
824
825 You can run embedded instances even in code which is itself being run at
825 You can run embedded instances even in code which is itself being run at
826 the IPython interactive prompt with '%run <filename>'. Since it's easy
826 the IPython interactive prompt with '%run <filename>'. Since it's easy
827 to get lost as to where you are (in your top-level IPython or in your
827 to get lost as to where you are (in your top-level IPython or in your
828 embedded one), it's a good idea in such cases to set the in/out prompts
828 embedded one), it's a good idea in such cases to set the in/out prompts
829 to something different for the embedded instances. The code examples
829 to something different for the embedded instances. The code examples
830 below illustrate this.
830 below illustrate this.
831
831
832 You can also have multiple IPython instances in your program and open
832 You can also have multiple IPython instances in your program and open
833 them separately, for example with different options for data
833 them separately, for example with different options for data
834 presentation. If you close and open the same instance multiple times,
834 presentation. If you close and open the same instance multiple times,
835 its prompt counters simply continue from each execution to the next.
835 its prompt counters simply continue from each execution to the next.
836
836
837 Please look at the docstrings in the :mod:`~IPython.frontend.terminal.embed`
837 Please look at the docstrings in the :mod:`~IPython.frontend.terminal.embed`
838 module for more details on the use of this system.
838 module for more details on the use of this system.
839
839
840 The following sample file illustrating how to use the embedding
840 The following sample file illustrating how to use the embedding
841 functionality is provided in the examples directory as example-embed.py.
841 functionality is provided in the examples directory as example-embed.py.
842 It should be fairly self-explanatory:
842 It should be fairly self-explanatory:
843
843
844 .. literalinclude:: ../../examples/core/example-embed.py
844 .. literalinclude:: ../../examples/core/example-embed.py
845 :language: python
845 :language: python
846
846
847 Once you understand how the system functions, you can use the following
847 Once you understand how the system functions, you can use the following
848 code fragments in your programs which are ready for cut and paste:
848 code fragments in your programs which are ready for cut and paste:
849
849
850 .. literalinclude:: ../../examples/core/example-embed-short.py
850 .. literalinclude:: ../../examples/core/example-embed-short.py
851 :language: python
851 :language: python
852
852
853 Using the Python debugger (pdb)
853 Using the Python debugger (pdb)
854 ===============================
854 ===============================
855
855
856 Running entire programs via pdb
856 Running entire programs via pdb
857 -------------------------------
857 -------------------------------
858
858
859 pdb, the Python debugger, is a powerful interactive debugger which
859 pdb, the Python debugger, is a powerful interactive debugger which
860 allows you to step through code, set breakpoints, watch variables,
860 allows you to step through code, set breakpoints, watch variables,
861 etc. IPython makes it very easy to start any script under the control
861 etc. IPython makes it very easy to start any script under the control
862 of pdb, regardless of whether you have wrapped it into a 'main()'
862 of pdb, regardless of whether you have wrapped it into a 'main()'
863 function or not. For this, simply type '%run -d myscript' at an
863 function or not. For this, simply type '%run -d myscript' at an
864 IPython prompt. See the %run command's documentation (via '%run?' or
864 IPython prompt. See the %run command's documentation (via '%run?' or
865 in Sec. magic_ for more details, including how to control where pdb
865 in Sec. magic_ for more details, including how to control where pdb
866 will stop execution first.
866 will stop execution first.
867
867
868 For more information on the use of the pdb debugger, read the included
868 For more information on the use of the pdb debugger, read the included
869 pdb.doc file (part of the standard Python distribution). On a stock
869 pdb.doc file (part of the standard Python distribution). On a stock
870 Linux system it is located at /usr/lib/python2.3/pdb.doc, but the
870 Linux system it is located at /usr/lib/python2.3/pdb.doc, but the
871 easiest way to read it is by using the help() function of the pdb module
871 easiest way to read it is by using the help() function of the pdb module
872 as follows (in an IPython prompt)::
872 as follows (in an IPython prompt)::
873
873
874 In [1]: import pdb
874 In [1]: import pdb
875 In [2]: pdb.help()
875 In [2]: pdb.help()
876
876
877 This will load the pdb.doc document in a file viewer for you automatically.
877 This will load the pdb.doc document in a file viewer for you automatically.
878
878
879
879
880 Automatic invocation of pdb on exceptions
880 Automatic invocation of pdb on exceptions
881 -----------------------------------------
881 -----------------------------------------
882
882
883 IPython, if started with the ``--pdb`` option (or if the option is set in
883 IPython, if started with the ``--pdb`` option (or if the option is set in
884 your config file) can call the Python pdb debugger every time your code
884 your config file) can call the Python pdb debugger every time your code
885 triggers an uncaught exception. This feature
885 triggers an uncaught exception. This feature
886 can also be toggled at any time with the %pdb magic command. This can be
886 can also be toggled at any time with the %pdb magic command. This can be
887 extremely useful in order to find the origin of subtle bugs, because pdb
887 extremely useful in order to find the origin of subtle bugs, because pdb
888 opens up at the point in your code which triggered the exception, and
888 opens up at the point in your code which triggered the exception, and
889 while your program is at this point 'dead', all the data is still
889 while your program is at this point 'dead', all the data is still
890 available and you can walk up and down the stack frame and understand
890 available and you can walk up and down the stack frame and understand
891 the origin of the problem.
891 the origin of the problem.
892
892
893 Furthermore, you can use these debugging facilities both with the
893 Furthermore, you can use these debugging facilities both with the
894 embedded IPython mode and without IPython at all. For an embedded shell
894 embedded IPython mode and without IPython at all. For an embedded shell
895 (see sec. Embedding_), simply call the constructor with
895 (see sec. Embedding_), simply call the constructor with
896 ``--pdb`` in the argument string and pdb will automatically be called if an
896 ``--pdb`` in the argument string and pdb will automatically be called if an
897 uncaught exception is triggered by your code.
897 uncaught exception is triggered by your code.
898
898
899 For stand-alone use of the feature in your programs which do not use
899 For stand-alone use of the feature in your programs which do not use
900 IPython at all, put the following lines toward the top of your 'main'
900 IPython at all, put the following lines toward the top of your 'main'
901 routine::
901 routine::
902
902
903 import sys
903 import sys
904 from IPython.core import ultratb
904 from IPython.core import ultratb
905 sys.excepthook = ultratb.FormattedTB(mode='Verbose',
905 sys.excepthook = ultratb.FormattedTB(mode='Verbose',
906 color_scheme='Linux', call_pdb=1)
906 color_scheme='Linux', call_pdb=1)
907
907
908 The mode keyword can be either 'Verbose' or 'Plain', giving either very
908 The mode keyword can be either 'Verbose' or 'Plain', giving either very
909 detailed or normal tracebacks respectively. The color_scheme keyword can
909 detailed or normal tracebacks respectively. The color_scheme keyword can
910 be one of 'NoColor', 'Linux' (default) or 'LightBG'. These are the same
910 be one of 'NoColor', 'Linux' (default) or 'LightBG'. These are the same
911 options which can be set in IPython with ``--colors`` and ``--xmode``.
911 options which can be set in IPython with ``--colors`` and ``--xmode``.
912
912
913 This will give any of your programs detailed, colored tracebacks with
913 This will give any of your programs detailed, colored tracebacks with
914 automatic invocation of pdb.
914 automatic invocation of pdb.
915
915
916
916
917 Extensions for syntax processing
917 Extensions for syntax processing
918 ================================
918 ================================
919
919
920 This isn't for the faint of heart, because the potential for breaking
920 This isn't for the faint of heart, because the potential for breaking
921 things is quite high. But it can be a very powerful and useful feature.
921 things is quite high. But it can be a very powerful and useful feature.
922 In a nutshell, you can redefine the way IPython processes the user input
922 In a nutshell, you can redefine the way IPython processes the user input
923 line to accept new, special extensions to the syntax without needing to
923 line to accept new, special extensions to the syntax without needing to
924 change any of IPython's own code.
924 change any of IPython's own code.
925
925
926 In the IPython/extensions directory you will find some examples
926 In the IPython/extensions directory you will find some examples
927 supplied, which we will briefly describe now. These can be used 'as is'
927 supplied, which we will briefly describe now. These can be used 'as is'
928 (and both provide very useful functionality), or you can use them as a
928 (and both provide very useful functionality), or you can use them as a
929 starting point for writing your own extensions.
929 starting point for writing your own extensions.
930
930
931 .. _pasting_with_prompts:
931 .. _pasting_with_prompts:
932
932
933 Pasting of code starting with Python or IPython prompts
933 Pasting of code starting with Python or IPython prompts
934 -------------------------------------------------------
934 -------------------------------------------------------
935
935
936 IPython is smart enough to filter out input prompts, be they plain Python ones
936 IPython is smart enough to filter out input prompts, be they plain Python ones
937 (``>>>`` and ``...``) or IPython ones (``In [N]:`` and `` ...:``). You can
937 (``>>>`` and ``...``) or IPython ones (``In [N]:`` and `` ...:``). You can
938 therefore copy and paste from existing interactive sessions without worry.
938 therefore copy and paste from existing interactive sessions without worry.
939
939
940 The following is a 'screenshot' of how things work, copying an example from the
940 The following is a 'screenshot' of how things work, copying an example from the
941 standard Python tutorial::
941 standard Python tutorial::
942
942
943 In [1]: >>> # Fibonacci series:
943 In [1]: >>> # Fibonacci series:
944
944
945 In [2]: ... # the sum of two elements defines the next
945 In [2]: ... # the sum of two elements defines the next
946
946
947 In [3]: ... a, b = 0, 1
947 In [3]: ... a, b = 0, 1
948
948
949 In [4]: >>> while b < 10:
949 In [4]: >>> while b < 10:
950 ...: ... print b
950 ...: ... print b
951 ...: ... a, b = b, a+b
951 ...: ... a, b = b, a+b
952 ...:
952 ...:
953 1
953 1
954 1
954 1
955 2
955 2
956 3
956 3
957 5
957 5
958 8
958 8
959
959
960 And pasting from IPython sessions works equally well::
960 And pasting from IPython sessions works equally well::
961
961
962 In [1]: In [5]: def f(x):
962 In [1]: In [5]: def f(x):
963 ...: ...: "A simple function"
963 ...: ...: "A simple function"
964 ...: ...: return x**2
964 ...: ...: return x**2
965 ...: ...:
965 ...: ...:
966
966
967 In [2]: f(3)
967 In [2]: f(3)
968 Out[2]: 9
968 Out[2]: 9
969
969
970 .. _gui_support:
970 .. _gui_support:
971
971
972 GUI event loop support
972 GUI event loop support
973 ======================
973 ======================
974
974
975 .. versionadded:: 0.11
975 .. versionadded:: 0.11
976 The ``%gui`` magic and :mod:`IPython.lib.inputhook`.
976 The ``%gui`` magic and :mod:`IPython.lib.inputhook`.
977
977
978 IPython has excellent support for working interactively with Graphical User
978 IPython has excellent support for working interactively with Graphical User
979 Interface (GUI) toolkits, such as wxPython, PyQt4/PySide, PyGTK and Tk. This is
979 Interface (GUI) toolkits, such as wxPython, PyQt4/PySide, PyGTK and Tk. This is
980 implemented using Python's builtin ``PyOSInputHook`` hook. This implementation
980 implemented using Python's builtin ``PyOSInputHook`` hook. This implementation
981 is extremely robust compared to our previous thread-based version. The
981 is extremely robust compared to our previous thread-based version. The
982 advantages of this are:
982 advantages of this are:
983
983
984 * GUIs can be enabled and disabled dynamically at runtime.
984 * GUIs can be enabled and disabled dynamically at runtime.
985 * The active GUI can be switched dynamically at runtime.
985 * The active GUI can be switched dynamically at runtime.
986 * In some cases, multiple GUIs can run simultaneously with no problems.
986 * In some cases, multiple GUIs can run simultaneously with no problems.
987 * There is a developer API in :mod:`IPython.lib.inputhook` for customizing
987 * There is a developer API in :mod:`IPython.lib.inputhook` for customizing
988 all of these things.
988 all of these things.
989
989
990 For users, enabling GUI event loop integration is simple. You simple use the
990 For users, enabling GUI event loop integration is simple. You simple use the
991 ``%gui`` magic as follows::
991 ``%gui`` magic as follows::
992
992
993 %gui [GUINAME]
993 %gui [GUINAME]
994
994
995 With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME``
995 With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME``
996 arguments are ``wx``, ``qt``, ``gtk`` and ``tk``.
996 arguments are ``wx``, ``qt``, ``gtk`` and ``tk``.
997
997
998 Thus, to use wxPython interactively and create a running :class:`wx.App`
998 Thus, to use wxPython interactively and create a running :class:`wx.App`
999 object, do::
999 object, do::
1000
1000
1001 %gui wx
1001 %gui wx
1002
1002
1003 For information on IPython's Matplotlib integration (and the ``pylab`` mode)
1003 For information on IPython's Matplotlib integration (and the ``pylab`` mode)
1004 see :ref:`this section <matplotlib_support>`.
1004 see :ref:`this section <matplotlib_support>`.
1005
1005
1006 For developers that want to use IPython's GUI event loop integration in the
1006 For developers that want to use IPython's GUI event loop integration in the
1007 form of a library, these capabilities are exposed in library form in the
1007 form of a library, these capabilities are exposed in library form in the
1008 :mod:`IPython.lib.inputhook` and :mod:`IPython.lib.guisupport` modules.
1008 :mod:`IPython.lib.inputhook` and :mod:`IPython.lib.guisupport` modules.
1009 Interested developers should see the module docstrings for more information,
1009 Interested developers should see the module docstrings for more information,
1010 but there are a few points that should be mentioned here.
1010 but there are a few points that should be mentioned here.
1011
1011
1012 First, the ``PyOSInputHook`` approach only works in command line settings
1012 First, the ``PyOSInputHook`` approach only works in command line settings
1013 where readline is activated. The integration with various eventloops
1013 where readline is activated. The integration with various eventloops
1014 is handled somewhat differently (and more simply) when using the standalone
1014 is handled somewhat differently (and more simply) when using the standalone
1015 kernel, as in the qtconsole and notebook.
1015 kernel, as in the qtconsole and notebook.
1016
1016
1017 Second, when using the ``PyOSInputHook`` approach, a GUI application should
1017 Second, when using the ``PyOSInputHook`` approach, a GUI application should
1018 *not* start its event loop. Instead all of this is handled by the
1018 *not* start its event loop. Instead all of this is handled by the
1019 ``PyOSInputHook``. This means that applications that are meant to be used both
1019 ``PyOSInputHook``. This means that applications that are meant to be used both
1020 in IPython and as standalone apps need to have special code to detects how the
1020 in IPython and as standalone apps need to have special code to detects how the
1021 application is being run. We highly recommend using IPython's support for this.
1021 application is being run. We highly recommend using IPython's support for this.
1022 Since the details vary slightly between toolkits, we point you to the various
1022 Since the details vary slightly between toolkits, we point you to the various
1023 examples in our source directory :file:`docs/examples/lib` that demonstrate
1023 examples in our source directory :file:`docs/examples/lib` that demonstrate
1024 these capabilities.
1024 these capabilities.
1025
1025
1026 Third, unlike previous versions of IPython, we no longer "hijack" (replace
1026 Third, unlike previous versions of IPython, we no longer "hijack" (replace
1027 them with no-ops) the event loops. This is done to allow applications that
1027 them with no-ops) the event loops. This is done to allow applications that
1028 actually need to run the real event loops to do so. This is often needed to
1028 actually need to run the real event loops to do so. This is often needed to
1029 process pending events at critical points.
1029 process pending events at critical points.
1030
1030
1031 Finally, we also have a number of examples in our source directory
1031 Finally, we also have a number of examples in our source directory
1032 :file:`docs/examples/lib` that demonstrate these capabilities.
1032 :file:`docs/examples/lib` that demonstrate these capabilities.
1033
1033
1034 PyQt and PySide
1034 PyQt and PySide
1035 ---------------
1035 ---------------
1036
1036
1037 .. attempt at explanation of the complete mess that is Qt support
1037 .. attempt at explanation of the complete mess that is Qt support
1038
1038
1039 When you use ``--gui=qt`` or ``--pylab=qt``, IPython can work with either
1039 When you use ``--gui=qt`` or ``--pylab=qt``, IPython can work with either
1040 PyQt4 or PySide. There are three options for configuration here, because
1040 PyQt4 or PySide. There are three options for configuration here, because
1041 PyQt4 has two APIs for QString and QVariant - v1, which is the default on
1041 PyQt4 has two APIs for QString and QVariant - v1, which is the default on
1042 Python 2, and the more natural v2, which is the only API supported by PySide.
1042 Python 2, and the more natural v2, which is the only API supported by PySide.
1043 v2 is also the default for PyQt4 on Python 3. IPython's code for the QtConsole
1043 v2 is also the default for PyQt4 on Python 3. IPython's code for the QtConsole
1044 uses v2, but you can still use any interface in your code, since the
1044 uses v2, but you can still use any interface in your code, since the
1045 Qt frontend is in a different process.
1045 Qt frontend is in a different process.
1046
1046
1047 The default will be to import PyQt4 without configuration of the APIs, thus
1047 The default will be to import PyQt4 without configuration of the APIs, thus
1048 matching what most applications would expect. It will fall back of PySide if
1048 matching what most applications would expect. It will fall back of PySide if
1049 PyQt4 is unavailable.
1049 PyQt4 is unavailable.
1050
1050
1051 If specified, IPython will respect the environment variable ``QT_API`` used
1051 If specified, IPython will respect the environment variable ``QT_API`` used
1052 by ETS. ETS 4.0 also works with both PyQt4 and PySide, but it requires
1052 by ETS. ETS 4.0 also works with both PyQt4 and PySide, but it requires
1053 PyQt4 to use its v2 API. So if ``QT_API=pyside`` PySide will be used,
1053 PyQt4 to use its v2 API. So if ``QT_API=pyside`` PySide will be used,
1054 and if ``QT_API=pyqt`` then PyQt4 will be used *with the v2 API* for
1054 and if ``QT_API=pyqt`` then PyQt4 will be used *with the v2 API* for
1055 QString and QVariant, so ETS codes like MayaVi will also work with IPython.
1055 QString and QVariant, so ETS codes like MayaVi will also work with IPython.
1056
1056
1057 If you launch IPython in pylab mode with ``ipython --pylab=qt``, then IPython
1057 If you launch IPython in pylab mode with ``ipython --pylab=qt``, then IPython
1058 will ask matplotlib which Qt library to use (only if QT_API is *not set*), via
1058 will ask matplotlib which Qt library to use (only if QT_API is *not set*), via
1059 the 'backend.qt4' rcParam. If matplotlib is version 1.0.1 or older, then
1059 the 'backend.qt4' rcParam. If matplotlib is version 1.0.1 or older, then
1060 IPython will always use PyQt4 without setting the v2 APIs, since neither v2
1060 IPython will always use PyQt4 without setting the v2 APIs, since neither v2
1061 PyQt nor PySide work.
1061 PyQt nor PySide work.
1062
1062
1063 .. warning::
1063 .. warning::
1064
1064
1065 Note that this means for ETS 4 to work with PyQt4, ``QT_API`` *must* be set
1065 Note that this means for ETS 4 to work with PyQt4, ``QT_API`` *must* be set
1066 to work with IPython's qt integration, because otherwise PyQt4 will be
1066 to work with IPython's qt integration, because otherwise PyQt4 will be
1067 loaded in an incompatible mode.
1067 loaded in an incompatible mode.
1068
1068
1069 It also means that you must *not* have ``QT_API`` set if you want to
1069 It also means that you must *not* have ``QT_API`` set if you want to
1070 use ``--gui=qt`` with code that requires PyQt4 API v1.
1070 use ``--gui=qt`` with code that requires PyQt4 API v1.
1071
1071
1072
1072
1073 .. _matplotlib_support:
1073 .. _matplotlib_support:
1074
1074
1075 Plotting with matplotlib
1075 Plotting with matplotlib
1076 ========================
1076 ========================
1077
1077
1078 `Matplotlib`_ provides high quality 2D and 3D plotting for Python. Matplotlib
1078 `Matplotlib`_ provides high quality 2D and 3D plotting for Python. Matplotlib
1079 can produce plots on screen using a variety of GUI toolkits, including Tk,
1079 can produce plots on screen using a variety of GUI toolkits, including Tk,
1080 PyGTK, PyQt4 and wxPython. It also provides a number of commands useful for
1080 PyGTK, PyQt4 and wxPython. It also provides a number of commands useful for
1081 scientific computing, all with a syntax compatible with that of the popular
1081 scientific computing, all with a syntax compatible with that of the popular
1082 Matlab program.
1082 Matlab program.
1083
1083
1084 To start IPython with matplotlib support, use the ``--pylab`` switch. If no
1084 To start IPython with matplotlib support, use the ``--pylab`` switch. If no
1085 arguments are given, IPython will automatically detect your choice of
1085 arguments are given, IPython will automatically detect your choice of
1086 matplotlib backend. You can also request a specific backend with ``--pylab
1086 matplotlib backend. You can also request a specific backend with ``--pylab
1087 backend``, where ``backend`` must be one of: 'tk', 'qt', 'wx', 'gtk', 'osx'.
1087 backend``, where ``backend`` must be one of: 'tk', 'qt', 'wx', 'gtk', 'osx'.
1088 In the web notebook and Qt console, 'inline' is also a valid backend value,
1088 In the web notebook and Qt console, 'inline' is also a valid backend value,
1089 which produces static figures inlined inside the application window instead of
1089 which produces static figures inlined inside the application window instead of
1090 matplotlib's interactive figures that live in separate windows.
1090 matplotlib's interactive figures that live in separate windows.
1091
1091
1092 .. _Matplotlib: http://matplotlib.sourceforge.net
1092 .. _Matplotlib: http://matplotlib.sourceforge.net
1093
1093
1094 .. _interactive_demos:
1094 .. _interactive_demos:
1095
1095
1096 Interactive demos with IPython
1096 Interactive demos with IPython
1097 ==============================
1097 ==============================
1098
1098
1099 IPython ships with a basic system for running scripts interactively in
1099 IPython ships with a basic system for running scripts interactively in
1100 sections, useful when presenting code to audiences. A few tags embedded
1100 sections, useful when presenting code to audiences. A few tags embedded
1101 in comments (so that the script remains valid Python code) divide a file
1101 in comments (so that the script remains valid Python code) divide a file
1102 into separate blocks, and the demo can be run one block at a time, with
1102 into separate blocks, and the demo can be run one block at a time, with
1103 IPython printing (with syntax highlighting) the block before executing
1103 IPython printing (with syntax highlighting) the block before executing
1104 it, and returning to the interactive prompt after each block. The
1104 it, and returning to the interactive prompt after each block. The
1105 interactive namespace is updated after each block is run with the
1105 interactive namespace is updated after each block is run with the
1106 contents of the demo's namespace.
1106 contents of the demo's namespace.
1107
1107
1108 This allows you to show a piece of code, run it and then execute
1108 This allows you to show a piece of code, run it and then execute
1109 interactively commands based on the variables just created. Once you
1109 interactively commands based on the variables just created. Once you
1110 want to continue, you simply execute the next block of the demo. The
1110 want to continue, you simply execute the next block of the demo. The
1111 following listing shows the markup necessary for dividing a script into
1111 following listing shows the markup necessary for dividing a script into
1112 sections for execution as a demo:
1112 sections for execution as a demo:
1113
1113
1114 .. literalinclude:: ../../examples/lib/example-demo.py
1114 .. literalinclude:: ../../examples/lib/example-demo.py
1115 :language: python
1115 :language: python
1116
1116
1117 In order to run a file as a demo, you must first make a Demo object out
1117 In order to run a file as a demo, you must first make a Demo object out
1118 of it. If the file is named myscript.py, the following code will make a
1118 of it. If the file is named myscript.py, the following code will make a
1119 demo::
1119 demo::
1120
1120
1121 from IPython.lib.demo import Demo
1121 from IPython.lib.demo import Demo
1122
1122
1123 mydemo = Demo('myscript.py')
1123 mydemo = Demo('myscript.py')
1124
1124
1125 This creates the mydemo object, whose blocks you run one at a time by
1125 This creates the mydemo object, whose blocks you run one at a time by
1126 simply calling the object with no arguments. If you have autocall active
1126 simply calling the object with no arguments. If you have autocall active
1127 in IPython (the default), all you need to do is type::
1127 in IPython (the default), all you need to do is type::
1128
1128
1129 mydemo
1129 mydemo
1130
1130
1131 and IPython will call it, executing each block. Demo objects can be
1131 and IPython will call it, executing each block. Demo objects can be
1132 restarted, you can move forward or back skipping blocks, re-execute the
1132 restarted, you can move forward or back skipping blocks, re-execute the
1133 last block, etc. Simply use the Tab key on a demo object to see its
1133 last block, etc. Simply use the Tab key on a demo object to see its
1134 methods, and call '?' on them to see their docstrings for more usage
1134 methods, and call '?' on them to see their docstrings for more usage
1135 details. In addition, the demo module itself contains a comprehensive
1135 details. In addition, the demo module itself contains a comprehensive
1136 docstring, which you can access via::
1136 docstring, which you can access via::
1137
1137
1138 from IPython.lib import demo
1138 from IPython.lib import demo
1139
1139
1140 demo?
1140 demo?
1141
1141
1142 Limitations: It is important to note that these demos are limited to
1142 Limitations: It is important to note that these demos are limited to
1143 fairly simple uses. In particular, you cannot break up sections within
1143 fairly simple uses. In particular, you cannot break up sections within
1144 indented code (loops, if statements, function definitions, etc.)
1144 indented code (loops, if statements, function definitions, etc.)
1145 Supporting something like this would basically require tracking the
1145 Supporting something like this would basically require tracking the
1146 internal execution state of the Python interpreter, so only top-level
1146 internal execution state of the Python interpreter, so only top-level
1147 divisions are allowed. If you want to be able to open an IPython
1147 divisions are allowed. If you want to be able to open an IPython
1148 instance at an arbitrary point in a program, you can use IPython's
1148 instance at an arbitrary point in a program, you can use IPython's
1149 embedding facilities, see :func:`IPython.embed` for details.
1149 embedding facilities, see :func:`IPython.embed` for details.
1150
1150
@@ -1,64 +1,70 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """IPython release script.
2 """IPython release script.
3
3
4 This should ONLY be run at real release time.
4 This should ONLY be run at real release time.
5 """
5 """
6 from __future__ import print_function
6 from __future__ import print_function
7
7
8 from toollib import *
8 from toollib import *
9 from gh_api import post_download
9
10
10 # Get main ipython dir, this will raise if it doesn't pass some checks
11 # Get main ipython dir, this will raise if it doesn't pass some checks
11 ipdir = get_ipdir()
12 ipdir = get_ipdir()
12 tooldir = pjoin(ipdir, 'tools')
13 tooldir = pjoin(ipdir, 'tools')
13 distdir = pjoin(ipdir, 'dist')
14 distdir = pjoin(ipdir, 'dist')
14
15
15 # Where I keep static backups of each release
16 # Where I keep static backups of each release
16 ipbackupdir = os.path.expanduser('~/ipython/backup')
17 ipbackupdir = os.path.expanduser('~/ipython/backup')
17
18
18 # Start in main IPython dir
19 # Start in main IPython dir
19 cd(ipdir)
20 cd(ipdir)
20
21
21 # Load release info
22 # Load release info
22 execfile(pjoin('IPython','core','release.py'))
23 execfile(pjoin('IPython','core','release.py'))
23
24
24 # Build site addresses for file uploads
25 # Build site addresses for file uploads
25 release_site = '%s/release/%s' % (archive, version)
26 release_site = '%s/release/%s' % (archive, version)
26 backup_site = '%s/backup/' % archive
27 backup_site = '%s/backup/' % archive
27
28
28 # Start actual release process
29 # Start actual release process
29 print()
30 print()
30 print('Releasing IPython')
31 print('Releasing IPython')
31 print('=================')
32 print('=================')
32 print()
33 print()
33 print('Version:', version)
34 print('Version:', version)
34 print()
35 print()
35 print('Source IPython directory:', ipdir)
36 print('Source IPython directory:', ipdir)
36 print()
37 print()
37
38
38 # Perform local backup, go to tools dir to run it.
39 # Perform local backup, go to tools dir to run it.
39 cd(tooldir)
40 cd(tooldir)
40 sh('./make_tarball.py')
41 sh('./make_tarball.py')
41 sh('mv ipython-*.tgz %s' % ipbackupdir)
42 sh('mv ipython-*.tgz %s' % ipbackupdir)
42
43
43 # Build release files
44 # Build release files
44 sh('./build_release %s' % ipdir)
45 sh('./build_release %s' % ipdir)
45
46
46 # Register with the Python Package Index (PyPI)
47 # Register with the Python Package Index (PyPI)
47 print( 'Registering with PyPI...')
48 print( 'Registering with PyPI...')
48 cd(ipdir)
49 cd(ipdir)
49 sh('./setup.py register')
50 sh('./setup.py register')
50
51
51 # Upload all files
52 # Upload all files
52 sh(sdists + ' upload')
53 sh(sdists + ' upload')
53 cd(distdir)
54 cd(distdir)
54 print( 'Uploading distribution files...')
55 print( 'Uploading distribution files...')
55
56
57 for fname in os.listdir('.'):
58 print('uploading %s to GitHub' % fname)
59 desc = "IPython %s source distribution" % version
60 post_download("ipython/ipython", fname, description=desc)
61
56 # Make target dir if it doesn't exist
62 # Make target dir if it doesn't exist
57 sh('ssh %s "mkdir -p %s/release/%s" ' % (archive_user, archive_dir, version))
63 sh('ssh %s "mkdir -p %s/release/%s" ' % (archive_user, archive_dir, version))
58 sh('scp * %s' % release_site)
64 sh('scp * %s' % release_site)
59
65
60 print( 'Uploading backup files...')
66 print( 'Uploading backup files...')
61 cd(ipbackupdir)
67 cd(ipbackupdir)
62 sh('scp `ls -1tr *tgz | tail -1` %s' % backup_site)
68 sh('scp `ls -1tr *tgz | tail -1` %s' % backup_site)
63
69
64 print('Done!')
70 print('Done!')
@@ -1,52 +1,66 b''
1 """
1 """
2 build [and upload] Windows IPython releases
2 build [and upload] Windows IPython releases
3
3
4 usage:
4 usage:
5
5
6 python tools/release_windows.py [--github] [--pypi]
6 python tools/release_windows.py [--github] [--pypi]
7
7
8 Meant to be run on Windows
8 Meant to be run on Windows
9
9
10 Requires that you have python and python3 on your PATH
10 Requires that you have python and python3 on your PATH
11 """
11 """
12
12
13 import glob
13 import glob
14 import os
14 import os
15 import shutil
15 import shutil
16 import sys
16 import sys
17
17
18 from toollib import sh
18 from toollib import sh
19 try:
19 try:
20 import gh_api
20 import gh_api
21 except ImportError:
21 except ImportError:
22 gh_api = None
22 gh_api = None
23
23
24 github = '--github' in sys.argv
24 github = '--github' in sys.argv
25
25
26 cmd_t = "{py} setup.py bdist_wininst --plat-name={plat}"
26 cmd_t = "{py} setup.py bdist_wininst"
27
27
28 pypi = '--pypi' in sys.argv
28 pypi = '--pypi' in sys.argv
29 pypi_cmd_t = "python setup.py upload_wininst -f {fname}"
29 pypi_cmd_t = "python setup.py upload_wininst -f {fname}"
30
30
31 for py in ['python', 'python3']:
31 # Windows Python cannot normally cross-compile,
32 # so you must have 4 Pythons to make 4 installers:
33 # http://docs.python.org/2/distutils/builtdist.html#cross-compiling-on-windows
34
35 pythons = {
36 2: {
37 'win32' : r'C:\\Python27\Python.exe',
38 'win-amd64': r'C:\\Python27_64\Python.exe',
39 },
40 3: {
41 'win32' : r'C:\\Python33\Python.exe',
42 'win-amd64': r'C:\\Python33_64\Python.exe',
43 },
44 }
45
46 for v,plat_py in pythons.items():
32 # deliberately mangle the name,
47 # deliberately mangle the name,
33 # so easy_install doesn't find these and do horrible wrong things
48 # so easy_install doesn't find these and do horrible wrong things
34 v = 3 if py.endswith('3') else 2
35 try:
49 try:
36 shutil.rmtree('build')
50 shutil.rmtree('build')
37 except OSError:
51 except OSError:
38 pass
52 pass
39 for plat in ['win32', 'win-amd64']:
53 for plat,py in plat_py.items():
40 cmd = cmd_t.format(**locals())
54 cmd = cmd_t.format(**locals())
41 sh(cmd)
55 sh(cmd)
42 orig = glob.glob(os.path.join('dist', 'ipython-*.{plat}.exe'.format(**locals())))[0]
56 orig = glob.glob(os.path.join('dist', 'ipython-*.{plat}.exe'.format(**locals())))[0]
43 mangled = orig.replace('.{plat}.exe'.format(**locals()),
57 mangled = orig.replace('.{plat}.exe'.format(**locals()),
44 '.py{v}-{plat}.exe'.format(**locals())
58 '.py{v}-{plat}.exe'.format(**locals())
45 )
59 )
46 os.rename(orig, mangled)
60 os.rename(orig, mangled)
47 if pypi:
61 if pypi:
48 sh(pypi_cmd_t.format(fname=mangled))
62 sh(pypi_cmd_t.format(fname=mangled))
49 if github and gh_api:
63 if github and gh_api:
50 print ("Uploading %s to GitHub" % mangled)
64 print ("Uploading %s to GitHub" % mangled)
51 desc = "IPython Installer for Python {v}.x on {plat}".format(**locals())
65 desc = "IPython Installer for Python {v}.x on {plat}".format(**locals())
52 gh_api.post_download('ipython/ipython', mangled, description=desc)
66 gh_api.post_download('ipython/ipython', mangled, description=desc)
General Comments 0
You need to be logged in to leave comments. Login now